Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sdk/typescript/_bundled_plugin/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
96 changes: 96 additions & 0 deletions sdk/typescript/_bundled_plugin/examples/completed-scan/report.md
Original file line number Diff line number Diff line change
@@ -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. |

<a id="finding-1"></a>

### [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. |
1 change: 1 addition & 0 deletions sdk/typescript/plugin-files.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion sdk/typescript/src/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
25 changes: 25 additions & 0 deletions sdk/typescript/tests-ts/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Loading