From 4ea06a66c80dd857a35db22770fc02f8042d779a Mon Sep 17 00:00:00 2001 From: kevin9327 <5299031+kevin9327@users.noreply.github.com> Date: Thu, 10 Sep 2026 19:40:47 +0900 Subject: [PATCH 1/2] fix(scan): reject unsafe finding location paths in imported JSON `scan import --csv` refuses a row whose `path` escapes the repository, is absolute, or carries a drive letter, backslash or control character. The `--json` branch of the same command validated only against the findings JSON schema, which constrains `locations[].path` to a non-empty string, so the same path was accepted. `--dry-run` therefore reported such a document as importable, and the real import failed later in finalization with a message about findings not being preserved, after the source had already been persisted and a scan registered. Apply the existing `safeFindingPath` check to imported JSON finding locations, reporting the finding index the way the duplicate `occurrenceId` check beside it already does. --- sdk/typescript/src/findings-import.ts | 5 +++++ sdk/typescript/tests-ts/findings-import.test.ts | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/sdk/typescript/src/findings-import.ts b/sdk/typescript/src/findings-import.ts index 6ca87b12b0..d316560d0e 100644 --- a/sdk/typescript/src/findings-import.ts +++ b/sdk/typescript/src/findings-import.ts @@ -164,6 +164,11 @@ export async function parseImportedFindings( ); } occurrenceIds.add(finding.occurrenceId); + if (finding.locations.some((location) => !safeFindingPath(location.path))) { + throw new CodexSecurityError( + `Findings JSON finding ${index + 1} has an invalid path.`, + ); + } } return document.findings; } diff --git a/sdk/typescript/tests-ts/findings-import.test.ts b/sdk/typescript/tests-ts/findings-import.test.ts index 6c4ba695f9..9a66780f05 100644 --- a/sdk/typescript/tests-ts/findings-import.test.ts +++ b/sdk/typescript/tests-ts/findings-import.test.ts @@ -145,4 +145,19 @@ describe("findings import formats", () => { ), ).rejects.toThrow("duplicate occurrenceId"); }); + + test("rejects an unsafe finding location path in either import format", async () => { + const document = await sourceDocument(); + document.findings[0]!.locations[0]!.path = "../../../outside/secrets.env"; + await expect( + parseImportedFindings(JSON.stringify(document), "json", PLUGIN_ROOT), + ).rejects.toThrow("has an invalid path"); + await expect( + parseImportedFindings( + CSV_SOURCE.replace("src/extract.ts", "../../../outside/secrets.env"), + "csv", + PLUGIN_ROOT, + ), + ).rejects.toThrow("has an invalid path"); + }); }); From 4ab061b7bafcd9b87ac858c884c3e6357115590a Mon Sep 17 00:00:00 2001 From: Michael D'Angelo Date: Sat, 17 Oct 2026 04:44:16 +0000 Subject: [PATCH 2/2] test(scan): rely on shared import path validation --- sdk/typescript/src/findings-import.ts | 5 ----- sdk/typescript/tests-ts/findings-import.test.ts | 15 --------------- sdk/typescript/tests-ts/import-scan.test.ts | 13 +++++++++++++ 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/sdk/typescript/src/findings-import.ts b/sdk/typescript/src/findings-import.ts index 84318e1c55..91ae004082 100644 --- a/sdk/typescript/src/findings-import.ts +++ b/sdk/typescript/src/findings-import.ts @@ -160,11 +160,6 @@ export async function parseImportedFindings( ); } occurrenceIds.add(finding.occurrenceId); - if (finding.locations.some((location) => !safeFindingPath(location.path))) { - throw new CodexSecurityError( - `Findings JSON finding ${index + 1} has an invalid path.`, - ); - } } return document.findings; } diff --git a/sdk/typescript/tests-ts/findings-import.test.ts b/sdk/typescript/tests-ts/findings-import.test.ts index 81a2794396..541dc6fcbe 100644 --- a/sdk/typescript/tests-ts/findings-import.test.ts +++ b/sdk/typescript/tests-ts/findings-import.test.ts @@ -287,19 +287,4 @@ describe("findings import formats", () => { ), ).rejects.toThrow("duplicate occurrenceId"); }); - - test("rejects an unsafe finding location path in either import format", async () => { - const document = await sourceDocument(); - document.findings[0]!.locations[0]!.path = "../../../outside/secrets.env"; - await expect( - parseImportedFindings(JSON.stringify(document), "json", PLUGIN_ROOT), - ).rejects.toThrow("has an invalid path"); - await expect( - parseImportedFindings( - CSV_SOURCE.replace("src/extract.ts", "../../../outside/secrets.env"), - "csv", - PLUGIN_ROOT, - ), - ).rejects.toThrow("has an invalid path"); - }); }); diff --git a/sdk/typescript/tests-ts/import-scan.test.ts b/sdk/typescript/tests-ts/import-scan.test.ts index 5b8ccab53b..6fd503d778 100644 --- a/sdk/typescript/tests-ts/import-scan.test.ts +++ b/sdk/typescript/tests-ts/import-scan.test.ts @@ -564,6 +564,19 @@ for (const dryRun of [false, true]) { }, ], ["duplicate evidence ID", { codeEvidence: [evidence, evidence] }], + [ + "unsafe secondary location", + { + locations: [ + { path: "src/extract.ts", startLine: 41 }, + { path: "../outside.ts", startLine: 1 }, + ], + }, + ], + [ + "unsafe evidence path", + { codeEvidence: [{ ...evidence, path: "../outside.ts" }] }, + ], ] as const)( `rejects %s before import persistence (dryRun: ${dryRun})`, async (_name, details) => {