Skip to content

fix(ci): advisory reviewer could never comment on a fork PR (403) - #11

Merged
bkd-dotcom merged 1 commit into
mainfrom
fix/reviewer-fork-pr-comment
Aug 18, 2026
Merged

fix(ci): advisory reviewer could never comment on a fork PR (403)#11
bkd-dotcom merged 1 commit into
mainfrom
fix/reviewer-fork-pr-comment

Conversation

@bkd-dotcom

Copy link
Copy Markdown
Member

Rolls out Signetry/core#92, which was verified end to end in core before propagating here.

The bug

reviewer.yml runs on pull_request and requested pull-requests: write. On a PR from a fork, GitHub downgrades GITHUB_TOKEN to read-only regardless of the permissions: block — so the write grant was silently dropped and issues.createComment returned:

Resource not accessible by integration  (403)

Every outside contribution showed a red review check and never received the advisory comment it was supposed to get.

The fix

The standard trusted/untrusted split:

trigger token runs PR code?
reviewer.yml pull_request no write perms yes
reviewer-comment.yml workflow_run pull-requests: write never

The untrusted job stages comment.md + the PR number as an artifact; the trusted job downloads it and posts. Because the trusted job never checks out, builds, or executes PR code, holding a writable token there is safe.

Why not pull_request_target: it's the one-line fix and it's the wrong one — it grants a writable token in the base repo's context, and checking out PR head code under it is a well-known RCE footgun. Not a trade a change-control product should make.

Second bug, fixed here too

Found while testing the first: the advisory step's trailing exit 0 had never run. GitHub invokes run: steps as bash -e, so set -uo pipefail doesn't clear errexit and any non-zero exit from the reviewer (i.e. a Block verdict) aborted the step before reaching it. That contradicted the workflow's own contract — "never blocks the PR" — by turning the check red.

Hardening

  • PR number travels in the artifact (workflow_run.pull_requests is empty for forks) and is filtered to digits on both write and read.
  • Comment body is passed to the API as data, never through a shell or eval.
  • Body truncated at 65 000 chars (API limit 65 536).
  • The poster only updates a marker-bearing comment authored by a Bot, so a contributor can't get the bot to overwrite a human's comment by planting the marker in a PR description.

Also

Bumps the signetry-reviewer pin to v0.2.0, which carries the fix for CI rules firing on YAML comments — the reviewer had been blocking PRs for documenting a risk in a comment.

Verification

actionlint clean on both files. The chain itself was proven in core: the Reviewer comment workflow fired on workflow_run, succeeded, and posted as github-actions[bot]; a real fork PR's review check went red → green.

One note: workflow_run workflows always load from the default branch, so the chain only completes for PRs opened after this merges (an existing PR needs a rebase to pick up the artifact-upload half). Not a problem, just the reason the fix can't retroactively fix in-flight PRs.

Rolls out Signetry/core#92, verified there end to end before propagating.

reviewer.yml runs on `pull_request` and requested `pull-requests: write`.
On a PR from a fork GitHub downgrades GITHUB_TOKEN to read-only
regardless of the `permissions:` block, so the grant was silently dropped
and issues.createComment failed with "Resource not accessible by
integration" (403). Every outside contribution showed a red `review`
check and never got the advisory comment.

Split into the standard trusted/untrusted pair:
  * reviewer.yml (untrusted, runs PR code) now holds no write permission
    and uploads the rendered comment + PR number as an artifact.
  * reviewer-comment.yml (trusted) runs on `workflow_run` from the default
    branch, where the token is writable, and posts it. It never checks
    out, builds, or executes PR code.

Deliberately not `pull_request_target`: that grants a writable token in
the base repo's context, and checking out PR head code under it is an RCE
footgun.

Also fixes a second bug found while testing the first: the advisory step's
trailing `exit 0` had never run, because GitHub invokes `run:` steps as
`bash -e`, so any non-zero reviewer exit (a Block verdict) aborted the
step first and turned the check red — contradicting the workflow's own
"never blocks the PR" contract.

Bumps the signetry-reviewer pin to v0.2.0, which carries the fix for CI
rules firing on YAML comments (a workflow that documented a risk tripped
the rule meant to catch it).
@bkd-dotcom
bkd-dotcom merged commit 6c22fac into main Aug 18, 2026
2 checks passed
@bkd-dotcom
bkd-dotcom deleted the fix/reviewer-fork-pr-comment branch August 18, 2026 20:51
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.

1 participant