Safeguard
Developer Tools•

Is pull_request_target safe in an open source repository?

pull_request_target is safe only if it never runs code from a pull request. Learn the pwn request pattern and how to split workflows with workflow_run.

Safeguard Security Team
4 min read

The pull_request_target trigger is safe only if the workflow never checks out or runs code from the pull request. It runs with the base repository's token and secrets, so running a stranger's build script there can hand them control of your repository. Use pull_request, or split the job in two.

What the trigger does differently

A normal pull_request workflow from a fork gets restricted permissions and no secrets. The pull_request_target trigger runs in the context of the target repository, with a read and write token and access to its secrets. GitHub's own hardening guide says to avoid it if it is not necessary, and says workflows with these triggers must not explicitly check out untrusted code.

The pwn request pattern

GitHub Security Lab describes the dangerous combination: a pull_request_target workflow that checks out the pull request head, then runs something like an install or build. The chain looks like this:

  1. An attacker opens a pull request from a fork.
  2. The workflow checks out the attacker's commit.
  3. A step runs npm install, a test script or a build.
  4. The attacker's code runs with your write token available.

Install scripts are one path. A changed package.json, build script or test file is another. Even a workflow that never references a secret is exposed because of the write token. Also note that actions/checkout keeps credentials on disk by default, which later steps can read, unless you set persist-credentials to false.

Safer patterns

NeedSafer approach
Run tests on fork pull requestsUse pull_request, which blocks secrets and write access for forks.
Comment on or label a pull request after testsTwo workflows: an unprivileged pull_request build that saves results as artifacts, then a privileged workflow_run job that reads them.
Run only after a human looksLabel gating, but treat it as a stopgap, since an attacker can push after approval and before the run.
Handle pull request files as dataFine if nothing executes them.

When you consume artifacts in the privileged job, treat them as untrusted. Reading a pull request number or a coverage figure is acceptable. Running a binary from the artifact is not.

A review checklist for your repository

  • Search your workflows for pull_request_target and workflow_run.
  • For each hit, ask whether it checks out the pull request head or runs anything from it.
  • Set permissions at the job level to the minimum. See least privilege GITHUB_TOKEN for a release workflow.
  • Add the workflows folder to CODEOWNERS so changes need a reviewer, as in CODEOWNERS for workflows and release files.
  • After you fix a workflow, close or rebase open pull requests, since open ones keep using the workflow version from when they were opened.

Try it yourself

Click through it at your own pace, or open it full screen.

Click through the pull request flow and notice that the check runs before anything merges. That ordering is the point of a gate.

Safeguard can enforce policies and gates on pull requests, and the free scan at gold.safeguard.sh/scan is a quick first look at your dependencies.

Quick check

Is it ever fine to use pull_request_target?

Yes, for jobs that only handle pull request metadata, such as labeling, as long as they do not check out or execute pull request code.

Does a label gate fully fix the risk?

No. The Security Lab article calls it a temporary fix because commits can be pushed after approval.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.