Conversation
When pull requests are opened from forks, the default pull_request trigger provides a read-only GITHUB_TOKEN. This causes the reusable design enforcement workflow to fail with a 403 error when attempting to update labels on the PR. Switching the trigger to pull_request_target runs the workflow in the context of the target repository with the declared write permissions, allowing label enforcement to succeed on fork PRs. The workflow does not checkout untrusted PR code or execute user scripts, making pull_request_target safe. Fixes projectbluefin#346 Signed-off-by: Danathar <Danathar@users.noreply.github.com>
hanthor
left a comment
There was a problem hiding this comment.
The premise checks out — here is the actual 403 from this repo
This is the kind of change that deserves proof rather than reasoning-by-analogy, so I went and found the failure it fixes. It is on #334, a fork PR from tunix:
Job 101876916497, enforce / Enforce workflow labels, conclusion failure:
Contents: read
Issues: read
Metadata: read
PullRequests: read
...
Error: PUT /repos/projectbluefin/finpilot/issues/334/labels: 403
{"message":"Resource not accessible by integration", ... "status":"403"}
Note the permissions block the runner actually granted: Issues: read, PullRequests: read. label-enforcement.yml declares issues: write and pull-requests: write, but on a pull_request event from a fork GitHub downgrades the token to read-only regardless of what the workflow asks for. The action then 403s on the label write. That is exactly the failure mode this PR fixes, and pull_request_target is the documented remedy.
The security question, answered from evidence
pull_request_target runs with a writable token in the base repo's context, so the standard objection is "does this now execute untrusted fork code with write credentials?" Here, no — and the job log proves it. The full step sequence in that run is:
Prepare workflow directory
Prepare all required actions
Download action repository 'projectbluefin/actions@2295d66a...'
Uses: projectbluefin/actions/.github/workflows/reusable-design-enforcement.yml@ce7ab75e...
Run projectbluefin/actions/.github/actions/design-enforcement@2295d66a...
There is no actions/checkout step at all. The job never materialises the PR head on disk; it only reads issue/PR metadata through the API and writes labels. No fork-authored code executes. That is the condition under which pull_request_target is safe, and it holds.
One thing to confirm before merging
The safety argument above rests entirely on the pinned reusable reusable-design-enforcement.yml@ce7ab75eb66d3ace300fed4c121a31b417e8f9fe never checking out github.event.pull_request.head.sha. I could not read projectbluefin/actions from this session to audit the file directly — I inferred it from the executed step list, which is strong but is one run. Someone with access should eyeball that pin and confirm, because if a future bump to that reusable ever adds a checkout, this change silently converts into an arbitrary-code-execution path. Worth a comment in the workflow recording that constraint so the next Renovate bump gets reviewed with it in mind.
Two operational notes
-
This PR cannot demonstrate itself.
pull_request_targetuses the workflow definition from the base branch, so the fix only takes effect once it is onmain. Do not wait for a green label-enforcement check on this PR as evidence. -
No CI has run here either.
get_check_runsfor #351 returnstotal_count: 0; theUnit Testsrun for1d68c7fsits atconclusion: action_required, pending maintainer approval of fork workflows. Slightly ironic given the subject matter, but it means nothing has been verified by the repo's own gates.
For completeness, the branch merged into origin/main @ 1db684b is clean: bats tests/unit 180/180, just lint exit 0, just check exit 0 — identical to the baseline on unmodified main. The diff is a single line and touches no shell.
Generated by Claude Code
hanthor's review on projectbluefin#351 confirmed this workflow is safe under pull_request_target because the job never checks out the fork's head -- it only reads metadata via the API and writes labels -- but noted the safety argument rests entirely on the pinned reusable workflow never adding a checkout, and asked for that constraint to be recorded so a future version bump gets reviewed with it in mind. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LNgJ8VMdoqzGequbxsP2Kc
|
@hanthor — pushed in |
When pull requests are submitted from forks, the
pull_requestevent provides a read-onlyGITHUB_TOKEN. As a result, the reusable label-enforcement workflow fails with403 Resource not accessible by integrationwhen attempting to set labels on fork PRs (such as seen on #334).Switching the event trigger in
.github/workflows/label-enforcement.ymlfrompull_requesttopull_request_targetensures the workflow runs in the context of the base repository with the declared write permissions forissuesandpull-requests. Because the workflow only inspects metadata and updates labels via the GitHub API without checking out PR code or executing untrusted user scripts, usingpull_request_targetis safe.Closes #346, closes #347, closes #348
— hive: backend=agy