diff --git a/extensions/git-read/src/args.ts b/extensions/git-read/src/args.ts index aca960c4..92a4b053 100644 --- a/extensions/git-read/src/args.ts +++ b/extensions/git-read/src/args.ts @@ -31,7 +31,8 @@ export function isSafeRevision(revision: string): boolean { const REPO_PATH_PATTERN = /^[^:/\\?#*[\]'"\s][^:/\\?#*[\]'"\s]*$/; export function isSafeRepoPath(value: string): boolean { - if (value.length === 0 || value.length > 512) return false; + if (value.length === 0 || value.length > 512 || value.includes("\0")) + return false; const segments = value.split("/"); for (const segment of segments) { if (segment === "" || segment === "." || segment === "..") return false; diff --git a/tests/extensions/git-read/args.test.ts b/tests/extensions/git-read/args.test.ts index 58cbe710..eefae84a 100644 --- a/tests/extensions/git-read/args.test.ts +++ b/tests/extensions/git-read/args.test.ts @@ -149,3 +149,14 @@ test("buildLogArgs clamps the limit and validates inputs", () => { assert.throws(() => buildLogArgs({ revision: "-n5" }), InvalidRevisionError); assert.throws(() => buildLogArgs({ file: "a/../b" }), InvalidPathError); }); + +test("git argv builders reject NUL bytes in repository paths", () => { + const path = "src\0secret.ts"; + + assert.throws( + () => buildShowArgs({ revision: "HEAD", path }), + InvalidPathError, + ); + assert.throws(() => buildDiffArgs({ path }), InvalidPathError); + assert.throws(() => buildLogArgs({ file: path }), InvalidPathError); +});