Skip to content

Refuse fork PR checkouts in pull_request_target/workflow_run unless allow-unsafe-pr-checkout is set, matching upstream actions/checkout - #65

Merged
rcooney-sh merged 1 commit into
mainfrom
devin/1793072542-fork-pr-checkout-guard
Oct 28, 2026
Merged

rcooney-sh merged 1 commit into
mainfrom
devin/1793072542-fork-pr-checkout-guard

Conversation

@rcooney-sh

@rcooney-sh rcooney-sh commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Feature — useblacksmith/checkout now refuses to check out a fork pull request's code from a pull_request_target or workflow_run workflow unless the step sets allow-unsafe-pr-checkout: true, matching the guard actions/checkout has shipped in every maintained major since v4.4.0 / v5.1.0. It does not change what the action checks out in any other event.

Problem

pull_request_target and workflow_run jobs run with the base repository's GITHUB_TOKEN, secrets, and cache scope even when the triggering PR comes from a fork. A workflow that then checks out github.event.pull_request.head.sha (or workflow_run.head_sha) executes the fork's code with those secrets: the "pwn request" pattern. Upstream actions/checkout fails such a step before fetching anything. This action is a fork of upstream 5.0.0 and had no such check, so a workflow that swaps actions/checkout@v4 for useblacksmith/checkout@v1 silently loses that protection.

Fix

Port upstream's src/unsafe-pr-checkout-helper.ts and its tests verbatim, and call it from getInputs:

// input-helper.ts
const isDefaultCheckout = isWorkflowRepository && !core.getInput('ref')
if (!isDefaultCheckout) {
  assertSafePrCheckout({qualifiedRepository, ref, commit, allowUnsafePrCheckout})
}

assertSafePrCheckout throws only when all of these hold: the event is pull_request_target, or workflow_run whose underlying event is a pull_request*; the PR head repository id differs from the base repository id (a fork); and the requested checkout targets the fork repository, a refs/pull/N/{head,merge} ref, or the PR head / merge commit SHA. Same-repo PRs, the default self-checkout, base-branch refs, and unrelated third-party repositories pass through unchanged.

File Change
src/unsafe-pr-checkout-helper.ts new, copied from upstream main (import path adjusted for CommonJS)
__test__/unsafe-pr-checkout-helper.test.ts upstream's 16 cases, ported from ESM unstable_mockModule to this repo's lib/-based jest setup
src/ref-helper.ts export the existing fromPayload helper the guard uses
src/git-source-settings.ts, src/input-helper.ts allowUnsafePrCheckout setting, parsed from the new input, guard call before return result
action.yml, README.md allow-unsafe-pr-checkout input (default false), upstream's wording
dist/index.js rebuilt with npm run build

Evidence

npm run build, npm run lint, and npx jest pass; the new suite covers fork head SHA, merge SHA, refs/pull/* refs, fork repository: input, case-insensitive matching, the opt-in, workflow_run with pull_request and pull_request_target underlying events, and the same-repo / unrelated-repo / default-checkout allow paths.

Risks

Verdict: Ship. The guard only fires for the event + fork + fork-ref combination upstream already rejects, and the error message names the opt-in.
Worry about: workflows that intentionally check out fork code in pull_request_target/workflow_run jobs start failing at the checkout step with Refusing to check out fork pull request code ... until they add allow-unsafe-pr-checkout: true. That is the same break upstream users hit in July, and it is the point of the change.
Checked, not worried: default self-checkout and ref: ${{ github.sha }} / base.sha patterns (tests allows pull_request_target default checkout, allows ... unrelated third-party repo); same-repo PRs (head_repository.id equals base id); non-PR workflow_run chains (workflow_run.event not pull_request* returns early); events other than the two named ones are untouched.
If it goes wrong: a customer's pull_request_target/workflow_run checkout step fails with the message above; they either add the input or pin the previous release while we investigate; rollback is moving v1 back to the previous tag.

Release and moving the v1 tag are a separate step after review.

Link to Devin session: https://app.devin.ai/sessions/79db4072e9c94658bfd784fd9acb22c9
Open in Devin Desktop: https://app.devin.ai/desktop/session/79db4072e9c94658bfd784fd9acb22c9?variant=devin
Requested by: @rcooney-sh


View with [code]smith View full Proof
Need help on this PR? Tag @codesmith-bot with what you need. (Staging)

…llow-unsafe-pr-checkout is set

Ports upstream actions/checkout's unsafe-pr-checkout-helper and its tests so a
pull_request_target or workflow_run job cannot check out a fork's code with the
base repository's secrets and token unless the step opts in, matching the guard
upstream has shipped since v4.4.0 / v5.1.0.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@blacksmith-staging

blacksmith-staging Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Matches Fork PR head SHA refused in pull_request_target

Before, the bundled action fetched and checked out the fork's head commit; after, it fails with the refusal message and leaves the workspace without a git checkout.

Merge-base

Fork SHA checked out

This PR a8130d58

Fork SHA refused

Also checked · 4 match

Matches allow-unsafe-pr-checkout: true opts back in
Matches Default and base-ref checkouts unaffected
Matches refs/pull and fork repository inputs refused
Matches workflow_run from fork PR refused

Proof on a8130d58

View full Proof report

@rcooney-sh
rcooney-sh requested review from adityamaru and pbardea and removed request for aayushshah15 and adityamaru October 27, 2026 14:08
@rcooney-sh
rcooney-sh merged commit 2b9fe87 into main Oct 28, 2026
18 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.

2 participants