Skip to content

refactor(deep-scan)!: unify execution and durable result recovery - #1123

Merged
mldangelo-oai merged 19 commits into
mdangelo/codex/pr939-stack-02-severity-historyfrom
mdangelo/codex/pr939-stack-18-native-preparation
Oct 4, 2026
Merged

mldangelo-oai merged 19 commits into
mdangelo/codex/pr939-stack-02-severity-historyfrom
mdangelo/codex/pr939-stack-18-native-preparation

Conversation

@mldangelo-oai

@mldangelo-oai mldangelo-oai commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Run SDK and plugin Deep Scans through one lifecycle built from ordinary Standard child scans. Save accepted findings, child membership, matching decisions, and accounting so an interrupted scan can continue or finish publication without repeating completed discovery or accepted matching.

Draft publication now commits a complete snapshot before exporting files. Recovery preserves accepted assessments and original evidence, and explicit recovery can incorporate corrected child results without changing unrelated parent or sibling findings.

Changes

  • Share preparation, registration, execution, cancellation, and completion across the SDK and plugin. Fresh and resumed discovery and matching workers inherit the selected executable, provider, authentication, profile, reference documents, and permissions. Per-scan overrides remain isolated during concurrent startup; saved empty reference selections remain empty. Preserve Windows package executable discovery and fresh setups with no existing Codex home.
  • Run bounded batches of Standard scans and combine their verified results with the existing finding matcher. Version-3 composition checkpoints store accepted progress separately from original sources and earlier revisions. Interrupted child retirement, matching, and publication can retry from their saved state; cancellation retains the original failure and completed work.
  • Publish stable-ID drafts through the locked workbench writer. Save pending evidence before immutable history and commit one snapshot before canonical exports. Preserve generic deferred-task closures and reopening, conflicting evidence, threat models, and optional projection warnings. Frozen publication retries retain their selected sources; explicit recovery refreshes child findings, locations, coverage, withdrawals, and provenance.
  • Retain original child writeups and supporting files, verify their saved seals and target binding, and include distinct source and historical evidence in reports. Store severity assessments per scan, recover eligible legacy assessments, and repair child membership and finding-history projections without exposing internal passes as independent history entries.
  • Store execution-session ownership and measured accounting in SQLite. Preserve known receipts across interrupted or partial measurement, keep unknown cost distinct from zero, recover child usage from live and archived session logs, and load verified sealed results without starting another Codex session when no follow-up is requested. Follow-up instructions use their own conversation and output directory while completed artifacts remain protected.
  • Remove the retired coordinator, worker/reducer runtime, Python engine, and artifact protocol. Keep historical reports, recorded worker-session accounting, active artifact tools, and supported legacy draft evidence readers. Bundle the shared SDK code and document-reader dependencies with the plugin.

Testing

Completed on the integrated changes:

  • Full Python suite: 1,673 passed, 8 skipped, plus 162 subtests passed.

  • Full MCP suite: 172 passed, no failures or skips.

  • Ruff lint and format, MCP TypeScript check, SDK build:ci, SDK formatting and type checks: passed.

  • Portable plugin source compatibility and its 9 tests: passed.

  • Plugin and production builds, the 617-entry package check, and installed-package smoke tests: passed.

  • Focused recovery, worker-launch, publication-retry, and concurrent-draft regression tests: passed.

  • Three fresh native code reviews and independent verification on the reviewed integration commit: passed, with no findings.

  • Full SDK suite in both seed-12345 and randomized order, three CI shards each: 3,990 passed, 48 skipped per run across 177 files. One randomized shard hit a 30-second timeout during high host load; rerunning that entire shard alone with the same seed and unchanged timeout passed. The original timeout log was retained.

Risk and rollout

The base PR remains open against main. New changes merged into main during consolidation overlap this work and require a separate conflict-resolution pass before the base PR can merge.

Live Deep Scan continuation requires version-3 checkpoints, the original checkout, and the owning merge-session logs once matching has started. Finish unfinished coordinator/version-2 scans with their original version or start a new scan. Historical artifacts remain readable; completing an older sealed Deep Scan with required cost tracking still requires a usable receipt. A saved accepted version-3 merge can retry publication without repeating discovery or matching.

SDK callers must allow ScanResult.threadId and ScanResultOptions.threadId to be null. Draft clients must reuse finding identities and coverage/deferred IDs for updates; missing IDs create new rows, except an unambiguous deferred candidate can reuse its existing ID. postScanPrompt starts a separate conversation with explicit saved-result context and a working directory under artifacts/follow-up/<id>. Requested repository changes retain the scan's existing permissions; sealed scan artifacts remain protected.

Workbench compatibility changes:

  • begin-deep-scan returns the SDK scan record and removes --available-parallelism and --workflow-version.
  • Retire get-deep-scan, claim-deep-scan-coordinator, upsert-deep-scan-worker, claim-deep-scan-dedup, commit-deep-scan-dedup, finish-deep-scan, fail-deep-scan, and record-deep-scan-publication-failure, plus coordinator-generation and MCP artifact-writer dispatch.
  • write-scan-draft --scan-id <id> accepts {documents, checkpoint} on stdin when --draft-path is omitted; staged-file input remains supported.
  • Add optional --claim-token to continuation/accounting operations, --defer-publication to cancel-scan and fail-scan, and --after-stop / --cost-json to preserve-scan-results. Ordinary stop commands continue publishing available results by default.

--workers now controls a batch of independent Standard scans, merged in original order before the next batch starts. --stop-after-no-new counts successfully merged scans and is checked after each batch, so a batch can pass the threshold; failed scans consume the discovery-run budget without counting toward that threshold. Public scans resume SCAN_ID remains limited to eligible Deep Scans. Deep Scan option defaults are unchanged. Database migrations apply through the existing migration runner; already-overwritten historical assessments cannot be reconstructed. Source plugin builds require the SDK checkout, while the distributed plugin contains its runtime dependencies.

Public disclosure review

  • No customer, partner, prospect, or user identities, data, or identifying details are included.
  • No credentials, personal data, private source, scan findings, or nonpublic links or tickets are included.
  • I reviewed the branch name, title, description, commits, changes, comments, logs, screenshots, attachments, and links for public disclosure.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-05T00:08:37.756021Z f3f7540 New commits
🔒 Security Review ✅ Completed 2026-10-04T23:59:32.075817Z f3f7540 New commits

Security findings

Blocking findings (1)

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@mldangelo-oai

Copy link
Copy Markdown
Collaborator Author

@codex review

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Tilde-based Codex homes fail during environment snapshotting, and most new native runtime tests are not executed by CI.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
What changed in this PR

Prepares native scans to inherit the invoking Codex runtime, configuration, authentication, executable, and permissions.

Changes:

  • Adds ambient runtime and native scan preparation.
  • Adds trusted executable and sandbox-permission handling.
  • Adds native runtime tests and CI coverage.
File Description
.github/​workflows/​node-ci.yml Adds native test steps.
sdk/​typescript/​tests-support/​process-environment.mjs Adds environment restoration helper.
sdk/​typescript/​src/​version.ts Loads versions via bundled JSON import.
sdk/​typescript/​src/​execution-preparation.ts Adds ambient execution/runtime preparation.
sdk/​typescript/​src/​auth.ts Forwards authentication configuration.
sdk/​typescript/​src/​api.ts Integrates ambient runtime dependencies.
plugins/​codex-security/​mcp-app/​tsconfig.json Adds DOM library types and reformats includes.
plugins/​codex-security/​mcp-app/​src/​native-scan.ts Prepares native scan clients and options.
plugins/​codex-security/​mcp-app/​src/​native-permissions.ts Resolves inherited sandbox restrictions.
plugins/​codex-security/​mcp-app/​src/​native-executable.ts Resolves trusted Codex executables and environment.
plugins/​codex-security/​mcp-app/​tests/​test_native_scan.mjs Tests native scan preparation.
plugins/​codex-security/​mcp-app/​tests/​test_native_permissions.mjs Tests sandbox metadata handling.
plugins/​codex-security/​mcp-app/​tests/​test_native_executable.mjs Tests executable discovery.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/node-ci.yml Outdated
Comment on lines +468 to +469
- name: Test native scan preparation
run: node --test --test-name-pattern="native preparation" plugins/codex-security/mcp-app/tests/test_native_scan.mjs
delete environment.CODEX_HOME;
} else if (codexHome !== undefined && codexHome.length > 0) {
// Resolve symlink/.. paths before consumers normalize them or change cwd.
environment.CODEX_HOME = await fs.realpath(codexHome);

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5bfc9854b6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sdk/typescript/src/api.ts
Comment on lines +251 to +255
registeredScan?: {
scanId: string;
scanDir: string;
threadId: string;
handoffClaimToken?: string;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Honor the claimed scan registration

When the client returned by prepareNativeScan is run, this field is never consumed: registerScan does not include registeredScan in its ScanOptions pick or forward its scanId, threadId, and claim token in the registration JSON. The workbench consequently takes the fresh-registration path instead of binding the already claimed native scan, which will normally fail because that scan directory is already populated (or could create a second scan record). Wire this metadata through the registration path before exposing it on the prepared options.

Useful? React with 👍 / 👎.

platform,
originalCwd,
)) {
if (platform === "win32" && isWindowsAppsPath(candidate)) continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve the managed Windows Codex binary

On Windows installations where CODEX_CLI_PATH is a WindowsApps alias—or where Codex is available only through CODEX_MANAGED_PACKAGE_ROOT/the LOCALAPPDATA cache—this skips the alias and then has no usable candidate, so every native scan fails with “Could not resolve a Codex executable.” The existing discovery-worker resolver in deep-scan/executor.ts handles these exact environments by resolving the managed package or cached native binary; reuse that resolution here rather than limiting lookup to direct PATH entries and the cwd.

AGENTS.md reference: AGENTS.md:L30-L34

Useful? React with 👍 / 👎.

delete environment.CODEX_HOME;
} else if (codexHome !== undefined && codexHome.length > 0) {
// Resolve symlink/.. paths before consumers normalize them or change cwd.
environment.CODEX_HOME = await fs.realpath(codexHome);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize CODEX_HOME before resolving it

When CODEX_HOME uses a supported tilde form such as ~/.codex or contains surrounding whitespace, this calls realpath on the literal, untrimmed value. The normal configuredCodexHome path handling trims and expands these forms, so native preparation instead throws ENOENT before it can use the invoking account and credentials. Normalize and expand the value before canonicalizing it for the later cwd change.

AGENTS.md reference: AGENTS.md:L49-L50

Useful? React with 👍 / 👎.

Comment on lines +51 to +52
(await gitMarkerRoot(input.scan.targetPath, signal, "outermost")) ??
input.scan.targetPath,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude every untrusted scan root from executable discovery

Executable trust is checked only against targetPath, even though the existing scan directory and each knowledgeBasePaths source are also repository- or model-controlled inputs. If PATH contains the scan directory (for example through .) or a separate knowledge-base repository, resolveTrustedCodex accepts and executes a codex binary from that location because it lies outside the sole protected root; prepareAmbientExecution can then run it during authentication with the invoking user's environment. Resolve and exclude all scan-controlled roots before selecting the executable.

AGENTS.md reference: AGENTS.md:L6-L8

Useful? React with 👍 / 👎.

@mldangelo-oai mldangelo-oai changed the title refactor: prepare native scans with the invoking runtime refactor: prepare plugin scans with the caller's Codex settings Sep 30, 2026

@alandelong-oai alandelong-oai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 5bfc9854b68ee044dd8e94bab05dfb9c6f1dc3d8 against mdangelo/codex/pr939-stack-17-scan-recovery at 1a009b9bd2c41c344af4fa454c9f596b9c2a4499, scoped to this PR's diff and touched files.

Reviewed native executable/configuration/authentication selection, inherited filesystem permissions, and preparation coverage. No additional distinct findings beyond the existing review comments. The native-permissions assertions passed against the exact TypeScript source using an adapted Node loader; this was not a packaged-runtime test.

Review included simplification/deletion opportunities. Full repository and platform test suites were not run.

@mldangelo-oai
mldangelo-oai force-pushed the mdangelo/codex/pr939-stack-18-native-preparation branch from 5bfc985 to e53883b Compare September 30, 2026 16:55
@mldangelo-oai
mldangelo-oai force-pushed the mdangelo/codex/pr939-stack-17-scan-recovery branch from 1a009b9 to abd6b3a Compare September 30, 2026 16:55

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e53883b183

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return {
client,
options: {
...options,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Enforce the saved plugin version before resuming

When a claimed scan is resumed after the plugin has been upgraded, this options object ignores recipe.pluginVersion, so the SDK's existing expectedPluginVersion check never runs and the new plugin can continue artifacts created under an incompatible schema or workflow. The CLI resume loader already maps this recipe field to expectedPluginVersion; native preparation should do the same so a version mismatch fails before modifying the scan.

AGENTS.md reference: AGENTS.md:L11-L12

Useful? React with 👍 / 👎.

@mldangelo-oai
mldangelo-oai force-pushed the mdangelo/codex/pr939-stack-18-native-preparation branch from e53883b to c9c9ae4 Compare September 30, 2026 17:19
@mldangelo-oai
mldangelo-oai force-pushed the mdangelo/codex/pr939-stack-17-scan-recovery branch from abd6b3a to 10e9da2 Compare September 30, 2026 17:19

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c9c9ae4de4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

persistentCredentialHome: true,
preserveCodexHomeConfig: true,
bootstrapWorkspace,
configPath: join(bootstrapWorkspace, "config-preflight.toml"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Store native Deep Scan config in the isolated workspace

When this runtime executes a native Deep Scan, leaving deepScanConfigPath unset makes #runLocal fall back to <codexHome>/codex-security/config.toml and write the resolved settings there. Fresh scans therefore mutate the caller's persistent configuration, resumed scans can replace it with saved settings, and concurrent scans share the same file; when CODEX_SECURITY_DEEP_SCAN_CONFIG_PATH selects another file, workers can also read a different configuration from the one the SDK wrote. Set deepScanConfigPath to a scan-local path under bootstrapWorkspace, alongside configPath.

AGENTS.md reference: AGENTS.md:L30-L34

Useful? React with 👍 / 👎.

@mldangelo-oai
mldangelo-oai force-pushed the mdangelo/codex/pr939-stack-17-scan-recovery branch from 10e9da2 to 81fa0fb Compare September 30, 2026 18:17
@mldangelo-oai
mldangelo-oai force-pushed the mdangelo/codex/pr939-stack-18-native-preparation branch from c9c9ae4 to 06527bb Compare September 30, 2026 18:17

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 06527bb891

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

execution.environment["CODEX_HOME"] ||
configuredCodexHome(execution.environment),
);
const bootstrapWorkspace = await createIsolatedHome();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the native bootstrap outside protected roots

When the scan target contains the OS temporary directory—for example, a repository-local TMPDIR or a scan of /tmp—this creates the bootstrap workspace inside the untrusted target because it omits the location validator used by the ordinary #prepareRuntime path. The scan sandbox grants workspace-root writes, and the generated config and copied plugin are exposed through this directory, so repository-driven commands can modify scan runtime assets or cause the scan to inspect its own artifacts. Pass the protected-root validator through native runtime preparation, or otherwise reject a bootstrap workspace nested under any protected root.

AGENTS.md reference: sdk/typescript/AGENTS.md:L21-L23

Useful? React with 👍 / 👎.

Comment on lines +152 to +155
options: {
...options,
...deep.settings,
inheritedPermissions,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Propagate cancellation to the prepared scan

When the caller aborts after prepareNativeScan has returned, the signal supplied to this helper is absent from the returned ScanOptions, so the subsequent client.run(..., prepared.options) continues launching and running discovery and reducer work despite cancellation, potentially consuming the remaining scan budget. Include signal in the prepared options so the same cancellation lifetime covers both preparation and execution.

Useful? React with 👍 / 👎.

Comment on lines +489 to +493
const marketplaceRoot =
preparedPlugin?.marketplaceRoot ??
(await createMarketplace(
bootstrapWorkspace,
execution.pluginRoot,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register the isolated plugin before launching native scans

createMarketplace only copies the plugin and writes its marketplace manifest; unlike bootstrapPlugin, it does not register the marketplace or enable the plugin in Codex configuration. Because nativeScanConfiguration also strips any existing managed plugin registration and this runtime keeps the caller's CODEX_HOME, the SDK child has no configuration reference to this isolated marketplace, so the Codex Security MCP server is not loaded and a Deep Scan cannot call start_codex_security_deep_scan. Add the isolated marketplace and enabled-plugin registration to the effective session configuration without mutating the caller's home.

Useful? React with 👍 / 👎.

@faizan-oai faizan-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed native settings, authentication, executable selection, and worker permission forwarding as part of the full stack. One reproducible test issue below: a caller's exported credential can make the fixture fail and appear in assertion output. The focused native suites pass when credential variables are isolated.

Comment on lines +693 to +699
const keys = [
"PATH",
"CODEX_HOME",
"CODEX_CLI_PATH",
"CODEX_SECURITY_CONFIG_PATH",
"CODEX_SECURITY_DEEP_SCAN_CONFIG_PATH",
];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Isolate credential variables in this fixture

This saved/restored key list omits OPENAI_API_KEY and CODEX_API_KEY, while prepareNativeScan snapshots the caller's environment. With OPENAI_API_KEY=synthetic-review-openai-key and no CODEX_API_KEY, the synthetic child retains that key and assert.equal(process.openai, undefined) fails, printing the value into the test log. I reproduced this at stack tip 24713fb088ba; clearing both keys makes the case pass. Add both names to this list and delete both variables before preparing the fixture, letting the existing synthetic keys supply the test credentials. I verified that four-line change under the same synthetic environment.

@mldangelo-oai mldangelo-oai changed the title refactor: prepare plugin scans with the caller's Codex settings refactor(plugin): prepare scans with the caller's Codex settings Oct 1, 2026

@alandelong-oai alandelong-oai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the native preparation change at 06527bb891794d687f4aa32767bb130ef6bcef1c with three independent passes and root verification. Two existing issues remain reproducible:

The three new native test files pass in the baseline environment. Full native-to-SDK activation is reviewed separately at its stack boundary. No duplicate inline comments added.

@mldangelo-oai
mldangelo-oai force-pushed the mdangelo/codex/pr939-stack-18-native-preparation branch from 06527bb to fdef28b Compare October 1, 2026 23:44

@alandelong-oai alandelong-oai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three independent restack passes and root reconciliation are complete.

The test credential isolation fix is present, but the own-head ambient runtime still has no isolated Deep configuration path. Per-run overrides therefore fall back to the invoking account's codex-security/config.toml, preserving the previously reproduced settings mutation. The later #1162 runtime removes this write; the current PR needs that isolation at its own boundary.

Approval remains withheld. This review distinguishes the PR's own head from fixes verified later in the stack; it does not duplicate existing inline findings.

@mldangelo-oai
mldangelo-oai force-pushed the mdangelo/codex/pr939-stack-17-scan-recovery branch from 7df192d to 52c7a9b Compare October 2, 2026 00:36
@mldangelo-oai
mldangelo-oai force-pushed the mdangelo/codex/pr939-stack-18-native-preparation branch from fdef28b to 32f2372 Compare October 2, 2026 00:36
@mldangelo-oai
mldangelo-oai force-pushed the mdangelo/codex/pr939-stack-18-native-preparation branch from 0e27989 to bd8e9b8 Compare October 4, 2026 12:33
@mldangelo-oai
mldangelo-oai force-pushed the mdangelo/codex/pr939-stack-17-scan-recovery branch from ab0e7f3 to 515ae24 Compare October 4, 2026 12:33

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bd8e9b8847

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

);
},
);
if (!overridden) environmentHeaders[name] = transfer(value);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Replace stale case-insensitive header mappings

When http_headers and env_http_headers contain the same HTTP header with different casing (for example, Authorization and authorization) and the mapped environment value is missing, blank, or invalid, this adds the literal fallback under the new spelling but leaves the stale mapping intact. The child therefore receives two case-insensitively equivalent headers; an invalid stale value can still make Codex reject the provider configuration, and otherwise precedence is ambiguous instead of reliably preserving the requested literal fallback. Remove every case-insensitive match before inserting the generated mapping.

AGENTS.md reference: sdk/typescript/AGENTS.md:L21-L24

Useful? React with 👍 / 👎.

Comment on lines +494 to +495
codexHome,
preserveCodexHomeConfig: true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep scoped-target metadata in the isolated workspace

For a path-scoped native scan, returning the caller's real Codex home here makes the existing allocation in api.ts derive targetPathsFile from dirname(runtime.codexHome). With the default home this writes ~/codex-security-target-paths-*.json instead of using the isolated bootstrap workspace, and a valid custom CODEX_HOME whose parent is not writable makes the scan fail at startup; an interrupted process can also leave the authorized scope metadata behind. Place this helper file under bootstrapWorkspace or another validated per-scan location.

AGENTS.md reference: sdk/typescript/AGENTS.md:L21-L24

Useful? React with 👍 / 👎.

@alandelong-oai alandelong-oai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed head bd8e9b88470eb513a6b7b4c9612654eabfc09269 against base 515ae24cb58d6bd7db2eadd63754a8840228a5ef using three fresh independent HIGH full actual-diff reviews, including all changed tests and fixtures, followed by root synthesis. Original reports and their review times are preserved.

Two introduced test fixture defects block source approval, detailed inline: the authentication tests pre-create a nonprivate scan directory and fail before authentication; the Windows provider fixture registers an unnormalized executable while production spawns its extended-path form. The earlier missing-home ENOENT source issue is fixed by creating the native ambient home before realpath.

The five selected SDK files passed 46 tests with 1 skip locally. The native executable-selection test passed, while all eight authentication cases failed at the output-directory check. Hosted MCP ran 316 passes and 9 failures: those eight fixture failures plus a bwrap loopback startup error. The selected Windows shard had 682 passes, 12 skips and the provider fixture failure. A separate parallel run had 3,979 passes, 53 skips, nine 30-second timeouts and four unhandled errors; their cause remains unresolved. These counts belong to separate executions.

The standalone sandbox denial test failed during bwrap loopback configuration, so it does not establish a permission bypass. Case-variant provider-header mappings and native scoped metadata under the real home parent remain conditional compatibility concerns; supported runtime impact is unestablished. No all-configurations-safe or fix-alone-green claim follows.

Hosted CI at 2026-10-04T12:53:09.313276+00:00–2026-10-04T12:53:33.556119+00:00 was pending with failed jobs already present. Source approval, CI and successful integration are separate. This prefix contains main 1fb0e5f, but whole-stack successful integration remains unestablished.

No product model, clean installation or local Windows run was performed. Native binaries were retained after source/dependency/artifact identity checks; no fresh native compilation occurred. Individual full-diff reviews qualify selective unchanged long-file context.

try {
await mkdir(home, { mode: 0o700 });
await mkdir(target);
await mkdir(join(root, "scan"));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Create the authentication fixture output directory with0700

The fixture pre-creates scan using default mkdir permissions, but the real SDK requires existing output directories to be private. Under the normal022 umask all eight bearer/env-key cases fail with OutputDirectoryError before onAuthentication runs, both locally and in the hosted MCP job. Create this fixture directory with mode0700 while keeping the product permission check; otherwise these tests never exercise the authentication behavior they claim to cover.

`,
);
const spawn = spyOn(childProcess, "spawn").mockImplementation(
fixtureSpawn(executable, script, () => {}),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Match the normalized Windows executable in the spawn fixture

On Windows disabledMcpServers spawns the extended-length form of the synthetic executable, while fixtureSpawn is registered with the unnormalized path and only matches exact strings. It falls through to a real spawn of the nonexistent synthetic-codex.exe and the new provider test fails with ENOENT before credential assertions. Normalize the intercepted path with the same executablePathForSpawn helper used by execution-worker-policy.test.ts, preserving the real production executable behavior.

@mldangelo-oai
mldangelo-oai force-pushed the mdangelo/codex/pr939-stack-17-scan-recovery branch from 515ae24 to c14d94d Compare October 4, 2026 13:16
@mldangelo-oai
mldangelo-oai force-pushed the mdangelo/codex/pr939-stack-18-native-preparation branch from bd8e9b8 to 42c3a60 Compare October 4, 2026 13:19

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 42c3a6004b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +57 to +60
recipe.knowledgeBasePaths ??
(inheritedEnvironment.CODEX_SECURITY_KNOWLEDGE_BASE
? [inheritedEnvironment.CODEX_SECURITY_KNOWLEDGE_BASE]
: undefined),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve empty saved knowledge-base selections

When resuming a scan whose saved recipe omits knowledgeBasePaths because the original scan had no knowledge base, this fallback imports the current CODEX_SECURITY_KNOWLEDGE_BASE value. The resumed discovery and reducer workers then receive a new authoritative input that was not part of the original scan, so changing the ambient environment between runs can change the scan's scope and findings. Distinguish fresh preparation from replay and treat an omitted saved value as an empty selection during resume.

AGENTS.md reference: AGENTS.md:L31-L35

Useful? React with 👍 / 👎.

Comment on lines +110 to +113
if (!isDeny) {
throw unsupportedParentSandbox(
"parent filesystem glob grants cannot be preserved",
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ignore parent glob grants instead of rejecting the scan

When a valid managed parent profile contains an absolute glob-shaped read or write grant alongside the required root-read entry, this branch rejects native Deep Scan preparation outright. Only denials are transported into the stricter child profile, and the same function already ignores ordinary path and special-path grants, so there is no grant-preservation requirement here; rejecting the known glob form merely prevents scans under otherwise compatible organization profiles. Ignore non-deny glob entries as well.

AGENTS.md reference: AGENTS.md:L43-L47

Useful? React with 👍 / 👎.

@alandelong-oai alandelong-oai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 42c3a6004be5cbdb91ae0fbe18c9533aba7d1f14 against base c14d94d15e2d4d73726692f92558bcbbe29e11e5. Three fresh independent HIGH full actual-diff passes, including every changed test/fixture, plus root synthesis.

The two prior fixture findings are fixed in source: auth output directories now use 0700, and the provider fixture normalizes executable paths. The policy fixture now normalizes its path too; the Linux deny-glob test explicitly enables networking for the filesystem-denial assertion. Locally, five selected SDK tests and eight auth cases passed on macOS; nine portable checks, SDK build, types, format and source compatibility passed. The first local host bundle attempt failed because my setup omitted retained generated native JavaScript; after restoring the verified prior payload it passed. No test was rerun before that correction. This is not Windows/Linux success or an explanation of historical timeouts and cancellation. Two new P2 source findings below remain. The old fresh-home ENOENT source fix remains intact.

Hosted CI is recorded separately in the final review evidence; it is not inferred from these local or retained checks. Source approval, hosted CI and whole-stack/current-main integration remain separate. No product model, local Windows run, clean install, fresh native compilation or deployment validation was performed.

Remaining source findings:

  • P2: a saved recipe with no knowledge-base selection imports the current ambient knowledge base.
  • P2: a compatible managed profile with root read and a redundant absolute glob grant is rejected.

Hosted CI observation 2026-10-04T13:37:39.993325+00:00 to 2026-10-04T13:38:02.237036+00:00: this exact head was pending. No failed jobs were reported for this head in that observation. Later source/metadata readbacks are not CI refreshes.

auth: recipe.auth,
cyberAccessProgram: recipe.cyberAccessProgram,
knowledgeBasePaths:
recipe.knowledgeBasePaths ??

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve the saved empty knowledge-base selection

A saved recipe for a scan with no knowledge base omits knowledgeBasePaths: the touched API producer passes knowledgeBase?.sources and scanRecipe omits undefined values. Replaying that recipe after CODEX_SECURITY_KNOWLEDGE_BASE changes takes this fallback and prepares a new knowledge input that was absent from the saved scan. Distinguish fresh preparation from saved replay, and treat an omitted saved selection as empty. This finding establishes changed prepared replay input; it does not claim an observed model result or deployment effect.

"a parent filesystem permission has an invalid glob",
);
}
if (!isDeny) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Ignore redundant absolute glob grants

A managed restricted parent with the required root-read entry plus an absolute glob_pattern read grant is rejected here. That grant adds no restriction beyond root read, and this function already ignores ordinary path and special-path grants while returning only denials for the stricter child profile. Validate the recognized glob shape and ignore compatible non-deny grants instead of rejecting native preparation. Keep the root-read requirement and validation of denial semantics. The finding is the local rejection of this supported metadata shape, not a claim about a particular deployed organization.

@mldangelo-oai
mldangelo-oai changed the base branch from mdangelo/codex/pr939-stack-17-scan-recovery to mdangelo/codex/pr939-stack-02-severity-history October 4, 2026 17:35
Consolidate approved Deep Scan changes into the parent topic branch.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8e9a059fbb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +298 to +302
preparation = await workbench([
"prepare-scan-completion",
"--scan-id",
scanId,
]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Forward the native claim token to completion commands

For a scan supplied through registeredScan with a non-null handoff claim, this call and the subsequent complete-scan call omit --claim-token; complete_scan_locked requires the matching claim and rejects either request. Consequently, once the claimed registration path is used, the native scan can finish its model work but cannot prepare or seal its results; carry registeredScan.handoffClaimToken through the publication context and all other mutating workbench calls.

AGENTS.md reference: AGENTS.md:L31-L35

Useful? React with 👍 / 👎.

Comment on lines +59 to +63
JSON.stringify({
recipe,
userContext: scanOptions.scanPrompt,
...(scanOptions.workflowId === undefined
? {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Mark registered child scans as Deep Scan passes

runPass sets deepScanPass: true, but registerScan neither picks that option nor emits the corresponding parentScanRole: "deep_pass" in this registration object. The workbench therefore stores the child with a null role, and the first refreshPasses call rejects it in savedPassIndex as “not an assigned Deep Scan pass,” so a production Deep Scan aborts after its first child batch. Forward the role during fresh child registration.

Useful? React with 👍 / 👎.

Comment thread sdk/typescript/src/api.ts Outdated
snapshotDigest,
targetRevision,
scopeFileCount,
} = await registerScan({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Invoke the Deep Scan registration callback

After this registration returns, options.onRegisteredScan is never invoked anywhere in the production client, although runPass relies on it to save the child's scan ID. If a child throws after registration, pass.scanId remains undefined and the retry starts a fresh scan in the same populated pass directory instead of setting resumeScanId; the composition tests mask this by explicitly invoking the callback in their fake client. Invoke the callback immediately after successful registration so in-flight and resumed workers retain their identity.

AGENTS.md reference: AGENTS.md:L31-L35

Useful? React with 👍 / 👎.

Comment on lines +65 to +68
let active = this.active.get(input.scan.scanId);
if (!active) {
const controller = new AbortController();
const promise = Promise.resolve()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Acquire the cross-process lock before starting a native scan

This active map deduplicates callers only within one NativeScanHost; a second MCP host or process holding the same scan claim can simultaneously prepare and run the same scan directory. The new acquireScanExecution helper is otherwise unused, so nothing prevents both processes from mutating the same checkpoints and canonical artifacts. Acquire that lock for input.scan.scanDir before preparation and retain it until client cleanup completes.

AGENTS.md reference: sdk/typescript/AGENTS.md:L21-L24

Useful? React with 👍 / 👎.

Consolidate Deep Scan changes and their reviewed follow-up fixes into the parent topic branch.
@mldangelo-oai mldangelo-oai changed the title refactor(plugin): prepare scans with the caller's Codex settings refactor(deep-scan)!: unify execution and durable result recovery Oct 4, 2026
Preserve the child source tree while recording its consolidated parent.
@mldangelo-oai
mldangelo-oai merged commit 885bf9b into mdangelo/codex/pr939-stack-02-severity-history Oct 4, 2026
3 of 16 checks passed
@mldangelo-oai
mldangelo-oai deleted the mdangelo/codex/pr939-stack-18-native-preparation branch October 4, 2026 23:53

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: f3f7540584

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Comment on lines +146 to +147
const source = expandHome(path, inheritedEnvironment);
return (await gitMarkerRoot(source, signal, "outermost")) ?? source;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ Codex Security Review · Automatically triggered

P1 Badge Security: Protect standalone knowledge-file directories from PATH lookup

When an operator selects a non-Git knowledge-base file whose attacker-controlled parent is on PATH, protect that parent before resolving Codex. The final root-expansion change still falls back to source itself for a standalone file, so a sibling codex is outside the protected file path and is accepted and executed with the user's authority. Fresh evidence is this non-Git-file fallback; the added regression covers only a knowledge directory beneath .git. Use the file's canonical parent as the fallback root.

SECURITY.md reference: SECURITY.md:L11-L14


Dismiss this finding: Reply with @codex security dismiss <reason> [context]. Codex will resolve this conversation automatically; GitHub may require a page refresh to show the result.

Valid reasons: false-positive, duplicate, out-of-scope, compensating-control, risk-accepted, or other. Example: @codex security dismiss duplicate Already flagged by another review

What each reason means
  • false-positive — Not a vulnerability
  • duplicate — Already tracked elsewhere
  • out-of-scope — Outside this review's scope
  • compensating-control — Mitigated by another control
  • risk-accepted — Risk intentionally accepted
  • other — Another reason; context required

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

metadata = {
threadId: (scan["continuationThreadId"] as string | null) ?? null,
turnResult: {
status: "completed",
usage: usage ?? (cost === null ? null : scanCostUsage(cost)),

P2 Badge Restore the model in recovered workflow metadata

When complete-scan succeeds but writing the FindingWorkflow stage receipt fails, the next invocation enters this completed-scan recovery branch and synthesizes new metadata. It restores the thread, usage, and cost but omits the model even when it is available from the saved cost or scan record, so the returned ScanResult.turnResult.model differs from the original result and that degraded metadata is then persisted by workflow.complete. Include the saved model when reconstructing the turn result.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +64 to +66
environment.CODEX_HOME = await fs.realpath(
expandHome(codexHome.trim(), environment),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Create a configured Codex home before canonicalizing it

When CODEX_HOME explicitly names a directory that has not been created yet, this realpath throws ENOENT before prepareAmbientRuntime reaches its existing mkdir(requestedHome, { recursive: true }), so a valid fresh API-key setup cannot start a native scan. The default-home case from the earlier review is now handled, but the explicit configured-home path still fails; normalize the value here without requiring existence, or create it before canonicalizing it.

AGENTS.md reference: AGENTS.md:L11-L12

Useful? React with 👍 / 👎.

if child["seal_manifest_digest"] is not None or (
isinstance(manifest_scan, dict)
and (
manifest_scan.get("sealedAt") is not None or manifest_scan.get("artifacts") is not None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep empty artifact drafts on the recovery path

When an interrupted child has written a valid unsealed manifest with scan.artifacts: [], this predicate classifies it as sealed. _prepare_scan_finalization itself removes an empty list before deciding seal status, and the resume path likewise treats it as unsealed; here, stopped Deep Scan recovery skips merge_saved_results, where pending checkpoints and candidates are retained, and instead attempts sealed finalization, which can fail or omit the child's unfinished evidence. Treat only a nonempty artifact list as sealed here and in the equivalent recovery predicates.

AGENTS.md reference: AGENTS.md:L11-L12

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants