Push and pull_request are the ordinary triggers. Treat pull_request_target and workflow_run as privileged. They can run with repository secrets and write access, and GitHub warns against combining them with a checkout of untrusted pull request code. If a template you copied uses either one, read it twice.
The ordinary triggers
Most starter workflows run on push and pull_request for the default branch. For a pull request from a fork, GitHub's documentation says workflows run on the base repository event, and workflows triggered from a fork with the pull_request event have read only permissions and no access to secrets. That is the safe baseline for building and testing contributed code.
pull_request_target
This event runs in the context of the default branch of the base repository, rather than the merge commit. According to the events reference, that design prevents execution of unsafe code from the head of the pull request that could alter your repository or steal secrets. It exists so a workflow can do things such as label or comment on pull requests from forks.
The same page says to avoid this event if you need to build or run code from the pull request. The secure use guide adds that workflows with this trigger must not explicitly check out untrusted code, including from forks. A copied workflow that does pull_request_target plus a checkout of the pull request head is the pattern to catch.
workflow_run
This trigger starts a workflow after another one finishes. GitHub describes it as a better choice than pull_request_target for privilege separation, but treats it with the same caution: the privileged workflow should handle artifacts from the other workflow carefully, because that content may come from an untrusted pull request. See separating privileged workflows with workflow_run.
Other triggers to read twice
GitHub's compromised runners page lists events where secrets and write permissions are in play, including issue_comment, issues, push, and pull_request from a branch inside the repository. These run with more authority than a fork pull request does, so any script step reacting to user supplied text needs care.
A triage table
| Trigger | Runs in context of | Secrets and write access | Copy rule |
|---|---|---|---|
| pull_request (fork) | Merge commit | Read only, no secrets | Fine for build and test |
| pull_request_target | Default branch | Can be available | Never check out untrusted code |
| workflow_run | Default branch | Can be available | Treat artifacts as untrusted |
| issue_comment, issues | Default branch | Can be available | Never put text into shell lines |
What to do with a template that uses one
- Ask whether the workflow needs the privileged context at all. Often it does not.
- If it does, remove any checkout of pull request head code.
- Put untrusted values in environment variables, never inline in scripts.
- Add CODEOWNERS on the workflows directory.
- Let a scanner such as CodeQL or OpenSSF Scorecard check for dangerous patterns. GitHub's guide mentions both.
Quick check
Is pull_request_target always unsafe?
No. It is intended for tasks like labelling. It becomes dangerous when combined with checking out or running code from the pull request.
Do first time contributors trigger these automatically?
For public repositories, GitHub may require a maintainer with write access to approve workflow runs from a first time contributor's pull request.
Sources
- Events that trigger workflows, GitHub Docs
- Secure use reference, GitHub Docs
- Compromised runners, GitHub Docs
Last reviewed: 19 October 2026.