GitHub Actions pull_request_target changes are arriving on December 8, 2025. Two things shift at once: 1) pull_request_target will always source workflow code from your default branch, and 2) environment branch protection rules will evaluate against the execution ref used by each pull request event (for pull_request it’s the PR merge ref; for pull_request_target it’s your default branch). If your workflows depend on current behavior, this will change what matches, what runs, and which secrets are exposed.
What changed—and why now
Starting December 8, 2025, GitHub locks pull_request_target to the default branch for both workflow source and its ref context. In practice, that means:
- The checked-out workflow and GITHUB_SHA come from the latest commit on your default branch, not from whatever PR base branch you targeted.
- GITHUB_REF resolves to the default branch during pull_request_target runs.
- Environment branch protection rules now evaluate the event’s execution ref. For pull_request and related review events, that’s refs/pull/<number>/merge; for pull_request_target, it’s the default branch.
Here’s the thing: this closes a family of exploits where outdated workflows on non-default branches or untrusted PR content could influence privileged jobs. It also means some environment filters and rulesets that used to match head/base branch names won’t match after December 8 unless you update them.
Why developers should care
If you use pull_request_target to triage forks, label PRs, run lightweight checks, or unlock limited secrets, the new behavior is a security win. But if you relied on environment branch filters (for example, a protected “deploy-staging” environment set to branches: [release/*]) to guard secrets on PR-triggered jobs, your filters may no longer match. With pull_request_target evaluating against the default branch, that deploy environment could suddenly match more broadly—or not at all—depending on how you wrote the patterns.
Security teams have long warned that mixing pull_request_target with a checkout of the PR head and executing scripts can allow code execution or secrets exfiltration in a privileged context. The new defaults reduce that risk by enforcing a trusted workflow source and aligning ref semantics with how secrets and environments are gated. You still need to remove dangerous patterns, but your floor is higher.
TL;DR: a 60‑minute audit you can run today
Block one focused hour. Grab org owner or repo admin access. Work through this list.
1) Find every workflow using pull_request_target
- Search repos for “on: pull_request_target”. Prioritize public repos and any that accept forks.
- Inventory jobs that: check out the PR head, run package managers, or run arbitrary scripts influenced by PR content.
2) Remove dangerous patterns
- Avoid checking out the PR head then running installers or build scripts. If you must inspect PR content, treat it as data only: diff, grep, parse metadata, never execute.
- Prefer permissions: read-all at the workflow level and explicitly elevate on a per-job basis as needed.
- Consider label-gating with a manual trust signal (for example, “safe to test”) before running anything that touches the PR head.
3) Update environment branch filters
- For jobs triggered by pull_request and its review/comment variants, add a pattern to match refs/pull/*/merge if you want those jobs to still unlock environment secrets.
- For jobs triggered by pull_request_target, add your default branch to the environment’s allowed branches. Otherwise, those jobs might stop unlocking secrets after December 8.
4) Switch triggers where possible
- If a job doesn’t need elevated privileges or secrets, use pull_request instead of pull_request_target.
- For privileged operations, break the flow into two steps: a safe pull_request job that writes minimal metadata, and a workflow_run job (triggered by the first job) that runs in a trusted context.
5) Turn on free scanning where it helps
- Enable CodeQL code scanning for Actions workflows (it’s free for public repos) and enable secret scanning. Both catch common misconfigurations early.
What exactly will break on December 8?
- Environments with branch filters expecting head/base branch names for PR events may stop matching because evaluation now uses refs/pull/<number>/merge for pull_request or the default branch for pull_request_target.
- Workflows that assumed GITHUB_REF or GITHUB_SHA pointed at the PR base branch (other than default) will see values from the default branch for pull_request_target.
- A PR against a maintenance branch (for example, release/1.2) will still execute pull_request_target from default. If you depended on old behavior to run an older workflow version, that’s gone.
Examples: before/after patterns that matter
Safe “no checkout” triage with pull_request_target
Don’t execute PR code. Keep tokens minimal. Example:
name: PR triage (safe)
on:
pull_request_target:
types: [opened, reopened, synchronize, labeled]
permissions:
contents: read
pull-requests: write
jobs:
label:
runs-on: ubuntu-latest
steps:
- name: Apply labels based on title
uses: actions-ecosystem/action-add-labels@v1
with:
labels: >-
needs-triage
Key points: no checkout, no running PR scripts, minimal permissions. This survives the December change because the workflow always comes from default anyway.
Label‑gated test of PR code without secrets
If you must run PR code, gate it with a label and never expose secrets:
name: PR tests (untrusted)
on:
pull_request:
types: [labeled, synchronize]
permissions:
contents: read
jobs:
test:
if: contains(github.event.pull_request.labels.*.name, 'safe to test')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
persist-credentials: false
- run: npm ci --ignore-scripts
- run: npm test
Because this uses pull_request, it doesn’t run with repository secrets by default and evaluates environment rules against the PR merge ref. If you need environments later, wire them via a follow‑up workflow_run job.
Environment branch filters that keep working
Adjust your environment to include the correct refs:
Environment: deploy-staging
Allowed branches:
- refs/pull/*/merge # for pull_request jobs
- main # for pull_request_target jobs
If you use repository rulesets to require a successful workflow, review them too. Rules that previously relied on pull_request_target behavior may need to include or exclude specific event types.
People also ask: Should I stop using pull_request_target entirely?
No. Use it for what it’s good at: commenting on PRs, applying labels, updating check runs, lightweight validations that need elevated permissions but never execute untrusted code. If you’re doing builds, tests, or package installs, move that to pull_request and keep secrets out of the picture.
How do I know if I’ve got risky patterns now?
Look for these red flags:
- checkout of the PR head inside pull_request_target, followed by npm install, pip install, make, or any script execution.
- use of extra long‑lived personal access tokens or custom secrets in pull_request_target jobs.
- permissive permissions: write-all on the GITHUB_TOKEN at the workflow level.
Replace them with: “no checkout” triage patterns, label gates, least privilege, and two‑phase pipelines (pull_request plus workflow_run for anything privileged).
Step‑by‑step: update environment rules before December 8
In each affected repo:
1) Go to Settings → Environments → select your environment (for example, deploy-staging).
2) In “Deployment branches”, update the allowed branches to include refs/pull/*/merge for pull_request and your default branch for pull_request_target.
3) If you use branch name patterns like release/*, remember those won’t match PR events anymore. Use refs/pull/*/merge for PR workflows and keep your release/* pattern for push/merge deployments.
4) If you’re using organization rulesets that require workflow checks, confirm they support the triggers you rely on and that they don’t implicitly force pull_request_target where it isn’t needed.
A five‑day migration plan you can hand your team
Day 1: Discovery and risk sort
- Export a list of repositories using pull_request_target. Tag public repos and high‑traffic projects as P0.
- Identify environments with branch filters and any repo rulesets that reference PR checks.
Day 2: Fix the P0s
- Remove “checkout + install” patterns from pull_request_target.
- Convert any feasible triage tasks to pull_request with minimal permissions.
- Enable CodeQL scanning on public repos.
Day 3: Environment alignment
- Update environment allowed branches across P0 and P1 repos.
- Where you deploy from PRs, decide if you truly need environment secrets at PR time or only after merge. If after merge, remove environment coupling from PR jobs.
Day 4: Rulesets and gates
- Review org/repo rulesets. Ensure required checks match your new triggers.
- Add label‑gates where maintainers need manual approval to run heavier tests on forked PRs.
Day 5: Verification and guardrails
- Run trial PRs from forks to verify labels and checks behave as expected.
- Document patterns in a CONTRIBUTING.md so external contributors know how to trigger tests.
Real‑world scenario: monorepo with multiple release lines
Say you maintain a monorepo with a default branch main and long‑lived branches release/1.2 and release/1.3. You accept forks and run triage on pull_request_target today. After December 8, the workflow source will always come from main. That’s fine for triage tasks. But if you depended on a workflow on release/1.2 for a PR targeting that branch, it will no longer run. Update your PR workflows to pull_request (for tests) and keep release workflows on push/merge to release/* with environments that match those branch patterns. Your deploy environments should not unlock on PRs anyway—move deploy previews to temporary, secret‑less infrastructure if needed.
Gotchas and edge cases
- Self‑hosted runners: if you allow PRs to target jobs on self‑hosted runners, treat them like production. Lock down runner groups, use ephemeral runners when possible, and never mount secrets for PR code.
- Composite actions: a composite action may run with the caller’s permissions. Audit what those actions do with inputs that originate from PR content.
- Caches: increasing cache eviction frequency across organizations can expose flaky assumptions. Keep PR caches scoped narrowly with explicit keys; do not reuse deploy caches for PR jobs.
Related deadlines you shouldn’t ignore
Many teams are juggling this change alongside other end‑of‑year CI/CD work. If you’re also pushing packages, the npm classic token shutdown around mid‑November 2025 forces you to rotate to granular access tokens and re‑wire CI tokens with shorter lifetimes. If that’s you, read our CI/CD playbooks to avoid breakage while you update Actions workflows to the new model: see our combined npm + Actions guide and the detailed npm token migration checklist.
Want a deeper, prescriptive cutover plan?
We published a complete cutover playbook that maps patterns repo by repo and includes ready‑to‑paste YAML snippets for label‑gating, two‑phase pipelines, and environment rule updates. Start here: The Last‑Mile Cutover Guide. For background and rationale, see our overview of the pull_request_target change. If you’re modernizing runners at the same time, factor in recent image deprecations—we covered a concrete example in our macOS 13 runner end‑of‑life note.
What to do next
- Update environment allowed branches for PR workflows (add refs/pull/*/merge) and for pull_request_target (add your default branch).
- Migrate any build/test steps out of pull_request_target. Use pull_request with minimal permissions, or two‑phase with workflow_run.
- Turn on CodeQL and secret scanning. Fix anything it flags in workflow YAMLs or composite actions.
- Document your trust model for forked PRs, including label‑gates and manual approval steps.
- Schedule a verification window before December 8 to run forked PRs against critical repos and confirm expected behavior.
If you’d rather have a team run this for you end‑to‑end—policy, YAML, rulesets, and env filters—our engineers can help. See what we do and get in touch.
FAQ quick hits
Can I keep using pull_request_target for title linting and label bots?
Yes. Keep it lightweight, no checkout of PR head, and restrict permissions.
Do I need to rewrite all my rulesets?
Probably not. Focus on rules that require workflows on PRs to pass before merge. Make sure they recognize the right triggers and merge ref.
Will this slow down my pipelines?
Not inherently. The bigger impact is on when secrets unlock. If you were doing risky PR‑time deployments, move them to post‑merge or use ephemeral, secret‑free preview infra.
December 8 is a firm date. Do the one‑hour audit, fix the P0s, then iterate. You’ll come out the other side with safer defaults and less brittle PR automation.
