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.
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 secretsCommon 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.
# 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
- Run untrusted build/test on on: pull_request (no secrets for forks) instead.
- If pull_request_target must check out head, never expose secrets and use a read-only token.
- 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.