# When Does actions/checkout Enforce the Unsafe PR Checkout Guard?

> Discover when actions/checkout enforces the unsafe PR checkout guard. Learn the specific conditions triggering this security measure to protect your workflows from malicious forks.

- Repository: [GitHub Actions/checkout](https://github.com/actions/checkout)
- Tags: deep-dive
- Published: 2026-08-30

---

**The `actions/checkout` action enforces the unsafe PR checkout guard exclusively when a workflow runs on a `pull_request_target` or `workflow_run` event, the pull request originates from a fork repository, the checkout target explicitly references the fork's code, and the `allow-unsafe-pr-checkout` input remains unset or `false`.**

The `actions/checkout` action implements this security mechanism to prevent "pwn-request" attacks, where malicious code from a fork could compromise repository secrets during CI execution. The guard operates as an opt-in protection system that blocks potentially dangerous checkouts unless explicitly disabled.

## Conditions Required to Trigger the Guard

The guard is enforced only when **all** five specific conditions are satisfied simultaneously. If any condition fails, the checkout proceeds normally without security intervention.

### 1. Workflow Event Type

The guard activates exclusively for workflows triggered by `pull_request_target` or `workflow_run` events that were initiated by a pull request. The source code in [`src/unsafe-pr-checkout-helper.ts`](https://github.com/actions/checkout/blob/main/src/unsafe-pr-checkout-helper.ts) checks `github.context.eventName` against these specific event types to determine if protection is necessary.

### 2. Fork Origin Verification

The pull request must originate from a forked repository rather than a branch within the same repository. The system compares repository IDs using `prHeadRepoId !== baseRepoId` to confirm the head and base repositories differ.

### 3. Checkout Target Points to Fork Code

The action must detect that the checkout parameters explicitly target the forked pull request code. In [`src/unsafe-pr-checkout-helper.ts`](https://github.com/actions/checkout/blob/main/src/unsafe-pr-checkout-helper.ts), the `assertSafePrCheckout` function performs three distinct validation checks:

- **`repositoryMatchesPrHead`**: Performs a case-insensitive comparison between the input repository and the PR head repository name.
- **`refMatchesPullPattern`**: Validates if the input ref matches the pattern `refs/pull/<num>/<head|merge>` using `PR_REF_PATTERN.test(input.ref)`.
- **`commitMatchesPrHeadSha`**: Checks if the supplied commit SHA exists within the PR head SHAs found in the event payload via `prShas.includes(input.commit)`.

If any of these three conditions match, the checkout is considered to be targeting fork code.

### 4. No Explicit Opt-Out Provided

The user must not have set the **`allow-unsafe-pr-checkout`** input to `true`. When this input is enabled, the helper returns early and skips all security validations. This input is declared in [`action.yml`](https://github.com/actions/checkout/blob/main/action.yml) and processed through the input helper in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts).

### 5. Custom Ref or Commit Supplied

The guard only runs when the workflow explicitly supplies a custom `ref` or `commit` parameter. Default self-checkout operations (where no explicit target is specified) bypass the guard entirely. The invocation logic in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts) determines whether a custom checkout target exists before calling `assertSafePrCheckout`.

## Source Code Implementation

The security logic resides primarily in **[`src/unsafe-pr-checkout-helper.ts`](https://github.com/actions/checkout/blob/main/src/unsafe-pr-checkout-helper.ts)**, which exports the `assertSafePrCheckout` function. This function throws an error with a clear security message when all dangerous conditions are met:

```

Refusing to check out fork pull request code from a 'pull_request_target' workflow…
To opt in, review the risks at https://gh.io/securely-using-pull_request_target
and set 'allow-unsafe-pr-checkout: true' on the actions/checkout step.

```

The **[`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts)** module prepares the `IUnsafePrCheckoutInput` interface and orchestrates the guard invocation, ensuring the security check occurs only after confirming a non-default checkout configuration.

## Practical Configuration Examples

### Guard Active: Blocked Checkout Attempt

When attempting to checkout fork PR code from a `pull_request_target` workflow without authorization, the guard blocks execution:

```yaml
name: CI
on:
  pull_request_target:
    types: [opened, synchronize]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: refs/pull/${{ github.event.pull_request.number }}/head
      # Error: Refusing to check out fork pull request code...

```

### Opt-In: Disabling the Guard

After reviewing the security advisory at `https://gh.io/securely-using-pull_request_target`, explicitly enable unsafe checkout:

```yaml
name: CI
on:
  pull_request_target:
    types: [opened, synchronize]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: refs/pull/${{ github.event.pull_request.number }}/head
          allow-unsafe-pr-checkout: true

```

### Safe Scenario: Guard Not Triggered

Standard events like `push` or `pull_request` (non-target) never trigger the guard, even for fork PRs:

```yaml
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: refs/pull/${{ github.event.pull_request.number }}/head
      # Proceeds normally: event type does not require protection

```

## Summary

- The **`unsafe PR checkout guard`** protects against pwn-request vulnerabilities in `pull_request_target` and `workflow_run` workflows.
- Enforcement requires five simultaneous conditions: specific event type, fork origin, explicit targeting of fork code, absence of the opt-out flag, and custom ref/commit parameters.
- The protection is **opt-out only**; setting `allow-unsafe-pr-checkout: true` disables the security check for that step.
- Core implementation resides in [`src/unsafe-pr-checkout-helper.ts`](https://github.com/actions/checkout/blob/main/src/unsafe-pr-checkout-helper.ts) with invocation logic in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts).
- The guard throws a descriptive error linking to GitHub's security advisory when dangerous checkout combinations are detected.

## Frequently Asked Questions

### What is the pwn-request vulnerability that this guard prevents?

The pwn-request vulnerability occurs when a `pull_request_target` workflow automatically checks out and executes code from a forked pull request, granting that code access to repository secrets and write permissions. Malicious actors can exploit this to exfiltrate secrets or modify repository code. The unsafe PR checkout guard prevents this by requiring explicit opt-in before allowing such checkouts.

### How do I safely check out fork PR code for testing?

First, review the security risks documented at `https://gh.io/securely-using-pull_request_target`. Ensure your workflow does not execute untrusted code with elevated permissions. If absolutely necessary, set `allow-unsafe-pr-checkout: true` on the specific `actions/checkout` step, but isolate that step from any operations using sensitive secrets or write permissions.

### Does this guard affect normal `pull_request` events?

No, the guard does not activate for standard `pull_request` events. It only enforces protection on `pull_request_target` and `workflow_run` events triggered by pull requests, as these contexts provide elevated repository permissions that make fork checkouts dangerous.

### What happens if I don't specify a ref or commit in pull_request_target?

If you use `actions/checkout@v4` without specifying a `ref` or `commit` input in a `pull_request_target` workflow, the action performs a default self-checkout of the base repository's default branch. Since this does not checkout the fork's code, the guard is not triggered, and the checkout succeeds without requiring `allow-unsafe-pr-checkout`.