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 worksAI 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
on:
pull_request:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: npm ci
- run: npm testAvoid 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.
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 testKeep 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.
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?
Find every
pull_request_target,issue_comment,workflow_run, andpull_request_reviewworkflow and inventory its token permissions and secrets.Trace every checkout, git fetch, downloaded artifact, cache restore, and command to determine whether outsider-controlled content can execute.
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.
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.
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.
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.
- Security
GitHub Actions permissions: use least privilege
Set a read-only top-level
permissions:default in every GitHub Actions workflow, then grant each job only the additionalGITHUB_TOKENscopes it needs, so unrelated steps cannot inherit repository write access. - Security
Prevent GitHub Actions cache poisoning
Keep untrusted code from writing cache entries that trusted release or deploy jobs restore, and cache inert dependency data rather than executable workspace state or credentials.
- Security
GitHub Actions secrets: prevent leaks and overexposure
Expose a GitHub Actions secret only to the step that consumes it, gate production secrets with an environment, prefer short-lived OIDC credentials, and keep credentials out of logs, command arguments, caches, and artifacts.
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