Skip to content

fix(ci): use pull_request_target for label enforcement on fork PRs - #351

Open
Danathar wants to merge 2 commits into
projectbluefin:mainfrom
Danathar:fix/label-enforcement-pull-request-target
Open

Danathar wants to merge 2 commits into
projectbluefin:mainfrom
Danathar:fix/label-enforcement-pull-request-target

Conversation

@Danathar

@Danathar Danathar commented Sep 11, 2026

Copy link
Copy Markdown

When pull requests are submitted from forks, the pull_request event provides a read-only GITHUB_TOKEN. As a result, the reusable label-enforcement workflow fails with 403 Resource not accessible by integration when attempting to set labels on fork PRs (such as seen on #334).

Switching the event trigger in .github/workflows/label-enforcement.yml from pull_request to pull_request_target ensures the workflow runs in the context of the base repository with the declared write permissions for issues and pull-requests. Because the workflow only inspects metadata and updates labels via the GitHub API without checking out PR code or executing untrusted user scripts, using pull_request_target is safe.

Closes #346, closes #347, closes #348

— hive: backend=agy

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 hanthor left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  1. This PR cannot demonstrate itself. pull_request_target uses the workflow definition from the base branch, so the fix only takes effect once it is on main. Do not wait for a green label-enforcement check on this PR as evidence.

  2. No CI has run here either. get_check_runs for #351 returns total_count: 0; the Unit Tests run for 1d68c7f sits at conclusion: 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
@Danathar

Copy link
Copy Markdown
Author

@hanthor — pushed in b1056c4: added a comment above the triggers recording that the pull_request_target safety argument depends entirely on the pinned reusable workflow never adding a checkout of the PR head, so the next Renovate bump to that pin gets reviewed with that constraint in mind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants