Best practice · Security

Secure pull_request_target in GitHub Actions

Use `pull_request` for any job that checks out, builds, installs, or tests contributor code; reserve `pull_request_target` for metadata-only work on trusted base-branch code with minimal permissions.

How StarSling works

AI agents open the PR

StarSling agents inspect your workflow, apply this optimization, and open a reviewable PR automatically.

Do this: run fork code without privileges

Untrusted contributor code runs only on pull_request, without repository secrets and with a read-only token. A pull_request_target workflow never checks out the pull request head, fetches its patch, runs its scripts, or restores state the fork can influence. If a privileged follow-up is necessary, it is a separate workflow that treats untrusted artifacts as data, not executable code.12

Build the contributor's code on pull_request
on:
  pull_request:

permissions:
  contents: read

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

Avoid this: execute the fork on pull_request_target

Any GitHub user can open a fork pull request whose install or test code executes in the base repository's privileged context.

Explicitly bypassing checkout's fork guard
on:
  pull_request_target:

permissions:
  contents: write

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          ref: ${{ github.event.pull_request.head.sha }}
          # Intentionally unsafe: checkout v7 blocks fork PR heads on this
          # privileged trigger unless this opt-out is set.
          allow-unsafe-pr-checkout: true
      - run: npm ci
      - run: npm test

Keep pull_request_target metadata-only

A target workflow can label a pull request or post a comment using trusted base-branch code, but every pull request field remains untrusted input. Do not interpolate titles, bodies, branch names, or labels directly into shell code. Keep permissions minimal and use least-privilege GitHub Actions permissions. The ci-secure skill detects privileged fork execution and related trust-boundary chains.

Runs on your machine. GitHub API optional, for the impostor-SHA check and the dormancy note.

Why it matters

pull_request_target runs in the context of the base branch and can receive a privileged token or secrets that ordinary fork pull requests do not. Checkout v7 blocks fork PR head and merge refs on this trigger by default, but an explicit allow-unsafe-pr-checkout: true, a manual git fetch, or another execution path can bypass that layer. Executing the fork inside the privileged context turns an outsider's contribution into code execution with repository authority.

When pull_request_target is appropriate

Use it when

Use pull_request_target only for base-branch, metadata-only automation that genuinely needs target-repository context, such as applying a label through a tightly scoped API call.

Be careful when

Do not use it for builds, tests, dependency installation, generated code, benchmarks, or any step that fetches or executes pull request content. Use pull_request for those jobs.

Verify on your repo

Hand this prompt to your coding agent (Claude Code, Cursor, and the like) to audit and fix this practice in your own repo.

Prompt for your coding agent
Audit workflows triggered by `pull_request_target` or another outsider-influenceable privileged event. If a job checks out a pull request head, fetches its patch, installs dependencies, runs repository scripts, restores fork-influenced state, or otherwise executes contributor content, move that work to an unprivileged `pull_request` workflow with read-only permissions and no secrets. Keep only metadata operations on `pull_request_target`, using trusted base-branch code and the minimum job permissions. Preserve the intended label, comment, or status behavior, show the diff, and open a PR rather than applying it blindly.

Ground these changes in the upstream docs before you edit: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request_target, https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/. If you cannot fetch them, say so rather than guessing, and cite what you used in the PR description.

Prefer to check by hand?

  1. Find every pull_request_target, issue_comment, workflow_run, and pull_request_review workflow and inventory its token permissions and secrets.

  2. Trace every checkout, git fetch, downloaded artifact, cache restore, and command to determine whether outsider-controlled content can execute.

  3. Move contributor builds to pull_request; keep privileged follow-ups separate and treat their inputs strictly as data.

Go further

One fix, all of them, or forever.

You have the prompt for this one practice. Here is how much further you can take it, each step doing more for you than the last.

  1. Fix this one thing

    Copy the prompt above

    Hand P14.1 / P14.7 / P14.9 / P14.18 to your coding agent and fix it in your repo today.

  2. Fix everything, once

    Install the ci-secure skill

    One prompt checks every workflow against ten critical exploit-chain attack vectors, explains each reachable path, and lets you choose focused fixes. Open source, runs locally. It is not a comprehensive security audit.

  3. Keep it fixed, forever

    Install the StarSling GitHub App

    Connect GitHub and the fixes stay applied as your CI evolves, with agents that keep inspecting your workflows and opening optimization PRs you review.

More best practices for GitHub Actions

Where to go next in the CI best-practices catalog.

All CI best practices

Trying to make GitHub Actions faster?

If you do not know why a run is slow yet, start with the diagnostic guide. Let an agent find which of these applies: /ci-speedup. If your workflows are already optimized but still slow, see our guide to fast GitHub Actions and GitHub Actions runner alternatives. Building containers in CI? The Docker workflow guide covers layer caching end to end. Want to see how your configuration measures up before changing anything? CI Score grades workflow config against a pass/fail rubric of these practices. It is not a speed measurement.

FAQ

What is the difference between pull_request and pull_request_target?

pull_request runs against the contribution with restricted fork permissions. pull_request_target runs in the base repository context, which is useful for trusted metadata automation but dangerous if it executes fork content.

Is pull_request_target always unsafe?

No. It can be appropriate for metadata-only work that uses trusted base-branch code, minimal permissions, and no execution of pull request content. The unsafe combination is privileged context plus attacker-controlled code or state.

Sources

1GitHub Actions · pull_request_target event (opens in new tab)

2GitHub Security Lab · preventing pwn requests (opens in new tab)

Last updated 2026-08-19