Safeguard
Security Guides•

Is workflow_run a safer trigger than pull_request_target?

workflow_run separates privilege from untrusted code, but it can still be abused. How it works, what GitHub warns about, and when each trigger fits.

Safeguard Security Team
3 min read

It can be, when used for privilege separation, which is how GitHub's guidance frames it. A workflow_run job runs the version of the workflow on your default branch after an untrusted workflow finishes. It is not safe if it checks out untrusted code or trusts artifacts blindly, and GitHub warns about both.

Definitions

pull_request_target runs in the context of the base repository when a pull request event happens. It can have secrets and a write token. The workflow file comes from the base branch, not from the pull request.

workflow_run runs when another workflow requests or completes. It uses the workflow file on the default branch, and it can access secrets and write tokens even if the earlier workflow could not.

How they differ

Questionpull_request_targetworkflow_run
What starts itA pull request eventAnother workflow's run
Workflow file taken fromBase branchDefault branch
Secrets and write tokenAvailableAvailable
Natural jobLabeling, triage that needs no PR codeReporting results of an untrusted workflow
Common misuseChecking out PR head and running itExecuting or trusting an artifact from the earlier run

What GitHub's guidance says

GitHub's secure use reference says to avoid pull_request_target if it is not necessary, and that for privilege separation between workflows, workflow_run is a better trigger. It also says these triggers should not explicitly check out untrusted code, including from forks, and that workflow_run workflows should treat artifacts from other workflows with caution.

The events reference adds that running untrusted code on either trigger can lead to cache poisoning and unintended access to write privileges or secrets.

Decision guide

  1. Does the job need to read or run the contributor's code? If yes, use pull_request, with no secrets.
  2. Does the job act on metadata alone, such as adding a label from the title? pull_request_target can fit, if you never check out the head.
  3. Does the job need to publish the output of an untrusted job? Use the two workflow pattern with workflow_run, and treat the artifact as data.
  4. Does the privileged step need a human decision? Put it behind an environment with required reviewers.

Hardening workflow_run

  • Validate every field read from the artifact against a strict format.
  • Never place artifact text into a shell command or an expression.
  • Do not check out the pull request in the reporter.
  • Keep the permissions block narrow and per job.
  • Avoid long relays. GitHub does not run chains deeper than three levels, and long chains are hard to reason about anyway.

Try it yourself

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

Look at where the decision is made and what data it needs.

Where Safeguard fits

Safeguard gates run policy on the pull request, and you can scan a project free to see which dependency findings a gate would act on.

Quick check

Does workflow_run use the workflow file from the pull request?

No. It runs from the default branch, so a contributor cannot edit it through the pull request.

Is the artifact from the first workflow trustworthy?

No. Treat it as untrusted data and validate it before use.

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.