When a pull request edits a workflow file, check four things: whether untrusted input reaches a run step, how much access the GITHUB_TOKEN gets, whether the trigger runs code from a fork with privileges, and whether third party actions are pinned. Workflow files run with your secrets, so treat them like production code.
Why workflows need their own review
A workflow is code that runs on your behalf with access to tokens and secrets. GitHub's secure use reference notes that anyone with write access can read all repository secrets. That makes a quiet edit to a workflow a good target, and a careless edit an easy mistake.
The four checks
| Check | What to look for | Safer pattern from GitHub's guidance |
|---|---|---|
| Script injection | A value such as the pull request title placed directly inside a run script | Pass it through an environment variable, or use an action that takes it as an argument |
| Token scope | A permissions block that grants write on everything | Default to read only, grant more to individual jobs |
| Trigger | pull_request_target or workflow_run that checks out pull request code | Avoid pull_request_target if not needed, and never check out untrusted code in it |
| Third party actions | A tag or branch reference | A full length commit SHA from the action's own repository |
Walking through a diff
- Read the trigger first. If it changed to pull_request_target, ask why. GitHub says to avoid it when not necessary and to prefer workflow_run for privilege separation.
- Search the diff for expression syntax inside run blocks. An untrusted field interpolated into a shell script becomes part of the script.
- Look at the permissions key at workflow and job level. Ask whether each write permission is needed by that job.
- Check each new uses line. Is it pinned to a SHA, and does the SHA come from the real repository and not a fork?
- Look for secrets written in plain text, or one secret holding a whole JSON or YAML blob. GitHub advises one secret per value so redaction works.
Here is the shape of the injection fix:
# risky
- run: echo "${{ github.event.pull_request.title }}"
# safer
- env:
TITLE: ${{ github.event.pull_request.title }}
run: echo "$TITLE"
Protect the workflow files themselves
GitHub recommends adding the workflows directory to CODEOWNERS, so any change needs approval from a named reviewer. Pair it with the branch protection setting that requires code owner review, otherwise the file only requests a review and does not enforce one.
Try it yourself
Open the pull request gate demo and watch how a policy decides whether a change may merge. Notice what the gate checks and where a human still decides.
Click through it at your own pace, or open it full screen.
Where Safeguard fits
Safeguard offers policies and gates for pull requests and a free dependency scan at gold.safeguard.sh/scan. Workflow hardening choices above remain settings in your own repository.
Quick check
Is a version tag as safe as a commit SHA?
GitHub says tags can be moved or deleted if a repository is compromised, so pin to a full length SHA unless you trust the action's creator.
Why add the workflows folder to CODEOWNERS?
So changes to workflow files need approval from a designated reviewer, as GitHub recommends.
Sources
Last reviewed: 19 October 2026.