GitHub Actions Checkout v7: Fixing the Pwn Request Vulnerability Before July 16
GitHub Actions Checkout v7: Fixing the Pwn Request Vulnerability Before July 16
If your CI pipeline uses pull_request_target and checks out the pull request's head commit, actions/checkout@v7 will now refuse to do it. That's not a bug โ it's GitHub closing one of the most common CI/CD attack patterns in the ecosystem, known as a "pwn request." The change shipped June 18, 2026, and on July 16, 2026 the same enforcement gets backported to every currently supported major version of the action (v3 through v6), so pinning to an older tag won't save you for long.
If you maintain workflows that touch fork pull requests, you need to know whether you're affected before that date.
Why pull_request_target workflows are dangerous
pull_request_target exists so that automation (auto-labeling, welcome comments, stale-PR bots) can run with access to the base repository's secrets and a writable GITHUB_TOKEN, even when triggered by a pull request from a fork. Unlike the pull_request event, the workflow definition itself is always pulled from the default branch โ not from the fork โ so in theory an attacker can't just rewrite your workflow file to do something malicious.
The vulnerability shows up when someone adds a checkout step that pulls in the fork's actual code:
1# INSECURE โ do not use this pattern
2on:
3 pull_request_target:
4
5jobs:
6 build:
7 runs-on: ubuntu-latest
8 steps:
9 - uses: actions/checkout@v6
10 with:
11 ref: ${{ github.event.pull_request.head.sha }}
12 - run: make test # attacker's Makefile now runs with your secrets
The workflow YAML is trusted, but the checked-out repository content is not. Once that untrusted code executes โ a Makefile, an npm install with a malicious postinstall script, a build tool's config file โ it runs with the full privileges of the job: your GITHUB_TOKEN, any secrets exposed to the workflow, and (for public repos) the default-branch dependency cache. This exact pattern has been behind several real supply-chain compromises, which is why GitHub's security team calls it a "pwn request."
What changed in actions/checkout v7
Starting with v7 (June 18, 2026), actions/checkout detects when it's running inside a pull_request_target workflow, or a workflow_run triggered by a pull request event, and refuses to fetch the fork's PR head. It fails the step outright if you try any of these common insecure patterns:
1# All three of these now fail on actions/checkout@v7
2- uses: actions/checkout@v7
3 with:
4 ref: refs/pull/${{ github.event.pull_request.number }}/merge
5
6- uses: actions/checkout@v7
7 with:
8 ref: ${{ github.event.pull_request.head.sha }}
9
10- uses: actions/checkout@v7
11 with:
12 repository: ${{ github.event.pull_request.head.repo.full_name }}
The rollout timeline matters for planning:
| Date | What happens |
|---|---|
| June 18, 2026 | v7 released with enforcement on by default |
| July 16, 2026 | Enforcement backported to all supported major versions (v3โv6) |
| Ongoing | Floating tags like @v4 pick up the change automatically; only SHA-pinned versions stay on old behavior |
If your workflows pin actions/checkout to a specific commit SHA rather than a major-version tag, you won't get this protection (or the breakage) automatically โ which cuts both ways: you're not protected until you upgrade, but you also won't get surprised mid-sprint by a floating tag suddenly failing your CI.
How to check if you're affected
Search your workflows for the risky combination:
1grep -rl "pull_request_target" .github/workflows/
For each match, check whether the same job also checks out the PR head:
1grep -A5 "actions/checkout" .github/workflows/*.yml | grep -E "head.sha|pull/.*merge|head.repo"
If nothing matches, you're likely fine. If something does, you have a workflow that needs one of the fixes below before July 16.
Migration paths
1. Switch to pull_request if you don't need secrets.
Most CI jobs โ running tests, linting, building โ don't actually need write access to the base repo or secret access. pull_request runs with a read-only, fork-scoped token and checks out fork code safely by default:
1on:
2 pull_request:
3
4jobs:
5 test:
6 runs-on: ubuntu-latest
7 steps:
8 - uses: actions/checkout@v7
9 - run: make test
This is the fix for the overwhelming majority of workflows that were using pull_request_target incorrectly just to "make checkout work."
2. Split trusted and untrusted work into two workflows.
If you genuinely need pull_request_target (e.g., to comment on the PR with results, or to access a deployment secret), separate the untrusted build/test step from the privileged step. Run untrusted code in a pull_request-triggered workflow with no secrets, upload the results as an artifact, then consume that artifact in a second workflow_run-triggered job that has secrets but never executes fork code:
1# .github/workflows/test.yml โ no secrets, safe to run untrusted code
2on:
3 pull_request:
4jobs:
5 test:
6 runs-on: ubuntu-latest
7 steps:
8 - uses: actions/checkout@v7
9 - run: npm test -- --json > results.json
10 - uses: actions/upload-artifact@v4
11 with:
12 name: test-results
13 path: results.json
1# .github/workflows/comment.yml โ has secrets, never checks out fork code
2on:
3 workflow_run:
4 workflows: ["test"]
5 types: [completed]
6jobs:
7 comment:
8 runs-on: ubuntu-latest
9 steps:
10 - uses: actions/download-artifact@v4
11 with:
12 name: test-results
13 run-id: ${{ github.event.workflow_run.id }}
14 github-token: ${{ secrets.GITHUB_TOKEN }}
15 - run: |
16 # parse results.json as data only โ never execute it
17 echo "Posting results comment using the GITHUB_TOKEN"
3. Explicitly opt out โ only if you've confirmed it's safe. If you've reviewed the workflow and are certain checking out fork code is intentional and controlled (for example, a maintainer-only repo where you trust every contributor, or a workflow that only reads files as data and never executes them), you can opt back in:
1- uses: actions/checkout@v7
2 with:
3 ref: ${{ github.event.pull_request.head.sha }}
4 allow-unsafe-pr-checkout: true
The flag name is deliberately loud. It's meant to be impossible to miss in a code review or a grep across your org's workflows, so treat any appearance of allow-unsafe-pr-checkout in a PR as something that needs a second reviewer.
Best practices
- Default to
pull_requestfor anything that doesn't need secrets. Reach forpull_request_targetonly when you have a specific, reviewed reason. - Never execute anything from checked-out fork code in a privileged context โ treat it as data (read files, parse JSON) rather than as code (
run,make,npm installwith lifecycle scripts,pip install -e). - Scope
GITHUB_TOKENpermissions explicitly with apermissions:block at the workflow or job level, even after fixing the checkout issue โ least privilege is a second layer of defense, not a replacement for this fix. - Audit self-hosted runners separately: they don't get the ephemeral isolation of GitHub-hosted runners by default, so a pwn request there can persist beyond a single job.
Common mistakes to avoid
- Assuming SHA-pinning your action version protects you long-term. It only delays the fix โ you're running without the security patch until you upgrade, and you'll eventually need to.
- Adding
allow-unsafe-pr-checkoutreflexively to "make CI green again." That defeats the entire point of the change. Fix the workflow instead of silencing the guardrail. - Forgetting
workflow_runworkflows. The same fork-checkout risk applies there when the triggering workflow was a pull request event โ teams often auditpull_request_targetand missworkflow_run. - Trusting
pull_request_targetbecause "it's just a label bot." Label/triage bots that later grew a "run tests and post results" step are exactly how this vulnerability class spreads โ audit workflows for scope creep, not just their original intent.
Troubleshooting
"My CI suddenly started failing on checkout after July 16 with no code changes."
You're almost certainly hitting the backported enforcement on a floating major-version tag (e.g., @v4). Check your checkout step for ref: or repository: pointing at PR head data, and apply one of the migration paths above.
"I need this to keep working exactly as before, right now, while I plan the real fix."
Add allow-unsafe-pr-checkout: true as a temporary bridge, but open a tracked issue immediately โ this flag is a visible admission of risk, not a fix.
FAQ
Does this affect pull_request workflows too?
No. pull_request already checks out fork code safely with a restricted, read-only token by default. This change only targets pull_request_target and pull-request-triggered workflow_run workflows, where the token has broader privileges.
Will my workflow just silently keep working if I do nothing?
Only if you're pinned to a specific commit SHA. Any major-version tag (@v3 through @v6) gets the enforcement backported by July 16, 2026, and will start failing checkout steps that use the insecure patterns.
Is this only a GitHub-hosted runner issue? No โ the risk is actually worse on self-hosted runners, since a compromised job there can potentially persist or pivot beyond the single workflow run, unlike GitHub's ephemeral hosted runners.
What if I use GitLab CI or another platform โ does this apply? The specific fix is GitHub-only, but the underlying vulnerability class (untrusted fork/branch code running in a privileged pipeline context) applies to any CI system that has an equivalent "run with elevated permissions on external contributions" trigger. The mitigation principle โ never execute untrusted code in a privileged context โ transfers directly.
Key takeaways
| Issue | Fix |
|---|---|
pull_request_target + fork PR checkout | Switch to pull_request if secrets aren't needed |
| Need secrets and fork PR content | Split into two workflows: untrusted build โ artifact โ privileged consumer |
| Confirmed-safe fork checkout | Explicit allow-unsafe-pr-checkout: true, reviewed deliberately |
| Deadline | Backport enforcement lands July 16, 2026 on all supported major versions |
Further Reading
- AWS Builder ID & GitHub Integration: Amazon Simplifies Cloud Sign-In (2026)
- AWS Security Checklist: Protecting Your Cloud Environment
- Enhancing AWS Security: A Comprehensive Guide to IAM Policies for Restricting Access by Source IP
- Amazon EKS Kubernetes Version Rollback: How to Undo a Bad Cluster Upgrade
- GitHub Changelog: Safer pull_request_target defaults for GitHub Actions checkout
- GitHub Docs: Securely using pull_request_target workflows