Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
70 changes: 40 additions & 30 deletions .github/release-notes.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,47 @@
<!-- release-version: 0.1.27 -->
<!-- release-version: 0.1.28 -->

<!-- release-section: highlights:start -->
## Highlights

- port source and test report checks to TypeScript ([#768](https://github.com/openai/codex-security/pull/768))
- port custom validation to TypeScript ([#792](https://github.com/openai/codex-security/pull/792))
- add Python-free Unix OS primitives ([#794](https://github.com/openai/codex-security/pull/794))
- add Python-free Windows OS primitives ([#795](https://github.com/openai/codex-security/pull/795))
- add Python-free musl native artifacts ([#796](https://github.com/openai/codex-security/pull/796))
- bundle verified native runtime artifacts ([#797](https://github.com/openai/codex-security/pull/797))
- preserve Windows filenames in native helpers ([#798](https://github.com/openai/codex-security/pull/798))
- port security policy resolution to TypeScript ([#799](https://github.com/openai/codex-security/pull/799))
- budget package installation and verification ([#834](https://github.com/openai/codex-security/pull/834))
- open ready pull requests ([#833](https://github.com/openai/codex-security/pull/833))
- draft SECURITY.md for owner review ([#536](https://github.com/openai/codex-security/pull/536))
- resolve Windows Node to an absolute executable ([#788](https://github.com/openai/codex-security/pull/788))
- shard automatic component planning for large repositories ([#845](https://github.com/openai/codex-security/pull/845))
- import CSV and JSON findings as saved scans ([#850](https://github.com/openai/codex-security/pull/850))
- add a feedback command ([#854](https://github.com/openai/codex-security/pull/854))
- improve scan usage and cost reports ([#853](https://github.com/openai/codex-security/pull/853))
- recover interrupted Deep Scans and bulk campaigns ([#835](https://github.com/openai/codex-security/pull/835))
- reject owned plugin keys inside Codex profiles ([#861](https://github.com/openai/codex-security/pull/861))
- include CommonJS and TypeScript module extensions in scan inventories ([#859](https://github.com/openai/codex-security/pull/859))
- include workflow files in diff inventories ([#820](https://github.com/openai/codex-security/pull/820))
- accept a UTF-8 byte order mark in imported findings JSON ([#856](https://github.com/openai/codex-security/pull/856))
- authenticate deep workers with OpenAI API keys ([#870](https://github.com/openai/codex-security/pull/870))
- drop canonical document size limits that do not exist ([#868](https://github.com/openai/codex-security/pull/868))
- pin native Rust formatting edition ([#863](https://github.com/openai/codex-security/pull/863))
- detect a Linear URL contradiction over plain HTTP ([#867](https://github.com/openai/codex-security/pull/867))
- pipeline dedupe with configurable concurrency ([#852](https://github.com/openai/codex-security/pull/852))
- disable reasoning summaries by default for Bedrock ([#869](https://github.com/openai/codex-security/pull/869))
- reuse scan authentication for patch and validation ([#871](https://github.com/openai/codex-security/pull/871))
- bump smol-toml from 1.6.1 to 1.7.1 in /sdk/typescript ([#873](https://github.com/openai/codex-security/pull/873))
- use versioned conventional commit titles ([#879](https://github.com/openai/codex-security/pull/879))
- share scan settings across the CLI and SDK ([#742](https://github.com/openai/codex-security/pull/742))
- keep parameterized JUnit names unique ([#877](https://github.com/openai/codex-security/pull/877))
- accept large saved post-scan prompts ([#876](https://github.com/openai/codex-security/pull/876))
- make cost display optional ([#881](https://github.com/openai/codex-security/pull/881))
- collect Desktop and worker session logs ([#872](https://github.com/openai/codex-security/pull/872))
- skip unavailable Daybreak access advisories ([#878](https://github.com/openai/codex-security/pull/878))
- align Codex 0.154.0 and dependency cooldowns ([#755](https://github.com/openai/codex-security/pull/755))
- bump docker/setup-buildx-action from 4.2.0 to 4.3.0 ([#891](https://github.com/openai/codex-security/pull/891))
- bump actions/attest-build-provenance from 4.1.1 to 4.2.2 ([#886](https://github.com/openai/codex-security/pull/886))
- update setuptools requirement from \>=64.0 to \>=84.0.0 in /plugins/codex-security ([#889](https://github.com/openai/codex-security/pull/889))
- bump SocketDev/action from 1.3.0 to 1.3.2 ([#884](https://github.com/openai/codex-security/pull/884))
- bump ruff from 0.16.1 to 0.16.6 in /plugins/codex-security ([#887](https://github.com/openai/codex-security/pull/887))
- bump typescript from 6.0.3 to 7.0.2 in /plugins/codex-security/mcp-app ([#894](https://github.com/openai/codex-security/pull/894))
- bump pytest from 9.0.3 to 9.1.1 in /plugins/codex-security ([#885](https://github.com/openai/codex-security/pull/885))
- bump typescript from 5.7.3 to 7.0.2 in /sdk/typescript ([#898](https://github.com/openai/codex-security/pull/898))
- simplify fast-uri dependency and update Ajv resolver ([#895](https://github.com/openai/codex-security/pull/895))
- bump @linear/sdk from 89.0.0 to 93.0.1 in /sdk/typescript ([#896](https://github.com/openai/codex-security/pull/896))
- bump actions/checkout from 6.0.2 to 7.0.1 ([#890](https://github.com/openai/codex-security/pull/890))
- bump actions/setup-node from 6.3.0 to 7.0.0 ([#888](https://github.com/openai/codex-security/pull/888))
- bump @types/node from 22.19.17 to 26.4.1 in /sdk/typescript ([#897](https://github.com/openai/codex-security/pull/897))
- bump @types/node from 25.9.1 to 26.4.1 in /plugins/codex-security/mcp-app ([#893](https://github.com/openai/codex-security/pull/893))
- bump the third-party group across 3 directories with 19 updates ([#892](https://github.com/openai/codex-security/pull/892))
- report patch failures and changed files ([#874](https://github.com/openai/codex-security/pull/874))
- persist scan artifacts through MCP ([#862](https://github.com/openai/codex-security/pull/862))
- advance scan progress when saving drafts ([#882](https://github.com/openai/codex-security/pull/882))
- simplify scan runtime bookkeeping ([#903](https://github.com/openai/codex-security/pull/903))
- upgrade json-schema-to-typescript to 16.0.0 ([#914](https://github.com/openai/codex-security/pull/914))
- upgrade eval OpenCode SDK to 1.18.29 ([#911](https://github.com/openai/codex-security/pull/911))
- upgrade Ink and use complete Escape input in TUI tests ([#913](https://github.com/openai/codex-security/pull/913))
- upgrade pnpm setup and align the package-manager pin ([#910](https://github.com/openai/codex-security/pull/910))
- upgrade actions/setup-python to 7.0.0 ([#909](https://github.com/openai/codex-security/pull/909))
- upgrade Stryker with compatible mutation tooling ([#912](https://github.com/openai/codex-security/pull/912))
- limit fix-finding to security vulnerabilities ([#923](https://github.com/openai/codex-security/pull/923))
- accept text knowledge-base files with any extension ([#924](https://github.com/openai/codex-security/pull/924))
- declare native release workflow permissions ([#925](https://github.com/openai/codex-security/pull/925))
- report context-aware estimate ranges ([#926](https://github.com/openai/codex-security/pull/926))
- update vulnerable image and archive dependencies ([#927](https://github.com/openai/codex-security/pull/927))
- update Inquirer prompts and Node/Bun types ([#929](https://github.com/openai/codex-security/pull/929))
<!-- release-section: highlights:end -->

<!-- release-section: upgrades:start -->
Expand Down
6 changes: 3 additions & 3 deletions .github/release-pr-state.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"baseVersion": "0.1.26",
"baseCommit": "2536d104deef9bca8ced84c6f6263b915418253b",
"baseVersion": "0.1.27",
"baseCommit": "c40d059935592adc1ae04119eb7ad5286d9de554",
"sections": {
"highlights": {
"generatedHash": "488ce0a2eadd5a3718a22b68227f42dbba6f3f57b1ad9259cc850c397318a977",
"generatedHash": "d0496c2cdec032d1ca5d28e8ae63b650918b93ebe458c76328734d47edc970d4",
"humanOwned": false
},
"upgrades": {
Expand Down
32 changes: 26 additions & 6 deletions plugins/codex-security/mcp-app/src/artifact-deep-reducer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { join } from "node:path";
import { dirname, join, relative, sep } from "node:path";
import type { ZodType } from "zod/v4";
import commonSchema from "../../schemas/definitions/artifact-common.schema.json";
import reducerSchema from "../../schemas/tools/deep-reducer.schema.json";
Expand All @@ -20,7 +20,9 @@ import {
type DeepScanArtifacts
} from "./deep-scan/artifacts.js";
import {
deepReductionForPersistence,
parseDeepReduction,
projectDiscoveryCoverage,
reconcileDeepReduction,
type DeepReductionInput,
type DeepReductionSources,
Expand Down Expand Up @@ -54,6 +56,18 @@ interface BoundReducer {
/** Read the findings and scan context assigned to this reducer. */
export async function getCodexSecurityDeepReducerInputs(
context: ArtifactContext
): Promise<DeepReductionSources> {
const inputs = await readDeepReductionSources(context);
const { sourceCoverage: _coverage, ...previous } = inputs.previous ?? {};
return {
discoveries: inputs.discoveries.map(({ workerId, result }) => ({ workerId, result })),
previous: inputs.previous === null ? null : previous as DeepReductionInput,
};
}

/** Capture host coverage alongside the reducer's immutable finding inputs. */
export async function readDeepReductionSources(
context: ArtifactContext
): Promise<DeepReductionSources> {
return withLogicalReducerErrors(context, async () => {
const bound = bindDeepReducer(context);
Expand All @@ -73,8 +87,13 @@ export async function getCodexSecurityDeepReducerInputs(
sourceFindingIds: [`${worker.id}:${index}`],
},
}));
const { coverage: _coverage, ...reduction } = result;
return { workerId: worker.id, result: reduction };
const { coverage, ...reduction } = result;
return {
workerId: worker.id,
...(worker.attempt === undefined ? {} : { attempt: worker.attempt }),
coverage: projectDiscoveryCoverage(coverage, worker, relative(bound.artifacts.scanDir, dirname(worker.resultPath)).split(sep).join("/")),
result: reduction,
};
}));
const previous = await readPreviousReduction(bound);
const scanId = bound.scanId ?? previous?.scanId ?? discoveries[0]?.result.scanId;
Expand Down Expand Up @@ -107,7 +126,7 @@ export async function recordCodexSecurityDeepReduction(
const submitted = deepReductionInputSchema.parse(input);
let reduction = parseDeepReduction(submitted);
if (reduction.complete === false) throw new Error("Deep reduction is only a checkpoint, not a complete result.");
const inputs = await getCodexSecurityDeepReducerInputs(context);
const inputs = await readDeepReductionSources(context);
const expectedScanId = bound.scanId
?? inputs.previous?.scanId
?? inputs.discoveries[0]?.result.scanId;
Expand All @@ -116,8 +135,9 @@ export async function recordCodexSecurityDeepReduction(
}
reduction = reconcileDeepReduction(reduction, inputs.discoveries, inputs.previous);

await saveScanDraftCheckpoint(context, reduction);
await writeJsonAtomic(bound.resultPath, reduction);
const persisted = deepReductionForPersistence(reduction, bound.state.persistSourceCoverage);
await saveScanDraftCheckpoint(context, persisted);
await writeJsonAtomic(bound.resultPath, persisted);
return {
findingCount: reduction.findings.length,
consumedWorkerIds: bound.state.claimedWorkers.map((worker) => worker.id)
Expand Down
2 changes: 2 additions & 0 deletions plugins/codex-security/mcp-app/src/artifact-io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { dirname, isAbsolute, join, resolve, sep } from "node:path";
export interface DeepReducerWorkerContext {
id: string;
resultPath: string;
attempt?: number;
}

export interface DeepReducerContext {
scanRoot: string;
claimedWorkers: DeepReducerWorkerContext[];
previousReducerResultPath?: string;
persistSourceCoverage?: boolean;
}

/**
Expand Down
17 changes: 14 additions & 3 deletions plugins/codex-security/mcp-app/src/artifact-scan-draft.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createHash, randomUUID } from "node:crypto";
import { promises as fs } from "node:fs";
import { dirname, join, sep } from "node:path";
import { basename, dirname, join, sep } from "node:path";
import type * as z from "zod/v4";
import commonSchema from "../../schemas/definitions/artifact-common.schema.json";
import scanDraftDocument from "../../schemas/tools/scan-draft.schema.json";
Expand Down Expand Up @@ -596,6 +596,7 @@ async function readArchivedWorkerCheckpoints(
}

const archived: ScanDraftInput[] = [];
const archivePrefix = `artifacts/deep_discovery/workers/${basename(workerRoot)}/attempts/`;
const attempts = (await fs.readdir(canonicalAttemptsRoot, { withFileTypes: true }))
.filter((entry) => entry.isDirectory() && !entry.isSymbolicLink())
.sort((left, right) => archivedAttemptNumber(right.name) - archivedAttemptNumber(left.name)
Expand Down Expand Up @@ -704,8 +705,18 @@ async function readArchivedWorkerCheckpoints(
drafts.sort((left, right) => right.modifiedMs - left.modifiedMs
|| Number(right.result) - Number(left.result)
|| right.name.localeCompare(left.name));
if (checkpointHead !== undefined) archived.push(checkpointHead);
archived.push(...drafts.map((draft) => draft.input));
const inputs = [...(checkpointHead ? [checkpointHead] : []), ...drafts.map((draft) => draft.input)];
for (const input of inputs) {
// The archive moved the receipts with this attempt. Rebase only the
// retained projection; the original checkpoint bytes remain unchanged.
for (const surface of input.coverage.surfaces as JsonObject[]) {
if (!Array.isArray(surface.receiptRefs)) continue;
surface.receiptRefs = (surface.receiptRefs as string[]).map((ref) => (
ref.startsWith(archivePrefix) ? ref : `${archivePrefix}${attempt.name}/${ref}`
));
}
archived.push(input);
}
}
return archived;
}
Expand Down
Loading
Loading