diff --git a/sdk/typescript/_bundled_plugin/.codex-plugin/plugin.json b/sdk/typescript/_bundled_plugin/.codex-plugin/plugin.json index 04c30b9c..4015ea14 100644 --- a/sdk/typescript/_bundled_plugin/.codex-plugin/plugin.json +++ b/sdk/typescript/_bundled_plugin/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "codex-security", - "version": "0.1.20", + "version": "0.1.21", "description": "Codex Security workflows for security scans, analysis, and investigation.", "author": { "name": "OpenAI" diff --git a/sdk/typescript/_bundled_plugin/examples/completed-scan/report.md b/sdk/typescript/_bundled_plugin/examples/completed-scan/report.md new file mode 100644 index 00000000..58834b65 --- /dev/null +++ b/sdk/typescript/_bundled_plugin/examples/completed-scan/report.md @@ -0,0 +1,96 @@ +# Security Review: example/repo + +## Scope + +The scan reviewed the canonical include paths and exclusions listed below. + +- Scan mode: repository +- Target kind: git_worktree +- Target ID: target_sha256_example +- Revision: deadbeef +- Snapshot digest: codex-security-snapshot/v1:sha256:ed88f96a4c1a06603a41b3f261f59c3de2555c367ef6ad3bb8b9e483495d34eb +- Inventory strategy: repository +- Included paths: . +- Excluded paths: none +- Runtime or test status: not recorded + +### Scan Summary + +| Field | Value | +| --- | --- | +| Reportable findings | 1 | +| Severity mix | high: 1 | +| Confidence mix | high: 1 | +| Coverage | complete | +| Validation mode | not recorded | + +Canonical artifacts: `scan-manifest.json`, `findings.json`, and `coverage.json`. This report is a deterministic projection of those files. + +## Threat Model + +No explicit canonical threat-model summary was recorded. + +## Findings + +| Finding | Severity | Confidence | Detailed write-up | +| --- | --- | --- | --- | +| [Unsafe archive extraction can escape the output directory](#finding-1) | high | high | inline below | + +### Confidence Scale + +| Label | Meaning | +| --- | --- | +| high | Direct evidence supports the finding with no material unresolved blocker. | +| medium | Evidence supports a plausible issue, but material runtime or reachability proof remains. | +| low | Evidence is incomplete and the item is retained only for explicit follow-up. | + + + +### [1] Unsafe archive extraction can escape the output directory + +| Field | Value | +| --- | --- | +| Severity | high | +| Confidence | high | +| Confidence rationale | Direct source trace reaches the filesystem write without a containment check. | +| Category | path-traversal | +| CWE | CWE-22 | +| Affected lines | src/extract.py:41-44 | + +#### Summary + +An attacker-controlled path reaches a filesystem write without containment validation. + +#### Validation + +Direct source trace reaches the filesystem write without a containment check. Validation details were not recorded separately. + +#### Dataflow + +The canonical finding records the affected path at src/extract.py:41-44, but no expanded source-to-sink narrative was recorded. + +#### Reachability + +Reachability was not recorded beyond the canonical finding summary and affected locations. + +#### Severity + +**High** — The scan assigned high severity; no separate canonical severity rationale was recorded. + +Additional runtime or deployment evidence could raise or lower this severity. + +#### Remediation + +Normalize destinations and reject entries that escape the extraction root. + +Tests: +- Assert that extracting an archive entry named `../escape.txt` fails without writing outside the extraction root. + +Preventive controls: +- Route all archive extraction through one helper that normalizes and validates destination paths. + +## Reviewed Surfaces + +| Surface | Risk Area | Outcome | Notes | +| --- | --- | --- | --- | +| Archive extraction | not recorded | Reported | No additional canonical notes were recorded. | diff --git a/sdk/typescript/plugin-files.json b/sdk/typescript/plugin-files.json index 8f2c17e2..4130ad81 100644 --- a/sdk/typescript/plugin-files.json +++ b/sdk/typescript/plugin-files.json @@ -9,6 +9,7 @@ "assets/logo.png", "examples/completed-scan/coverage.json", "examples/completed-scan/findings.json", + "examples/completed-scan/report.md", "examples/completed-scan/scan-manifest.json", "mcp/server.mjs", "mcp/server.mjs.br.part-000", diff --git a/sdk/typescript/src/version.ts b/sdk/typescript/src/version.ts index 01dd5006..955feef3 100644 --- a/sdk/typescript/src/version.ts +++ b/sdk/typescript/src/version.ts @@ -8,7 +8,7 @@ const PACKAGE_VERSIONS = packageVersions( export const VERSION = PACKAGE_VERSIONS.package; export const CODEX_SDK_VERSION = PACKAGE_VERSIONS.sdk; export const CODEX_EXECUTABLE_VERSION = PACKAGE_VERSIONS.executable; -export const BUNDLED_PLUGIN_VERSION = "0.1.20" as const; +export const BUNDLED_PLUGIN_VERSION = "0.1.21" as const; const PACKAGE_NAME = "@openai/codex-security"; const VERSION_PATTERN = diff --git a/sdk/typescript/tests-ts/contract.test.ts b/sdk/typescript/tests-ts/contract.test.ts index 4fdfcd05..2898d609 100644 --- a/sdk/typescript/tests-ts/contract.test.ts +++ b/sdk/typescript/tests-ts/contract.test.ts @@ -105,6 +105,31 @@ function expectation( } describe("canonical scan contract", () => { + test("ships a completed example that passes tracking preflight", async () => { + const python = Bun.which("python3") ?? Bun.which("python"); + expect(python).not.toBeNull(); + const result = Bun.spawnSync( + [ + python!, + "-I", + "-B", + join(PLUGIN_ROOT, "scripts", "validate_tracking_source.py"), + EXAMPLE, + ], + { stdout: "pipe", stderr: "pipe" }, + ); + const findings = await readJson(join(EXAMPLE, "findings.json")); + + expect(result.exitCode, new TextDecoder().decode(result.stderr)).toBe(0); + expect( + new TextDecoder().decode(result.stdout).trim().split(/\r?\n/u), + ).toEqual( + findings["findings"].map( + (finding: { findingId: string }) => finding.findingId, + ), + ); + }); + test("compares exact Windows volume serials without rounding file identity", async () => { const scanDir = await copyExample(); const path = join(scanDir, "scan-manifest.json");