openai / openai/codex-security
multiscan cleanup failure masks the scan outcome and skips the repository receipt
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 10.8k
- Forks
- 801
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 257
Description
Summary
The per-repository worker in runMultiscan awaits checkout removal in a
finally block before it appends the attempt receipt:
} catch (error) {
if (options.signal?.aborted === true) options.signal.throwIfAborted();
failure = redactedErrorMessage(error);
} finally {
await rm(checkout, { recursive: true, force: true });
}
const status = failure === undefined ? "completed" : "failed";
await appendReceipt(...);
If rm rejects, its filesystem error replaces the outcome already captured by
the worker and execution never reaches appendReceipt. force: true ignores a
missing path, but it does not suppress EACCES, EPERM, or EBUSY.
This is still present on main at
a8fc00984b07f10f5b607a9521c2f05aa57c5107:
Environment
- Windows
- Node.js 24.18.0
- pnpm 11.9.0
- Bun 1.3.13
Reproduction
No API request is required. The reproduction creates a one-commit local Git
repository and a one-row multiscan inventory. The injected security client
denies delete access to its checkout with icacls, then throws
ORIGINAL_SCAN_FAILURE.
The complete deterministic harness is available as
repro-multiscan-cleanup.ts in the accompanying review evidence. Its relevant
injection is:
createSecurity: () => ({
async run(repository: string) {
const acl = spawnSync(
"icacls",
[repository, "/deny", `${account}:(D,DC)`],
{ encoding: "utf8", windowsHide: true },
);
if (acl.status !== 0) throw new Error(`icacls failed: ${acl.stderr}`);
throw new Error("ORIGINAL_SCAN_FAILURE");
},
async close() {},
}),
Observed result:
{
"surfaced": "Error: EACCES: permission denied, rm '<checkout>'",
"preservedOriginalFailure": false,
"receiptWritten": false
}
The harness restores the ACL and removes its temporary fixture in a finally
block. It exits successfully only when the cleanup error surfaces, the original
failure is absent, and no receipt contains that failure.
Expected behavior
Checkout cleanup should not replace the scan outcome. Every attempted
repository should receive a durable receipt before a secondary cleanup failure
can terminate or warn.
Actual behavior
The caller receives the checkout rm error, the original scan failure is lost,
and results.jsonl contains no receipt for the attempt. The same ordering also
discards a successful scan result if cleanup fails.
Impact
- The real scan failure is unavailable to the caller.
- A successful result can be changed into a campaign failure.
- The durable campaign ledger no longer records every attempt.
- Resume and operational diagnosis cannot distinguish an unattempted repository
from one whose cleanup failed after scanning.
Duplication check
- #38 and #127 cover the analogous single-scan cleanup path in
api.ts; the
fix now onmaindoes not changemultiscan.ts. - #98 and #196 change multiscan lock publication and recovery, not the worker
checkoutfinallyor receipt ordering. - #30 concerns resume accepting existing corrupted artifacts, not a missing
receipt caused by checkout cleanup.
Searches across open, closed, and merged issue/PR states did not find the same
multiscan behavior.
Suggested direction
Capture the attempt outcome first and treat checkout removal as best-effort
cleanup. Persist the receipt before a cleanup error can terminate the worker,
and report cleanup failure as secondary information. Regression coverage should
include both:
- scan failure plus checkout cleanup failure;
- scan success plus checkout cleanup failure.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read sdk/typescript/src/multiscan.ts around the runMultiscan worker and its checkout cleanup and receipt ordering. Run the repro-multiscan-cleanup.ts harness, then add regression coverage for both scan failure and scan success when cleanup fails. Done means the scan outcome is preserved, every attempt gets a receipt, and cleanup errors remain secondary.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- nodejs, typescript
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100