Understanding the Unsafe PR Checkout Guard in actions/checkout v7

The unsafe PR checkout guard is a security mechanism in actions/checkout v7 that blocks workflows from automatically checking out forked pull request code when running with elevated privileges, requiring explicit opt-in via allow-unsafe-pr-checkout: true.

The unsafe PR checkout guard protects repositories from privilege escalation attacks when using GitHub Actions. Introduced in actions/checkout v7, this safety feature specifically targets the pull_request_target and workflow_run events, where workflows execute with the base repository's GITHUB_TOKEN and secrets access.

What Is the Unsafe PR Checkout Guard?

The guard is an automated validation system implemented in src/unsafe-pr-checkout-helper.ts that intercepts checkout requests before they execute. It analyzes whether a workflow attempts to checkout code from a forked pull request while running in a privileged context that has access to sensitive repository data. If the guard detects this unsafe pattern without explicit user consent, it aborts the workflow with a descriptive security error.

Why the Guard Exists

Workflows triggered by pull_request_target and workflow_run events run with the base repository's permissions, secrets, and cache scope. If such a workflow automatically checks out and executes code from a forked pull request, a malicious contributor could inject scripts that steal secrets, modify repository contents, or access sensitive data using these elevated privileges. This attack vector, known as a pwn request, has historically affected open-source projects that process untrusted pull requests.

The guard mitigates this risk by forcing repository maintainers to explicitly acknowledge the danger through configuration, preventing accidental execution of untrusted code in privileged environments.

How the Guard Works

The implementation in src/unsafe-pr-checkout-helper.ts follows a strict validation pipeline:

Step 1: Check for Explicit Opt-In

At lines 14-16, the guard first inspects the allowUnsafePrCheckout input. If the user configured allow-unsafe-pr-checkout: true in the workflow YAML, the function returns immediately, bypassing all security checks.

Step 2: Filter by Event Type

The protection only applies to privileged events. Lines 18-21 verify that eventName equals pull_request_target or workflow_run. Workflows running on standard pull_request, push, or other events bypass the guard entirely.

Step 3: Repository Identification and Fork Detection

At lines 23-26, the helper retrieves the base repository ID using fromPayload('repository.id'). It then extracts the pull request head repository data (lines 32-50), populating variables for prHeadRepoId, prHeadRepoFullName, and an array of commit SHA values (prShas).

At lines 52-55, the guard compares the PR head repository ID against the base repository ID. If these values match, the pull request originates from a branch within the same repository rather than a fork, and the guard exits safely without blocking the checkout.

Step 4: Checkout Target Validation

For confirmed fork pull requests, the guard validates whether the checkout request specifically targets the fork's code. Lines 57-70 examine the requested repository name, ref pattern, and commit SHA against the collected prShas to determine if the operation would checkout the untrusted fork code.

Step 5: Error Throwing

If the guard detects a fork PR checkout attempt without explicit opt-in, lines 74-81 throw an error containing a clear explanation of the security risk and a link to GitHub's advisory documentation.

Configuration Examples

Default Safe Behavior (Blocking Fork PRs)

When using pull_request_target without explicit opt-in, the guard prevents checkout of fork code:

name: CI
on:
  pull_request_target:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - name: Build
        run: npm ci && npm run build

This configuration fails with the error message Refusing to check out fork pull request code from a 'pull_request_target' workflow... when processing pull requests from forks.

Opting In to Unsafe Checkout

To explicitly permit fork PR checkouts in privileged contexts:

name: CI
on:
  pull_request_target:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          allow-unsafe-pr-checkout: true
      - name: Build
        run: npm ci && npm run build

Warning: Only enable this option after thoroughly reviewing the pull request for malicious code or when the job runs in an isolated environment. Enabling this exposes your base repository's secrets and GITHUB_TOKEN to potentially untrusted contributors.

Safe Alternative Using pull_request

For workflows that do not require base repository secrets, use the standard pull_request event:

name: CI
on:
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - name: Test
        run: npm test

Because pull_request workflows execute in the fork's context without access to base repository secrets, the guard does not activate, and checkout proceeds normally with reduced privileges.

Key Source Files

The guard spans three critical files in the actions/checkout v7 codebase:

  • src/unsafe-pr-checkout-helper.ts: Contains the core validation logic, fork detection algorithms, and error throwing mechanisms.
  • src/input-helper.ts: Parses the allow-unsafe-pr-checkout boolean from workflow inputs and passes it to the guard function.
  • src/main.ts: Serves as the entry point that orchestrates the checkout flow, invoking the unsafe PR checkout helper before executing git operations.

Summary

  • The unsafe PR checkout guard automatically blocks checkout of fork PR code in privileged workflow contexts to prevent pwn request attacks.
  • Protection activates specifically for pull_request_target and workflow_run events.
  • The guard compares repository IDs to detect forks and validates checkout targets against PR head commit SHAs.
  • Users must explicitly set allow-unsafe-pr-checkout: true to bypass the security check.
  • Core implementation resides in src/unsafe-pr-checkout-helper.ts with integration points in src/input-helper.ts and src/main.ts.

Frequently Asked Questions

What triggers the unsafe PR checkout guard?

The guard triggers when a workflow running on pull_request_target or workflow_run attempts to checkout code from a forked pull request without setting allow-unsafe-pr-checkout: true. Specifically, it activates when the PR head repository ID differs from the base repository ID and the checkout target matches the fork's ref or commit SHA.

How do I bypass the unsafe PR checkout guard?

Add allow-unsafe-pr-checkout: true to the with configuration of your actions/checkout@v7 step. However, only bypass the guard after manually verifying the pull request contents contain no malicious code, or when the workflow runs in a sandboxed environment without access to sensitive secrets.

Is it safe to set allow-unsafe-pr-checkout to true?

Setting allow-unsafe-pr-checkout to true exposes your workflow to pwn request vulnerabilities unless additional safeguards exist. It is only safe when you have manually reviewed the pull request changes or isolated the job using techniques like requiring manual approval for workflow runs, running in ephemeral containers without secret access, or using CODEOWNERS reviews to gate execution.

What is the pwn request vulnerability?

A pwn request is a security attack where a malicious actor submits a pull request from a fork containing hidden malicious scripts. If a pull_request_target or workflow_run workflow automatically checks out and executes this code, the attacker gains access to the base repository's secrets, GITHUB_TOKEN write permissions, and cache data. The unsafe PR checkout guard prevents this by requiring explicit opt-in before allowing fork code execution in these privileged contexts.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →