Repository navigation
fix(pr-review-cli): pass the review between jobs as an artifact so secret masking cannot drop it - #874
Conversation
…cret masking cannot drop it The runner drops a job output whose value contains a registered secret's value anywhere in it. A review that quoted one reached the status job empty and published as incomplete. Each model job now writes the review and the last marker's summary to one file after the model step, uploads it as an artifact named for the lane, leg, run and head, and emits only the file's sha256. The status job downloads this run's files with no token and takes each leg's only when its sha256 matches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 finished @kyle-sexton's task in 4m 7s —— View job Security review complete
Scope: What changed: the review text and the previous marker's summary move from the model jobs to the status job via an Trust-boundary reasoning specific to this change (not zizmor's lane):
No instruction-surface deletions or plugin-manifest changes are present in this diff, so those criteria don't apply. No security issues found. critical-findings: 0 claude-lane-summary: lane=claude-security-review sha=c18f059c8a16b3774d452c4dcead08fef90ede7c run=40055922799 |
|
Claude finished @kyle-sexton's task in 6m 20s —— View job Code review —
|
|
claude-security-review has reviewed this pull request through c18f059; a later push is reviewed from there. |
|
claude-review has reviewed this pull request through c18f059; a later push is reviewed from there. |
…contract (#864) No related issue: follow-up to [ci-workflows#874](melodic-software/ci-workflows#874), which fixed a review-lane failure on [claude-code-plugins#9545](melodic-software/claude-code-plugins#9545). ## Summary Registers `pr-review-cli.yml@b49ac366bb3426039363a6927c41512e44533808` (ci-workflows [v0.65.1](https://github.com/melodic-software/ci-workflows/releases/tag/v0.65.1)), so claude-code-plugins' two review callers can re-pin to the release that passes the review between jobs as an artifact. ## Fix v0.65.1 changes only `pr-review-cli.yml`'s jobs and its tests: the review and the last marker's summary now travel as a checked artifact instead of a job output, which the runner dropped whenever the text contained a registered secret's value. The workflow's `on.workflow_call` inputs, secrets and runner did not change (`git diff d02b8697..b49ac366 -- .github/workflows/pr-review-cli.yml` touches the header comment and `jobs:` only), so the contract copies its v0.65.0 entry forward verbatim. ## Verification - `node --test runner-policy.test.mjs` in `components/runner-policy`: 343 pass, 0 fail. That includes the new copy-forward test, which checks the new entry deep-equals the v0.65.0 entry and that exactly one contract names the new SHA. - `npx --no-install @biomejs/biome check` (2.5.15) on both changed files: clean. ## Related - Same shape as [#856](#856) (v0.65.0) and [#853](#853) (v0.64.2). - Next: the standards sync into claude-code-plugins, then that repository's review-caller re-pin. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
No related issue: claude-code-plugins runs 40054659344 (security) and 40054659337 (code) on PR #9545 finished the model leg (8 and 9 turns), then the runner annotated the key job with "Skip output 'review' since it may contain secret." The status job got an empty
reviewand publishedfailure-class=incomplete: the model returned no usable review. Which secret matched is not verified, and no secret value was read.Summary
The runner drops a job output whose value contains a registered secret's value anywhere in it, so any review that quotes one fails this way. The review now moves from the model jobs to the status job as an artifact, and only the file's sha256 travels as a job output.
Fix
review-key,review-subscription, identical steps) gains two steps afterextract. A script step with no model writes{ review, last-summary }to${{ runner.temp }}/review-handoff/<job>.jsonand outputs its sha256.upload-artifact(the pinned v7.0.2) uploads it aspr-review-cli-handoff-<lane>-<job>-<run_id>-<head sha>, withoverwrite: trueandretention-days: 1. The name carries norun_attempt: re-running only the failed status job keeps the key job's attempt-1 outputs, so it has to find the attempt-1 file. The write step runsalways(), so a skip still carries its summary.reviewandlast-summaryleave the job outputs andhandoff-sha256joins them.last-summaryis the previous review's summary, read back from the marker, so it is model text as well.pr-review-cli-handoff-<lane>-*-<run_id>-<head sha>withmerge-multipleand nogithub-tokenorrun-id, so it reads this run only. A check step recomputes each leg's sha256. On a match it sets thekey-review,sub-reviewandlast-summarystep outputs. On a mismatch, or with no file, it sets nothing and warns, and publish falls into its existingincompletepath. Step outputs are not filtered for secrets; only job outputs are (sources below).publish.cjs,hold.cjs, the env names they read (KEY_REVIEW,SUB_REVIEW,LAST_SUMMARY), the marker, the review-complete notice, check names, the lane-record format, and the trust rules. The model still has no shell or write tool, and the file is written after the model step from the extract step's checked output.pr-filesstays a job output. It holds the head SHA,changed_files, each file name and a rename's old name, and the hunk ranges as numbers (writePrFiles, pr-review-cli.yml scope step). That is file names and numbers from the PR author, not model text. If a file name happened to contain a registered secret's value, the output would drop, and publish and the hold would fall back to their ownlistFilesread: a slower run, not a failed one.Sources
{output.Key}since it may contain secret."" (workflow syntax,jobs.<job_id>.outputs).!string.Equals(output.Value, HostContext.SecretMasker.MaskSecrets(output.Value))(actions/runner JobExtension.cs#L821-L825).MaskSecretsreplaces every occurrence inside the value, so a secret embedded in a larger text drops the whole output. Step outputs (SetOutputFileCommand) carry no such check (FileCommandManager.cs#L328-L350).github-token, download-artifact leavesfindByunset (download-artifact.ts at v8.0.2). The toolkit's internal client then calls the results service withACTIONS_RUNTIME_TOKEN(artifact-twirp-client.ts, config.ts). Upload always takes that path (CreateArtifact,FinalizeArtifactin upload-artifact.ts).API calls per run
pr-review-cli-api-calls.test.cjsstay at 8 / 6 / 10 / 8 / 11, now run through the handoff path.CreateArtifactandFinalizeArtifactplus one blob PUT. For the download, oneListArtifacts, then per file aListArtifactsand aGetSignedArtifactURLplus one blob GET. A key-only run adds 5 results-service calls and 2 blob transfers; a handoff run adds 9 and 4.Verification
incomplete.needs.*.outputs.revieworlast-summaryremains.nice:node --test .github/scripts/*.test.cjs647 pass, 0 fail;.github/actions/report-lane-outcome/*.test.cjs77 pass;.github/actions/run-claude-cli/*.test.cjs72 pass.actionlint(1.7.12) clean on the workflow.zizmor1.30.0 reports no findings (2 suppressed, as before).biome ci --error-on-warnings .github/scripts .github/actionsclean.Related
pr-review-hosted.ymlandpr-review-security-hosted.yml; a follow-up PR repins both after the release.🤖 Generated with Claude Code