SEC003 — dangerous-trigger-checkout¶
Severity: blocker · Category: Security
What it checks¶
Flags workflows triggered by pull_request_target, workflow_run, or
issue_comment (events that run with elevated GITHUB_TOKEN privileges
and access to repo secrets, but can be invoked by anyone without repo
write access) that also actions/checkout an untrusted head — a ref:
pointing at github.event.pull_request.head.*, github.head_ref,
github.event.workflow_run, or a ChatOps-style refs/pull/<issue
number>/head.
Why it matters¶
pull_request_target deliberately runs in the base repo's context (so
it has secrets and write access), but its default checkout is the
base branch — safe. The vulnerability only appears when a workflow
explicitly overrides that to check out the PR author's code instead:
now their build scripts, test fixtures, or even a malicious commit run
with privileges they were never supposed to have. This is the
single most-exploited GitHub Actions misconfiguration pattern.
Examples¶
Flagged — checks out the PR's own head commit while running with base-repo privileges:
on: pull_request_target
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- run: npm ci && npm test # runs the PR author's code with your secrets
Fixed — either drop the ref: override (so it checks out the safe
base branch), or, if the workflow genuinely needs to build PR code,
split it into an unprivileged pull_request-triggered job that does the
building and a separate, minimal pull_request_target job that only
reads metadata (never checks out code):
on: pull_request_target
jobs:
read-metadata-only:
runs-on: ubuntu-latest
permissions:
pull-requests: read
steps:
- run: echo "PR title is ${{ github.event.pull_request.title }}"
# no checkout at all — nothing here can execute PR-supplied code
Suppressing¶
The finding is reported on the uses: actions/checkout@... line itself:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # vlotpipe: ignore[SEC003]
with:
ref: ${{ github.event.pull_request.head.sha }}
Only if you've verified — and ideally documented right there in a comment — exactly why the checkout is safe (e.g. later steps only read a specific, non-executable file path and never run PR-supplied code).