Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
1233936
fix: retain worker coverage in Deep Scan reports
mldangelo-oai Oct 11, 2026
c430ff7
fix: preserve Deep Scan checkpoints at discovery deadlines
mldangelo-oai Oct 11, 2026
c36a8d4
fix: roll back failed deadline aggregate checkpoints
mldangelo-oai Oct 11, 2026
e034d40
fix: preserve checkpoint observation times on rollback
mldangelo-oai Oct 11, 2026
00f522b
fix: complete deadline recovery and rollback
mldangelo-oai Oct 11, 2026
126a876
fix: preserve rejected recovery entries during rollback
mldangelo-oai Oct 11, 2026
dbfa2db
fix: skip unencodable saved threat models
mldangelo-oai Oct 11, 2026
2076e79
Merge commit '825e7fd059a8047eef6fac6c814dce294d58fc18' into mdangelo…
mldangelo-oai Oct 11, 2026
bd579e4
Merge commit '825e7fd059a8047eef6fac6c814dce294d58fc18' into mdangelo…
mldangelo-oai Oct 11, 2026
7190724
fix: retain stopped worker coverage before parent publication
mldangelo-oai Oct 11, 2026
7428bf3
refactor(deep): keep only required accepted coverage state
mldangelo-oai Oct 11, 2026
819af22
Merge commit '825e7fd059a8047eef6fac6c814dce294d58fc18' into mdangelo…
mldangelo-oai Oct 11, 2026
188d574
Merge commit '7428bf3a002857ccb61215256acf96037a1213ff' into mdangelo…
mldangelo-oai Oct 11, 2026
e8b6324
fix(deep): preserve committed deadline recovery on interruption
mldangelo-oai Oct 11, 2026
7d92bca
fix: reserve receipts only for retained worker coverage
mldangelo-oai Oct 11, 2026
7b5a93f
fix(deep): reserve only retained coverage receipt paths
mldangelo-oai Oct 11, 2026
bd5086a
Merge commit '7b5a93f7bcfa5f264ef4dfed278fd3f53abec1c3' into mdangelo…
mldangelo-oai Oct 11, 2026
76a7db9
Merge updated source coverage into deadline checkpoint recovery
mldangelo-oai Oct 11, 2026
771c725
fix(deep): require available receipt preference sources
mldangelo-oai Oct 11, 2026
a12050c
Merge commit '771c725bc236b5435652792d07f3ceebc857987a' into mdangelo…
mldangelo-oai Oct 11, 2026
137bc15
fix(deep): snapshot only deadline publication outputs
mldangelo-oai Oct 11, 2026
ad26206
fix(deep-scan): validate worker coverage before checkpointing
mldangelo-oai Oct 11, 2026
bbbd5f1
Merge commit 'ad26206a533521bf421dc7442353e2d1abc77aec' into mdangelo…
mldangelo-oai Oct 11, 2026
070fa18
fix(deep-scan): reserve receipts from retained coverage only
mldangelo-oai Oct 11, 2026
eaff113
Merge commit '7d92bcad4b216b8945046a26063b2495cf84b5c4' into mdangelo…
mldangelo-oai Oct 11, 2026
f2c6953
Merge commit 'eaff113896b86ce5d761086ebc824b7d24a29e91' into mdangelo…
mldangelo-oai Oct 11, 2026
134ea8a
Merge commit '76a7db99af02635cac19cf1841fd797cdbba9ccb' into mdangelo…
mldangelo-oai Oct 11, 2026
eb4f795
fix(deep-scan): preserve retained metadata during receipt repair
mldangelo-oai Oct 11, 2026
f7cf5ba
Merge commit '5efc3d9416132c835aba4225a42903c1a7dedf61' into mdangelo…
mldangelo-oai Oct 11, 2026
45c536e
Merge commit 'f7cf5ba123cb985225a1627b0bf6242ceffe9e31' into mdangelo…
mldangelo-oai Oct 11, 2026
f5306d2
test: deduplicate shared finding fixture import after main merge
mldangelo-oai Oct 11, 2026
d3933fd
chore: merge main and resolve integration conflicts
mldangelo-oai Oct 11, 2026
d885ae6
fix: retain admitted receipt preferences during coverage recovery
mldangelo-oai Oct 11, 2026
2a26cff
Merge commit 'd885ae602e6a6b8be5dcde65bf1d2965eafa946f' into mdangelo…
mldangelo-oai Oct 11, 2026
23e0aed
Merge commit 'd3933fd7cbc2d99ab4cfeaa51f56b7ab9ea506c9' into mdangelo…
mldangelo-oai Oct 11, 2026
42aa053
Merge commit '23e0aed425bf2ca240639921b592a1495fe7171d' into mdangelo…
mldangelo-oai Oct 11, 2026
fc28158
fix: include plugin asset types in evaluation builds
mldangelo-oai Oct 11, 2026
a86ebe5
Merge commit 'fc28158c9c5c5466ec6ca6cca3566f1f0c2d9712' into mdangelo…
mldangelo-oai Oct 11, 2026
d9911d6
fix(deep): preserve scoped deadline publication after commit
mldangelo-oai Oct 11, 2026
1cbf39d
test: supply requested scope in deadline rollback fixture
mldangelo-oai Oct 11, 2026
9c4239c
fix(deep): retain receipt selection by coverage surface
mldangelo-oai Oct 11, 2026
6608f46
fix(deep): reserve receipts only for recoverable surface metadata
mldangelo-oai Oct 11, 2026
545748e
Merge reviewed retained coverage parent into deadline recovery
mldangelo-oai Oct 11, 2026
5ec1fa8
Merge main into accepted worker coverage
codex Oct 13, 2026
4ed1cbf
Merge main into accepted worker coverage
mldangelo-oai Oct 17, 2026
425a1cc
Merge updated coverage parent into deadline recovery
mldangelo-oai Oct 17, 2026
640ae71
fix(deep-scan): preserve retained receipts and recovery links
mldangelo-oai Oct 17, 2026
bce354a
Merge commit '640ae71d7e7276a0285581d8f6c014f3ba113d1c' into HEAD
mldangelo-oai Oct 17, 2026
284d429
fix(deep-scan): keep recovered links through ID collisions
mldangelo-oai Oct 17, 2026
7819fba
fix(deep-scan): pass typed finding scope to deadline recovery
mldangelo-oai Oct 17, 2026
a2a4dce
Merge repaired parent coverage recovery
mldangelo-oai Oct 17, 2026
dabf5ba
fix(deep-scan): recover partial deadline findings
mldangelo-oai Oct 17, 2026
7f37ae7
fix: validate deadline findings before duplicate selection
mldangelo-oai Oct 17, 2026
59af917
Merge main into deadline checkpoint recovery
mldangelo-oai Oct 20, 2026
d1611ca
fix(deep-scan): finish rollback after failed snapshot writes
mldangelo-oai Oct 20, 2026
dc7a39a
Merge main into deadline checkpoint recovery
mldangelo-oai Oct 20, 2026
41e872f
Merge remote-tracking branch 'origin/main' into mdangelo/codex/pr-ref…
mldangelo-oai Oct 20, 2026
01928c6
Merge remote-tracking branch 'origin/main' into mdangelo/codex/pr-ref…
mldangelo-oai Oct 20, 2026
98ddee0
Merge main owner-suggestion support
mldangelo-oai Oct 20, 2026
844d318
chore(deep-scan): merge main into deadline recovery
mldangelo-oai Oct 22, 2026
e203e81
Merge main into PR branch
mldangelo-oai Oct 22, 2026
b6b6d44
fix: normalize scoped deadline inventory before finalization
mldangelo-oai Oct 22, 2026
7c8f81a
Merge main into PR branch
mldangelo-oai Oct 23, 2026
59fb6c6
Merge main into deep deadline checkpoint recovery
mldangelo-oai Oct 23, 2026
01daf16
Merge main package smoke retry budget
mldangelo-oai Oct 23, 2026
f3d426a
chore: merge main container security update
mldangelo-oai Oct 23, 2026
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
188 changes: 174 additions & 14 deletions plugins/codex-security/mcp-app/tests/test_deep_scan_store_integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import { mock } from "node:test";
import { execFile, spawn } from "node:child_process";
import { randomUUID } from "node:crypto";
import { once } from "node:events";
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
import {
mkdir,
readFile,
readdir,
rm,
stat,
writeFile,
} from "node:fs/promises";
import path from "node:path";
import { promisify } from "node:util";
import { importModule } from "./import-module.ts";
Expand Down Expand Up @@ -1180,20 +1187,106 @@ async function testDeadlinePreservesValidWorkerCheckpoints() {
scanId: run.scanId,
threadId,
});
const worker = await createWorkerFixture(run, "saved", "discovery");
await store.updateWorker({ ...worker, status: "running" });
await saveScanDraftCheckpoint(
{ root: worker.artifactDir, layout: "worker", repoRoot: targetPath },
{
const workers: { id: string }[] = [];
for (const label of [
"valid",
"second-valid",
"anonymous",
"mismatch",
"corrupt",
"missing",
]) {
const worker = await createWorkerFixture(run, label, "discovery");
await store.updateWorker({ ...worker, status: "running" });
const context = {
root: worker.artifactDir,
layout: "worker",
repoRoot: targetPath,
};
const saved = {
...checkpoint(
run.scanId,
"pending-review",
"Worker did not finish reviewing this source.",
),
findings: [finding("saved", "fixture.py")],
},
);
await store.updateWorker({ ...worker, status: "canceled" });
findings: [finding(label, "fixture.py")],
};
if (label === "valid" || label === "second-valid") {
if (label === "valid") {
saved.findings.push(finding("out-of-scope", "../outside.py"));
saved.findings.push({
title: "Unvalidated, incomplete finding",
} as (typeof saved.findings)[number]);
} else {
saved.findings = [];
}
await mkdir(path.join(worker.artifactDir, "artifacts"));
await writeFile(
path.join(worker.artifactDir, "artifacts/review.md"),
`Source evidence for ${label}.\n`,
);
await saveScanDraftCheckpoint(context, {
...saved,
coverage: {
...saved.coverage,
surfaces: [
{
id: "api",
label: `${label} API`,
disposition: "no_issue_found",
receiptRefs: ["artifacts/review.md"],
},
],
deferred: [
...saved.coverage.deferred,
{
id: "surface-review",
reason: "Review remains.",
surfaceIds: ["api"],
},
],
},
});
} else if (label === "anonymous") {
for (const reason of [
"Review authentication.",
"Review authorization.",
]) {
await saveScanDraftCheckpoint(context, {
scanId: run.scanId,
complete: false,
findings: [],
threatModel: {},
coverage: {
surfaces: [],
explicitExclusions: [],
deferred: [{ reason }],
},
});
}
} else if (label === "mismatch") {
await saveScanDraftCheckpoint(context, {
...saved,
scanId: randomUUID(),
});
} else {
await mkdir(path.join(worker.artifactDir, "checkpoints"), {
recursive: true,
});
const name = "a".repeat(64) + ".json";
await writeFile(
path.join(worker.artifactDir, "checkpoint-head.json"),
JSON.stringify({ checkpoint: name }),
);
if (label === "corrupt")
await writeFile(
path.join(worker.artifactDir, "checkpoints", name),
'{"scanId":',
);
}
await store.updateWorker({ ...worker, status: "canceled" });
workers.push(worker);
}
const context = await createScanArtifactContext(run.scanId, runWorkbench, {
requireRunning: true,
});
Expand Down Expand Up @@ -1229,6 +1322,26 @@ async function testDeadlinePreservesValidWorkerCheckpoints() {
const beforeFailure = await Promise.all(
draftNames.map((name) => readFile(path.join(run.scanDir, name), "utf8")),
);
const readObservationTimes = () =>
Promise.all(
draftNames.map(
async (name) =>
(await stat(path.join(run.scanDir, name), { bigint: true }))
.mtimeNs,
),
);
const observationTimesBeforeFailure = await readObservationTimes();
const checkpointDirectory = path.join(run.scanDir, "checkpoints");
const readParentCheckpoints = async () =>
Promise.all(
(await readdir(checkpointDirectory))
.sort()
.map(async (name) => [
name,
await readFile(path.join(checkpointDirectory, name), "utf8"),
]),
);
const checkpointsBeforeFailure = await readParentCheckpoints();
const database = path.join(
environment.CODEX_SECURITY_STATE_DIR,
"workbench.sqlite3",
Expand Down Expand Up @@ -1265,6 +1378,16 @@ async function testDeadlinePreservesValidWorkerCheckpoints() {
beforeFailure,
"a failed finish commit must restore the unsealed parent and accepted checkpoint head",
);
assert.deepEqual(
await readParentCheckpoints(),
checkpointsBeforeFailure,
"rollback must not publish an aggregate checkpoint as new parent evidence",
);
assert.deepEqual(
await readObservationTimes(),
observationTimesBeforeFailure,
"rollback must preserve parent observation order",
);
assert.equal((await store.get(run.scanId, threadId)).status, "running");
await runSql("DROP TRIGGER reject_deadline_commit;");
const finished = await store.finish({
Expand All @@ -1283,11 +1406,27 @@ async function testDeadlinePreservesValidWorkerCheckpoints() {
const recovered = await readJson(run.scanDir, "findings.json");
assert.deepEqual(
recovered.findings.map((item: { title: string }) => item.title),
["Unsafe request output saved"],
["Unsafe request output valid"],
);
assert.equal(recovered.findings[0].provenance.workerId, worker.id);
assert.equal(recovered.findings[0].provenance.workerId, workers[0].id);
const coverage = await readJson(run.scanDir, "coverage.json");

assert.equal(coverage.surfaces.length, 2);
for (const [index, label] of ["valid", "second-valid"].entries()) {
const workerId: string = workers[index].id;
const surface = coverage.surfaces.find(
(row: { workerId: string }) => row.workerId === workerId,
);
const task = coverage.deferred.find(
(row: { id: string }) => row.id === `${workerId}/surface-review`,
);
assert.equal(surface.id, `${workerId}/api`);
assert.equal(surface.disposition, "no_issue_found");
assert.deepEqual(task.surfaceIds, [surface.id]);
assert.equal(
await readFile(path.join(run.scanDir, surface.receiptRefs[0]), "utf8"),
`Source evidence for ${label}.\n`,
);
}
assert.equal(
coverage.deferred.some((item: { reason: string }) =>
/time limit elapsed/.test(item.reason),
Expand All @@ -1301,12 +1440,25 @@ async function testDeadlinePreservesValidWorkerCheckpoints() {
),
true,
);
const anonymousTasks = coverage.deferred.filter(
(row: { workerId?: string }) => row.workerId === workers[2].id,
);
assert.deepEqual(
new Set(anonymousTasks.map((row: { reason: string }) => row.reason)),
new Set(["Review authentication.", "Review authorization."]),
);
const manifest = await readJson(run.scanDir, "scan-manifest.json");
assert.equal(manifest.scan.threatModel, undefined);
const savedScan = await runWorkbench(["get-scan", "--scan-id", run.scanId]);
assert.match(
JSON.stringify(savedScan),
/Skipped malformed saved threat model.*missing required schema property/,
);
assert.equal(manifest.scan.sealedAt, undefined);
assert.equal(manifest.scan.complete, undefined);
assert.equal(
Object.keys(manifest.scan.preservedSources).some((source) =>
source.includes("saved/output/checkpoints/"),
source.includes("valid/output/checkpoints/"),
),
true,
);
Expand All @@ -1325,6 +1477,14 @@ async function testDeadlinePreservesValidWorkerCheckpoints() {
beforeReplay,
);
await runWorkbench(["complete-scan", "--scan-id", run.scanId]);
assert.deepEqual(
(await readJson(run.scanDir, "coverage.json")).surfaces,
coverage.surfaces,
);
assert.deepEqual(
(await readJson(run.scanDir, "coverage.json")).deferred,
coverage.deferred,
);
const sealed = await readJson(run.scanDir, "scan-manifest.json");
assert.equal(typeof sealed.scan.sealedAt, "string");
assert.deepEqual(
Expand Down
44 changes: 27 additions & 17 deletions plugins/codex-security/scripts/deep_scan_workbench.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import sqlite3
import sys
import uuid
from contextlib import nullcontext
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any, Callable
Expand Down Expand Up @@ -1676,6 +1675,9 @@ def finish_deep_scan_locked(
if len(set(omitted_worker_ids)) != len(omitted_worker_ids):
raise SystemExit("Omitted Deep Scan worker IDs must be unique.")
promotion: tuple[Path, Path, Path | None] | None = None
deadline_publication: (
tuple[Path, dict[str, tuple[bytes, os.stat_result] | None], set[str]] | None
) = None
connection.execute("BEGIN IMMEDIATE")
try:
run = require_deep_scan_run(connection, scan_id)
Expand Down Expand Up @@ -1799,25 +1801,33 @@ def finish_deep_scan_locked(
kind="file",
)
promotion = promote_staged_file(staged_manifest_path, manifest_path)
with (
saved_results.preserve_deep_deadline_draft(dependencies(), connection, scan)
if zero_discovery_deadline and standard_scan_manifest
else nullcontext()
):
timestamp = dependencies().now()
connection.execute(
"""
UPDATE deep_scan_runs
SET status = 'succeeded', phase = 'terminal', terminal_reason = ?,
manifest_path = ?, completed_at = ?, updated_at = ?
WHERE scan_id = ?
""",
(args.terminal_reason, manifest_path, timestamp, timestamp, scan_id),
if zero_discovery_deadline:
deadline_publication = saved_results.preserve_deep_deadline_draft(
dependencies(), connection, scan, allow_empty=not standard_scan_manifest
)
cancel_active_workers(connection, scan_id, timestamp)
connection.commit()
timestamp = dependencies().now()
connection.execute(
"""
UPDATE deep_scan_runs
SET status = 'succeeded', phase = 'terminal', terminal_reason = ?,
manifest_path = ?, completed_at = ?, updated_at = ?
WHERE scan_id = ?
""",
(args.terminal_reason, manifest_path, timestamp, timestamp, scan_id),
)
cancel_active_workers(connection, scan_id, timestamp)
connection.commit()
except BaseException:
connection.rollback()
if deadline_publication is not None or promotion is not None:
# A signal can arrive after SQLite commits but before commit returns.
# The completion lock makes persisted success authoritative here.
if require_deep_scan_run(connection, scan_id)["status"] == "succeeded":
if promotion is not None:
finish_staged_file(promotion)
raise
if deadline_publication is not None:
saved_results._restore_published_outputs(*deadline_publication)
if promotion is not None:
rollback_staged_file(promotion)
raise
Expand Down
4 changes: 4 additions & 0 deletions plugins/codex-security/scripts/finalize_scan_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,8 @@ def _recover_unsealed_findings(
schema_dir: Path,
scan_dir: Path,
warnings: list[str],
*,
current_contract: bool = False,
) -> list[str]:
schema = _read_json(schema_dir / "findings.schema.json")
properties = _require_dict(schema, "properties", "findings.schema")
Expand Down Expand Up @@ -1190,6 +1192,8 @@ def _recover_unsealed_findings(
f"Skipped malformed {auxiliary} for finding {index + 1}: {exc}."
)
_validate_schema_node(finding, finding_schema, context, schema)
if current_contract:
_validate_current_findings({"findings": [finding]}, schema_dir)
except ContractError as exc:
warning = f"Skipped malformed finding {index + 1}: {exc}."
warnings.append(warning)
Expand Down
Loading
Loading