Skip to content
Latchkey

GitHub Actions pull_request_target Checks Out Untrusted Fork Code

A pull_request_target workflow runs in the context of the base repository - with write permissions and access to secrets - even for fork PRs. Checking out and executing the PR head code hands those secrets to untrusted contributors.

What this error means

A workflow on pull_request_target checks out the PR head (often with ref: github.event.pull_request.head.sha) and runs build or test scripts from the fork, exposing secrets and a write-scoped token to attacker-controlled code.

.github/workflows/ci.yml
on: pull_request_target
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}  # runs fork code with secrets

Common causes

pull_request_target has elevated context

Unlike pull_request, pull_request_target runs with the base repo’s token (write) and secrets, even for forks - by design, so maintainers can label or comment.

Executing untrusted head code

Checking out the PR head and running its scripts (install, build, test) lets fork-supplied code run with those secrets and that token.

How to fix it

Do not run head code under pull_request_target

Keep pull_request_target jobs to trusted, code-free operations (labeling, commenting). Run the actual build/test on pull_request, which has no secrets for forks.

.github/workflows/labeler.yml
# safe: no fork code execution, minimal permissions
on: pull_request_target
permissions:
  pull-requests: write
jobs:
  label:
    runs-on: ubuntu-latest
    steps: [{ uses: actions/labeler@v5 }]

If you must build fork code, isolate it

  1. Run untrusted build/test on on: pull_request (no secrets for forks) instead.
  2. If pull_request_target must check out head, never expose secrets and use a read-only token.
  3. Require a maintainer label/approval gate before any privileged step touches PR code.

How to prevent it

  • Use pull_request_target only for trusted, code-free tasks.
  • Run fork build/test on pull_request without secrets.
  • Never check out and execute PR head code with secrets present.

Frequently asked questions

Why does GitHub even offer pull_request_target?
It lets maintainers run trusted automation (labeling, triage, comments) on fork PRs with the base repo’s permissions. The danger is only when you check out and execute the untrusted PR code with those permissions present.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →