How actions/checkout Blocks Unsafe Forked PR Checkouts by Default
The actions/checkout action prevents "pwn request" attacks by default through a safety guard that detects and blocks attempts to check out code from forked pull requests when workflows run with privileged tokens.
Forked pull request workflows present a critical security risk where malicious code executes with the base repository's trusted GITHUB_TOKEN and secrets. The actions/checkout repository mitigates this threat through built-in safeguards that analyze checkout parameters before executing dangerous operations.
The "Pwn Request" Vulnerability
The "pwn request" attack occurs when a workflow triggered by pull_request_target or workflow_run events runs with the base repository's privileged credentials but processes code from a forked pull request. By default, actions/checkout implements a security boundary that prevents this exploit without requiring manual configuration from repository maintainers.
Input Processing and Guard Invocation
The protection mechanism begins in src/input-helper.ts, where the getInputs function orchestrates the checkout configuration. After resolving the repository, reference, and commit values, the action invokes the safety guard unless performing a default self-checkout:
unsafePrCheckoutHelper.assertSafePrCheckout({
qualifiedRepository,
ref: result.ref,
commit: result.commit,
allowUnsafePrCheckout: result.allowUnsafePrCheckout
})
This invocation occurs at lines 94-100 in src/input-helper.ts. The guard is skipped only when the workflow performs a default checkout of its own repository without an explicit ref input, preserving normal operations while maintaining security boundaries.
Fork Detection and Blocking Logic
The core protection resides in src/unsafe-pr-checkout-helper.ts within the assertSafePrCheckout function (lines 13-81). This helper executes a multi-layered verification process specifically for events capable of running under trusted tokens:
- Event Validation: The guard activates exclusively for
pull_request_targetandworkflow_runevents - Repository Identification: It extracts the head repository ID and full name from the workflow context payload
- Fork Detection: The helper compares the head repository ID against the base repository ID; differing values indicate a forked pull request
When a fork is detected, the guard blocks the checkout if any of these conditions match the unsafe repository:
- The qualified repository name equals the fork's head repository full name
- The input
refmatches the pull request patternrefs/pull/<num>/<head|merge> - The supplied commit SHA matches one of the fork's head commit SHAs
If triggered, the helper throws an error that aborts the workflow and directs users to security guidance at https://gh.io/securely-using-pull_request_target.
Configuration and Opt-In Bypass
Maintainers can explicitly disable this protection for specific use cases by setting the allow-unsafe-pr-checkout input parameter to true. The following configuration demonstrates both safe default usage and the blocked pattern:
# Safe default usage - checks out the workflow's own repository
- uses: actions/checkout@v4
# Blocked unsafe pattern - fork checkout without opt-in
- uses: actions/checkout@v4
with:
repository: attacker/forked-repo
ref: refs/pull/123/head
# allow-unsafe-pr-checkout: true # Required to bypass the guard
Summary
- Guard Location: The
getInputsfunction insrc/input-helper.tsinvokesassertSafePrCheckoutto intercept dangerous operations before execution. - Detection Method: The helper in
src/unsafe-pr-checkout-helper.tsidentifies forks by comparing repository IDs and validates against PR ref patterns and commit SHAs. - Scope: Protection applies only to privileged events (
pull_request_target,workflow_run) that present actual security risks. - Bypass Mechanism: Repository owners must explicitly set
allow-unsafe-pr-checkout: trueto check out forked PR code in sensitive workflow contexts.
Frequently Asked Questions
What triggers the unsafe PR checkout block?
The block triggers when a workflow running under pull_request_target or workflow_run attempts to check out code where the repository ID differs from the base repository, or when the ref matches refs/pull/<num>/<head|merge> patterns that indicate forked PR code.
How can I bypass the protection for legitimate use cases?
Add allow-unsafe-pr-checkout: true to the action inputs. According to the source code in src/unsafe-pr-checkout-helper.ts, this boolean flag instructs assertSafePrCheckout to skip validation, though this is strongly discouraged without additional security measures.
Does the guard affect normal pull_request events?
No. The assertSafePrCheckout function specifically checks for pull_request_target and workflow_run events before activating. Standard pull_request events do not receive the base repository's privileged GITHUB_TOKEN, so they do not present the same "pwn request" attack vector.
Where is the blocking logic implemented in the source code?
The blocking logic resides in src/unsafe-pr-checkout-helper.ts within the assertSafePrCheckout function (lines 13-81). The invocation trigger exists in src/input-helper.ts (lines 94-100), while src/workflow-context-helper.ts provides supporting context utilities used during the security validation process.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →