Skip to content
Merged
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
Binary file modified sdk/typescript/_bundled_plugin/mcp/server.mjs.br.part-000
Binary file not shown.
Binary file modified sdk/typescript/_bundled_plugin/mcp/server.mjs.br.part-001
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Workbench-owned Standard scans submit findings and coverage through `record_code
- The writer validates candidates against assigned source paths, merges rows with the same CWE ids, locations, and optional instance, preserves their text, and assigns deterministic `candidate_id` values.
- After normalization, compact validation adds exactly one `validation` object to every row with `disposition` (`reportable`, `suppressed`, `not_applicable`, or `deferred`), `method`, `confidence` (`high`, `medium`, or `low`), `confidence_rationale`, concise `rubric` and `evidence`, `counterevidence_or_proof_gap`, `remaining_uncertainty`, and optional `artifact_paths`. Add `source`, `control`, `sink`, or `preconditions` only when they clarify or differ from the discovery fields.
- Compact attack-path analysis adds exactly one `attack_path` object to each validation row marked `reportable` or `deferred`, with `decision` (`reportable`, `ignore`, or `deferred`), `dataflow`, `reachability`, `counterevidence`, `impact` and `likelihood` (`high`, `medium`, `low`, `ignore`, or `unknown`), `severity` (`critical`, `high`, `medium`, `low`, `ignore`, or `unknown`), `severity_rationale`, `change_conditions`, and `proof_gap` when deferred. A `reportable` decision requires severity `critical`, `high`, `medium`, or `low`; `ignore` requires severity `ignore`; `deferred` uses a provisional reportable severity or `unknown`.
- Record all validations through `record_codex_security_candidate_validations` and all eligible attack-path decisions through `record_codex_security_candidate_attack_paths`. The tools atomically preserve all discovery fields and candidate order.
- Record all validations through `record_codex_security_candidate_validations` and all eligible attack-path decisions through `record_candidate_attack_paths`. The tools atomically preserve all discovery fields and candidate order.
- Optional compact validation evidence: `<discovery_dir>/validation_artifacts/<candidate_id>/`
- Create this directory only for actual PoCs, crafted inputs, or logs and reference those paths from the row's `validation` object. Do not create placeholder per-candidate directories or narrative reports.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Use the shared scan artifact path conventions in `../../references/scan-artifact

### Compact Deep Candidate Mode

When `$deep-security-scan` invokes this skill, load the per-scan threat model and read the validated candidates with `list_codex_security_candidates({ scanId, cursor?, limit? })`. Analyze every `reportable` or `deferred` candidate in one invocation. Record one nested attack-path decision per eligible candidate with `record_codex_security_candidate_attack_paths({ scanId, attackPaths: [{ candidateId, attackPath }] })`, using the Deep candidate shape in `../../references/scan-artifacts.md` and preserving every discovery and validation field and the original candidate order. Standard scans assess attack paths in their parent and do not invoke this skill.
When `$deep-security-scan` invokes this skill, load the per-scan threat model and read the validated candidates with `list_codex_security_candidates({ scanId, cursor?, limit? })`. Analyze every `reportable` or `deferred` candidate in one invocation. Record one nested attack-path decision per eligible candidate with `record_candidate_attack_paths({ scanId, attackPaths: [{ candidateId, attackPath }] })`, using the Deep candidate shape in `../../references/scan-artifacts.md` and preserving every discovery and validation field and the original candidate order. Standard scans assess attack paths in their parent and do not invoke this skill.

In this mode, the tool atomically records the nested decision in place of a per-finding attack-path report or receipt. Submit all eligible attack-path decisions together; submit `attackPaths: []` when no candidate enters this phase. Keep attack-path facts, counterevidence, severity calibration, and policy adjustment as separate reasoning steps even though their output is compact. All reachability, instance-preservation, and evidence requirements still apply; only the artifact packaging changes.

Expand All @@ -38,7 +38,7 @@ In this mode, the tool atomically records the nested decision in place of a per-
5. Calibrate impact and likelihood from the repository evidence.
6. Apply a separate final policy-adjustment pass mechanically using those facts and the calibrated severity.
7. Record final policy decision `ignore` explicitly. Outside compact Deep candidate mode, drop it from the surviving finding set; in compact mode, retain the ledger row for coverage mapping.
8. In compact Deep candidate mode, call `record_codex_security_candidate_attack_paths` once with the nested attack-path decision for every eligible candidate; the tool atomically updates the stored candidates.
8. In compact Deep candidate mode, call `record_candidate_attack_paths` once with the nested attack-path decision for every eligible candidate; the tool atomically updates the stored candidates.
9. Outside compact Deep candidate mode, save that finding's visible attack-path report and append one attack-path receipt per candidate id at the default paths from `../../references/scan-artifacts.md`. The receipt must record the candidate id, attack-path reportability decision, attack-path facts or exact proof gap, and attack-path artifact/report reference for that candidate finding.

## Scope and Attack Path Checklist
Expand Down Expand Up @@ -82,7 +82,7 @@ Apply severity and policy calibration using `references/severity-policy.md`.

## Output Contract

In compact Deep candidate mode, submit the nested record defined in `../../references/scan-artifacts.md` using `record_codex_security_candidate_attack_paths`. Every candidate with validation disposition `reportable` or `deferred` must receive exactly one attack-path decision. The recorded result is the phase closure; do not also create a narrative report or receipt.
In compact Deep candidate mode, submit the nested record defined in `../../references/scan-artifacts.md` using `record_candidate_attack_paths`. Every candidate with validation disposition `reportable` or `deferred` must receive exactly one attack-path decision. The recorded result is the phase closure; do not also create a narrative report or receipt.

Outside compact Deep candidate mode, use the following report contract.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ After accepting the terminal manifest, continue in the same turn. A discovery ma
1. Read the canonical review items and candidate set with `list_codex_security_review_items({ scanId, handoffClaimToken?, cursor?, limit? })` and `list_codex_security_candidates({ scanId, cursor?, limit? })`. Follow `nextCursor` until all pages are read. If either tool fails or returns malformed records, report the tool failure and stop; do not repair coordinator-owned discovery artifacts, reopen discovery, or silently drop candidates.
2. Synthesize one canonical validation threat model from the ordered worker threat models and write it to the per-scan `<context_dir>/threat_model.md` path. Preserve relevant attacker models, trust boundaries, privileged surfaces, contradictions, and risk framings conservatively. This threat model is downstream context, not a retroactive discovery filter.
3. Run `$codex-security:validation` once in compact Deep candidate mode over the canonical merged candidates, recording every result with `record_codex_security_candidate_validations`.
4. Run `$codex-security:attack-path-analysis` once in compact Deep candidate mode over the reportable or deferred validated candidates, recording every decision with `record_codex_security_candidate_attack_paths`.
4. Run `$codex-security:attack-path-analysis` once in compact Deep candidate mode over the reportable or deferred validated candidates, recording every decision with `record_candidate_attack_paths`.
5. Assemble complete finding and coverage semantics using `../../references/final-report.md` and `../../references/finding-detail-fields.md`, then call `record_codex_security_scan_draft({ scanId, handoffClaimToken?, scope?, threatModel?, findings, coverage })`.
- Use the existing shared final-report contract: an evidence-supported lowercase vulnerability-family `ruleId`, the candidate's exact CWE array in `taxonomy.cwe`, its actual `provenance.source`, genuine nonempty code evidence, and coverage surfaces with canonical `label` and `disposition` fields. Preserve candidate and worker provenance.
- Set coverage to `partial` when deferred work or a `needs_follow_up` surface remains; retain the actual evidence and reason.
Expand Down
48 changes: 48 additions & 0 deletions sdk/typescript/tests-ts/attack-path-tool-name.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { spawnSync } from "node:child_process";
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, test } from "bun:test";
import { PLUGIN_ROOT } from "./plugin-root.js";

test("keeps model-visible attack-path tool names within the Codex limit", () => {
const node = Bun.which("node");
expect(node).not.toBeNull();
const state = mkdtempSync(join(tmpdir(), "codex-security-mcp-tools-"));

try {
const server = spawnSync(node!, [join(PLUGIN_ROOT, "mcp", "server.mjs")], {
encoding: "utf8",
env: { ...process.env, CODEX_SECURITY_STATE_DIR: state },
input: [
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"codex-security-test","version":"1.0.0"}}}',
'{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}',
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}',
"",
].join("\n"),
timeout: 30_000,
});
expect(server.status, server.stderr).toBe(0);

const tools = server.stdout
.trim()
.split("\n")
.map((line) => JSON.parse(line))
.find((response) => response.id === 2).result.tools as {
name: string;
_meta?: { ui?: { visibility?: string[] } };
}[];

expect(tools.map((tool) => tool.name)).toContain(
"record_candidate_attack_paths",
);
for (const tool of tools) {
if (tool._meta?.ui?.visibility?.includes("model") === false) continue;
expect(`mcp__codex_security__${tool.name}`.length).toBeLessThanOrEqual(
64,
);
}
} finally {
rmSync(state, { recursive: true, force: true });
}
});
2 changes: 1 addition & 1 deletion sdk/typescript/tests-ts/runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ describe("plugin runtime preparation", () => {
"start_codex_security_prompt_only_scan",
"start_codex_security_deep_scan",
"record_codex_security_scan_draft",
"record_codex_security_candidate_attack_paths",
"record_candidate_attack_paths",
"complete_codex_security_scan",
]) {
expect(names.has(name)).toBe(true);
Expand Down
Loading