Repository navigation
fix(lanes): scan every lane and eval upload for credentials before it leaves - #11806
Conversation
… leaves Add .github/actions/scan-upload, which runs lib/publish-gate.mjs scanLeak (the read runner's leak-scan rule set) over every file and file name an upload is about to send. A credential, hidden assignment, link, compressed file or glob fails the step and withholds the upload: each gated upload's if: requires the scan's clean output, so an always() upload is withheld too. The lane transcript action scans the plaintext execution file before the seal step. Every lane and eval upload not already behind leak-scan now runs it. .github/artifact-retention.json records a required scan field per family (upload, plaintext or none), and check-artifact-retention fails a scanned upload with no scan over the same path before it. lane-sweep-units and eval-browser-* drop to 3 days. Closes #8295 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Four upload conditions written as ${{ }} became always true once the scan
gate was appended outside the braces; unwrap them so the scan's clean output
gates those uploads. check-artifact-retention now treats an if: with text
around ${{ }} as gating nothing, and its test covers that case. The callers
contract test admits the verdict's pre-upload scan in the read runner's scan
job (default-branch .base, no secret) and expects the gated upload
conditions.
Refs: #8295
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…out replaces .base The read runner's scan job and the write runner's run job check out the head at the workspace root, which replaces .base, so a scan step loading ./.base/.github/actions/scan-upload there ran head-supplied code. Both jobs now stage scan-upload.mjs and lib/publish-gate.mjs into RUNNER_TEMP before the head checkout, on every run: the read runner from the default-branch tip through the API, as leak-scan loads the gate, and the write runner from its default-branch .base. Their scans run the staged copy with node. check-artifact-retention now counts a scan only as a top-level && conjunct with no top-level ||, and requires the upload's condition to be the scan step's own if:, parenthesized, then the clean output. The callers contract test asserts that no job loads an action from ./.base/ after a checkout that replaced it, and that the two jobs run the staged scanner. Refs: #8295 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-every-lane-upload
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 39369502 | Triggered | Generic Password | 53326e3 | .github/actions/scan-upload/scan-upload.test.mjs | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secret safely. Learn here the best practices.
- Revoke and rotate this secret.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
claude-security-review reviewed Security review: PR #11806, scan every lane and eval upload for credentialsI read all 32 changed files in Trust controls checked (nothing widened):
Instruction surface: no standing instruction, hook allowlist or skill body is deleted or softened. No plugin manifest changed. Inline finding (SUGGESTION): a credential hit now throws away the lane transcript. That transcript was already encrypted, and it is the forensic record of the run, so losing it costs more than the scan protects. Details are on the inline comment. Spend: the extra scan steps add a few seconds of Actions time per upload on standard runners. Two retention periods go from 7 to 3 days, which lowers storage. Neither is a hold. critical-findings: 0 |
|
claude-review reviewed Code review: PR #11806, scan every lane and eval upload for credentials before it leavesI read every hunk of all 32 changed files in What I checked
Closing issue #8295: criteria against the change
Findings outside any changed line
Spend: the read runner adds two Embedded text: none found in the PR context. |
plan-test-lanes refused the new suite because no lane ran it. Give .github/actions/scan-upload a package.json with `node --test` and list it in scripts/outside-node-packages.txt, as the other action suites are. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GitGuardian flagged the literal password assignment in the warn-only fixture. Join the key at run time so no tracked line holds the shape; the file the test writes is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rerun-ci-status job's .base checkout is continue-on-error, so a failed checkout made the record's scan step fail and skipped `Re-run ci-status`. The scan step is now continue-on-error too; the record upload still needs its clean output, so a hit or a missing scanner withholds the record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dispatched, re-run and alerted records hold only run ids and an attempt number the workflow writes. Scanning them added a step that fails when dispatch-full-run's continue-on-error .base checkout fails, which skipped `Record the dispatched run`. Mark the three families scan: none with that reason, drop their scan steps, and drop the scanner from dispatch-full-run's sparse checkout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… hit In merge and post-merge-dispatch, the uploads the new scans gate are continue-on-error, but the scans were not: a hit, such as a token-shaped branch name in head-refs.json, failed merge and skipped dispatch-full-run. Each scan before a continue-on-error upload is now continue-on-error too; the upload still needs the clean output, so a hit still withholds the file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pload # Conflicts: # .github/actions/resolve-config/callers.test.mjs
|
The pr-unblock lane stopped
When a retry on this head can change the outcome, the lane runs one on its own, up to its per-head cap, and edits this comment if it stops again; a run that does not stop clears the status. When no retry can (a review thread the lane does not take or settle, or a failing check after the retries are spent), a person resolves the threads or gets the checks green, then dispatches the workflow below: that run finds the cause gone and clears the status. On a pull request the lanes App opened, the dispatchers start that run once per stop; on any other, a person does. A new commit to the branch starts clean. To run it again on this head now, dispatch the gh workflow run pr-unblock.yml -R melodic-software/claude-code-plugins -f pr-number=11806 -f activity=resolve-conflicts |
|
The pr-unblock lane stopped
When a retry on this head can change the outcome, the lane runs one on its own, up to its per-head cap, and edits this comment if it stops again; a run that does not stop clears the status. When no retry can (a review thread the lane does not take or settle, or a failing check after the retries are spent), a person resolves the threads or gets the checks green, then dispatches the workflow below: that run finds the cause gone and clears the status. On a pull request the lanes App opened, the dispatchers start that run once per stop; on any other, a person does. A new commit to the branch starts clean. To run it again on this head now, dispatch the gh workflow run pr-unblock.yml -R melodic-software/claude-code-plugins -f pr-number=11806 -f activity=address-feedback |
|
The pr-unblock lane stopped
When a retry on this head can change the outcome, the lane runs one on its own, up to its per-head cap, and edits this comment if it stops again; a run that does not stop clears the status. When no retry can (a review thread the lane does not take or settle, or a failing check after the retries are spent), a person resolves the threads or gets the checks green, then dispatches the workflow below: that run finds the cause gone and clears the status. On a pull request the lanes App opened, the dispatchers start that run once per stop; on any other, a person does. A new commit to the branch starts clean. To run it again on this head now, dispatch the gh workflow run pr-unblock.yml -R melodic-software/claude-code-plugins -f pr-number=11806 -f activity=fix-ci |
…the scan steps The #8295 decision applies the pre-upload scan to the pr-merge-dispatched and pr-merge-rerun records, so restore the scan steps c1afd2b dropped for the dispatched, re-run and alerted records, with their rows back at scan: upload. Each restored scan is continue-on-error, as the ci-status re-run's is: a failed .base checkout no longer skips the record step, and the upload, which needs the clean output, is withheld, failing closed. The reuse-summary scan in pr-run-activity-read's summary job and the two enable-auto-merge scans before its continue-on-error uploads are continue-on-error too, so a scan hit withholds a best-effort upload without failing the job. Mark each scan-upload uses line with main's zizmor self-repository ignore, and give the lane-sweep handoff scans their own id so the three judge legs' findings steps stay identical. Update the workflow contract tests to the scan step: each asserts the scan before its upload, over the same path, and the upload's gate on the scan's clean output. Refs: #8295 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeBVgcHpe4YpFuL5b24Ft2
The dispatched, re-run and alerted records gate the post-merge check, and their uploads were never continue-on-error. With continue-on-error on their scans, a failed .base checkout skipped the record and left the job green. Drop it, so a missing scanner fails the job as a failed upload of the record does, and say so beside dispatch-full-run's checkout. Refs: #8295 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeBVgcHpe4YpFuL5b24Ft2
|
The pr-verify verifier failed head What it wrote (40 lines)This pull request leaves the merge queue, and ci-status holds it out while it carries |
callers.test.mjs: both sides appended tests at the same spot; kept both (the .base reload and upload-scanner staging tests, and main's sync-shared-copies select and not-run tests). Semantic conflict: main's new sync-shared-copies-select patch upload (family patch-pr-unblock-sync-shared-copies-*) had no scan class and no scan step, which this branch's retention schema requires. Classed it scan: upload and gated its upload on a scan-upload step over the patch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Post-merge mutation testingKilled 5, survived 2, timeout 0, no coverage 0, invalid 0, not run 0.
Surviving mutants
The run's summary has the full report. |
Refs: #8295
Summary
Lane and eval uploads are public artifacts on this public repository. Before this change, only uploads behind the read runner's leak-scan were checked for credentials, so a token in a lane transcript, review handoff or eval result could be published. Every lane and eval upload now runs
lib/publish-gate.mjsscanLeakover each file and file name first, and the upload is withheld on a hit. The ci-workflows half (review handoff and shadow report uploads) is melodic-software/ci-workflows#945.Fix
.github/actions/scan-upload/(scan-upload.mjs) runsscanLeakover every file and file name an upload is about to send. A credential, hidden assignment, link, compressed file or glob fails the step without printing the value.if:requires the scan'scleanoutput, so analways()upload is withheld too. Four${{ }}-form upload conditions that the appended gate made always true are unwrapped..base(the read runner's scan job, the write runner's run job) stagescan-upload.mjsandlib/publish-gate.mjsintoRUNNER_TEMPfrom the default branch before that checkout and run the staged copy, so no head-supplied code runs the scan..github/artifact-retention.jsonrecords a requiredscanfield per family (upload,plaintextornone).check-artifact-retentionfails a scanned upload with no scan over the same path before it, counts a scan only as a top-level&&conjunct that is the scan step's ownif:, and treats anif:with text around${{ }}as gating nothing.sync-shared-copies-selectpatch upload that main added (patch-pr-unblock-sync-shared-copies-*) is classedscan: uploadand gated on a scan of the patch.lane-sweep-unitsandeval-browser-*drop from 7 to 3 days.Verification
scan-upload.test.mjs,callers.test.mjs(resolve-config) andcheck-artifact-retention.test.shpass. The callers contract test asserts that no job loads an action from./.base/after a checkout that replaced it, and that the two staged jobs run the staged scanner../.baseload after a root head checkout (P1, P2), no.baseon skip (P3) and theincludes()gate (P4); all are fixed in eab9ef5 and a26ff79. Round 2 passed with no HIGH or CRITICAL finding.scanLeakover gitleaks for upload bytes): gitleaks runs inscan-mode: giton commit history, whilescanLeakreads upload bytes and file names, including split, UTF-16LE and escaped tokens, with onlynode:builtins.Remaining on #8295
pr-review-cli.yml@main, so no pin here moves for it; what remains is confirming on a review lane run here that the scan step runs before the handoff and shadow report uploads.scanLeakagainst gitleaks with this repository's.gitleaks.toml, and against Betterleaks, over the same upload corpus.Related
@main, so no pin moves.🤖 Generated with Claude Code