Skip to content

Commit f00d3c1

Browse files
test(redact): guard the pathspec fail-open + non-ASCII ignore
(g) a non-ASCII filename is exempted by its glob (core.quotePath=false). (h) a `?`-bearing ignored filename does NOT drag a sibling holding a real secret out of the scan — the sibling credential still blocks (exit 1). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent d6a474d commit f00d3c1

1 file changed

Lines changed: 38 additions & 0 deletions

File tree

test/redact-prepush-hook.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,44 @@ describe("path-ignore for generated data files (#1946 follow-up)", () => {
305305
expect(stderr).toContain("engine.input_too_large");
306306
expect(stderr).not.toContain("skipped");
307307
});
308+
309+
test("(g) an ignore glob applies to a non-ASCII filename (core.quotePath=false)", () => {
310+
// Regression: git C-quotes non-ASCII paths by default (`"caf\303\251.csv"`),
311+
// which the glob can't match — the ignore rule would silently fail to apply
312+
// and the large export would still oversize-block. The hook forces
313+
// core.quotePath=false so the raw UTF-8 path matches.
314+
writeIgnoreFile("prospecting/exports/**/*.csv\n");
315+
const base = git(["rev-parse", "HEAD"]);
316+
fs.mkdirSync(path.join(repo, "prospecting", "exports"), { recursive: true });
317+
fs.writeFileSync(path.join(repo, "prospecting", "exports", "café.csv"), BIG);
318+
git(["add", "-A"]);
319+
git(["commit", "-q", "-m", "big export with non-ascii name"]);
320+
const head = git(["rev-parse", "HEAD"]);
321+
const { code, stderr } = runHook(`refs/heads/main ${head} refs/heads/main ${base}\n`);
322+
expect(code).toBe(0);
323+
expect(stderr).toContain("skipped 1 path(s)");
324+
expect(stderr).toContain("café.csv");
325+
});
326+
327+
test("(h) a glob metacharacter in an ignored filename does NOT exclude a sibling (fail-open guard)", () => {
328+
// Fail-open regression (adversarial review): a real filename holding a `?`
329+
// is a legal path. A plain `:(exclude)export?.csv` pathspec is glob-ENABLED,
330+
// so git would ALSO drop `exportX.csv` (a sibling holding a real secret) from
331+
// the scan. `:(top,exclude,literal)` treats the name literally, so only the
332+
// exact ignored file is exempted and the sibling secret still blocks.
333+
// The glob is `export\?.csv` (escaped) so Bun.Glob matches ONLY the literal
334+
// `export?.csv` — isolating the git pathspec overreach, not Bun.Glob's own.
335+
writeIgnoreFile("export\\?.csv\n");
336+
const base = git(["rev-parse", "HEAD"]);
337+
fs.writeFileSync(path.join(repo, "export?.csv"), "benign generated data\n");
338+
fs.writeFileSync(path.join(repo, "exportX.csv"), "key AKIA1234567890ABCDEF\n");
339+
git(["add", "-A"]);
340+
git(["commit", "-q", "-m", "ignored ? file + sibling with a secret"]);
341+
const head = git(["rev-parse", "HEAD"]);
342+
const { code, stderr } = runHook(`refs/heads/main ${head} refs/heads/main ${base}\n`);
343+
expect(code).toBe(1); // the sibling's credential must still block
344+
expect(stderr).toContain("aws.access_key");
345+
});
308346
});
309347

310348
describe("install / chaining", () => {

0 commit comments

Comments
 (0)