mktemp: Fix template validation to require trailing consecutive Xs (#10224)

* fix(mktemp): Validation of consecutive X's at template end

* fix(mktemp): Updated integration tests to expect failure for templates that do not end with a sufficient X run

* mktemp: document trailing-X template parsing

* mktemp: match GNU behavior for template X run detection
Change logic to only accept templates where the run of Xs containing the final 'X' is at least 3 characters long. This fixes cases like "XXX_XX", which should error with "too few X's in template", and allows "tempXXXlate", matching GNU mktemp

* mktemp: Simplify search using rposition
This commit is contained in:
✿ Fleur de Blue
2026-01-15 16:45:18 +01:00
committed by GitHub
parent 2c75e7187e
commit 5fd34598a0
2 changed files with 53 additions and 3 deletions
+16 -3
View File
@@ -193,9 +193,22 @@ struct Params {
/// assert_eq!(find_last_contiguous_block_of_xs("aXbXcX"), None);
/// ```
fn find_last_contiguous_block_of_xs(s: &str) -> Option<(usize, usize)> {
let j = s.rfind("XXX")? + 3;
let i = s[..j].rfind(|c| c != 'X').map_or(0, |i| i + 1);
Some((i, j))
let bytes = s.as_bytes();
// Find the index of the last 'X'.
let end = bytes.iter().rposition(|&b| b == b'X')?;
// Walk left to find the start of the run of Xs that ends at `end`.
let mut start = end;
while start > 0 && bytes[start - 1] == b'X' {
start -= 1;
}
if end + 1 - start >= 3 {
Some((start, end + 1))
} else {
None
}
}
impl Params {
+37
View File
@@ -30,6 +30,7 @@ static TEST_TEMPLATE7: &str = "XXXtemplate";
static TEST_TEMPLATE8: &str = "tempXXXl/ate";
#[cfg(windows)]
static TEST_TEMPLATE8: &str = "tempXXXl\\ate";
static TEST_TEMPLATE9: &str = "XXX_XX";
#[cfg(not(windows))]
const TMPDIR: &str = "TMPDIR";
@@ -109,6 +110,11 @@ fn test_mktemp_mktemp() {
.env(TMPDIR, &pathname)
.arg(TEST_TEMPLATE8)
.fails();
scene
.ucmd()
.env(TMPDIR, &pathname)
.arg(TEST_TEMPLATE9)
.fails();
}
#[test]
@@ -168,6 +174,12 @@ fn test_mktemp_mktemp_t() {
.no_stdout()
.stderr_contains("invalid suffix")
.stderr_contains("contains directory separator");
scene
.ucmd()
.env(TMPDIR, &pathname)
.arg("-t")
.arg(TEST_TEMPLATE9)
.fails();
}
#[test]
@@ -224,6 +236,12 @@ fn test_mktemp_make_temp_dir() {
.arg("-d")
.arg(TEST_TEMPLATE8)
.fails();
scene
.ucmd()
.env(TMPDIR, &pathname)
.arg("-d")
.arg(TEST_TEMPLATE9)
.fails();
}
#[test]
@@ -280,6 +298,12 @@ fn test_mktemp_dry_run() {
.arg("-u")
.arg(TEST_TEMPLATE8)
.fails();
scene
.ucmd()
.env(TMPDIR, &pathname)
.arg("-u")
.arg(TEST_TEMPLATE9)
.fails();
}
#[test]
@@ -367,6 +391,13 @@ fn test_mktemp_suffix() {
.arg("suf")
.arg(TEST_TEMPLATE8)
.fails();
scene
.ucmd()
.env(TMPDIR, &pathname)
.arg("--suffix")
.arg("suf")
.arg(TEST_TEMPLATE9)
.fails();
}
#[test]
@@ -424,6 +455,12 @@ fn test_mktemp_tmpdir() {
.arg(pathname)
.arg(TEST_TEMPLATE8)
.fails();
scene
.ucmd()
.arg("-p")
.arg(pathname)
.arg(TEST_TEMPLATE9)
.fails();
}
#[test]