Skip to content
Merged
Changes from all commits
Commits
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
30 changes: 29 additions & 1 deletion .github/workflows/flamingo-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@
# writable scopes" — the corpus 304 path silently never worked. The path
# is reachable only from a corpus-only fetch (see the cache step), so this
# scope buys a rehearsal-lane saving and nothing on a production trigger.
# NECESSARY, NOT SUFFICIENT: an untrusted trigger (issue_comment) gets a
# read-only cache token whatever this block grants, which is why the save
# step below runs on workflow_dispatch only.
actions: write

jobs:
Expand Down Expand Up @@ -655,9 +658,17 @@
# spares no transfer on a production trigger. Kept for the rehearsal
# lane; the reason is stated here so nobody expects a saving it never
# made.
# RESTORE and SAVE are separate steps, not the combined actions/cache
# whose post-step saves at job end: since GitHub's 2026-06-26 change an
# UNTRUSTED trigger on the default-branch scope (issue_comment here —
# anyone who can comment can fire it) gets a READ-ONLY cache token, so
# that post-step ended every commanded review with "Failed to save …
# cache write denied: token has no writable scopes" (run 34519652342),
# a warning no permissions block can lift. The save is done only where
# it can succeed AND serves the one lane that reads it — see below.
# v5 = the Node 24 drop-in (v4 targets EOL Node 20 and warns on every run).
- name: Restore the last corpus hash
uses: actions/cache@v5
uses: actions/cache/restore@v5
with:
# BOTH files. Caching only the hash meant a 304 left rules.json
# truncated to zero bytes while the run continued as if it had a
Expand Down Expand Up @@ -828,21 +839,38 @@
REVIEW_FULL: ${{ needs.resolve_command.outputs.full }}
run: /tmp/code-review-fetch-rules.sh

# The save half of the corpus cache (see the restore step for why the
# two are split). workflow_dispatch ONLY: it is the sole trigger that is
# both trusted (keeps a read-write cache token on the default-branch
# scope) and served by the 304 path the cache exists for. A pull_request
# run could write, but into its own branch scope, which the rehearsal
# lane never reads. Guarded on the files existing so a fetch that
# skipped (corpus unavailable, review disabled) does not fail the job on
# a missing path.
- name: Save the corpus hash for the next rehearsal
if: github.event_name == 'workflow_dispatch' && hashFiles('.rules-hash', 'rules.json') != ''
uses: actions/cache/save@v5
with:
path: |
.rules-hash
rules.json
key: flamingo-rules-hash-${{ github.repository }}-${{ github.run_id }}

# Stage checkpoints are their OWN credentialed steps. The review step
# holds the shared webhook secret — unavoidably, since the reviewer's
# Claude calls go through the hub's secret-gated proxy rather than
# straight to Anthropic — but NOT the run token, which is the credential
# that can close out a run row. So a checkpoint ping happens at a step
# boundary, not inside the step that reads untrusted PR content.
# Say so ON THE PULL REQUEST before spending minutes in the model. Placed
# AFTER the rules step on purpose: the skip decision is known here, so a run that
# reviews nothing never leaves a placeholder to strand. The comment
# carries the summary marker, so the final "Post to the pull request"
# step UPDATES this exact comment into the verdict rather than adding a
# second one, and it is re-created (not edited) each run so the live
# review sits at the BOTTOM of the timeline instead of scrolling away
# under later discussion. Best-effort: never fails the review.
- name: Say the review is running

Check failure

Code scanning / CodeQL

Cache Poisoning via caching of untrusted files High

Potential cache poisoning in the context of the default branch due to privilege checkout of untrusted code. (
workflow_dispatch
).
if: (github.event_name == 'pull_request' || needs.resolve_command.outputs.pr_number != '') && steps.rules.outputs.skip != 'true'
continue-on-error: true
env:
Expand Down
Loading