Skip to content

fix: greet fork pull requests with a token that can post the comment - #181

Merged
Maxerns merged 2 commits into
mainfrom
fix/greet-workflow
Sep 9, 2026
Merged

fix: greet fork pull requests with a token that can post the comment#181
Maxerns merged 2 commits into
mainfrom
fix/greet-workflow

Conversation

@Maxerns

@Maxerns Maxerns commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Why

The greeting workflow has never greeted anyone from a fork.

#179 and #180 were both opened from a fork and both failed the same way, on the call that posts the comment:

POST /repos/getsotto/sotto/issues/{n}/comments
{ message: 'Resource not accessible by integration', status: '403' }

The cause is not the permissions: block. That block already asks for exactly what the endpoint wants, and the 403 response says as much in x-accepted-github-permissions: issues=write; pull_requests=write. What the job was actually handed is in its own Set up job log, and it is not what the file asked for:

#179, #180 (fork) #178 (branch in this repo)
Issues read write
PullRequests read write
Secret source None Actions

Same workflow file, same permissions: block, same pull_request trigger. permissions: is a ceiling rather than a floor: a pull request from a fork gets a read-only token and no secrets under pull_request, so the write the job asks for is never granted and the greeting cannot be posted.

Only forks hit that, which is why nothing noticed since the workflow landed on 4 September. Every green run on pull_request came from a branch in this repo and logged Skipping...Not First Contribution, because the author was a maintainer. Of the four fork pull requests the workflow has seen, #176 and #177 stalled at action_required and never ran at all, and #179 and #180 ran and 403'd. Zero greetings posted, and the workflow reported success the whole time.

What it does

Moves the pull request trigger to pull_request_target, which runs the same job in the base repository's context and therefore with the write token the comment needs. The issues trigger, the permissions: block and both message bodies are untouched.

actions/first-interaction needs no change to suit it: it branches on payload.pull_request and posts to context.issue.number, and pull_request_target delivers the same pull_request payload with action: opened.

The decision worth arguing with

pull_request_target is the well-known footgun, so it deserves stating plainly rather than leaving in a comment. It runs with a write token and secret access in the base repository's context, and the way that gets exploited is a workflow that checks out and then executes code from the pull request.

This job does neither. It is a single step, there is no actions/checkout anywhere in the file, and under pull_request_target the workflow definition is read from main rather than from the pull request, so a fork cannot edit this file to change what runs.

The residual risk I would rather name than bury: actions/first-interaction@v3 is a mutable tag, and after this change it runs with a write token and secret access where before it got a read-only one. If that tag were ever moved to hostile code, the blast radius is larger than it was. Pinning shuts it:

- uses: actions/first-interaction@1c4688942c71f71d4f5502a26ea67c331730fa4d # v3

I have left it out to keep this the one-line fix it should be, and because nothing else in .github/workflows pins by SHA either: all 17 distinct uses: in this repo are tags. That makes it a repo-wide question rather than something to settle quietly here. Say the word and I will add it, on this pull request or across all the workflows.

What is tested and what is not

Nothing here is covered by a test, and there is no workflow linter in the repo to add one to. What I did check by hand:

  • the file still parses, and the added comment has not detached the on: mapping. The trigger set really is issues: [opened] plus pull_request_target: [opened].
  • the job is still one step with no checkout, which is the fact the section above rests on.
  • the action's source at v3 reads github.context.payload.pull_request and github.context.issue.number, both of which pull_request_target supplies.

What I cannot show is a greeting actually being posted. No run of this workflow has ever reached createComment successfully on any trigger, issues included, so the success path is unproven rather than repaired: this change removes a failure that was definitely happening, and does not prove the happy path. The next first-time contributor is the real test. If it fails again, read the GITHUB_TOKEN Permissions group at the top of the run log first, since that is what made the diagnosis obvious here.

I also expect, but have not confirmed, that this clears the action_required stall that caught #176 and #177, on the reasoning that pull_request_target runs base-branch code rather than fork code. The documentation I could find does not say so plainly, so please treat that as a hope and not a claim.

@Maxerns
Maxerns merged commit fc8d948 into main Sep 9, 2026
10 checks passed
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