GitHub Actions pull_request_target changes: Fix by Dec 8
On December 8, 2025, GitHub will roll out pull_request_target changes that alter what ref executes and how environments are checked. If you run workflows on pull requests—especially from forks—this affects you. This isn’t a cosmetic tweak; it closes long‑standing security gaps and can silently break environment rules and branch filters you’ve relied on for years. (github.blog)
Here’s the thing: the update makes pull_request_target always execute the workflow from the repository’s default branch and shifts environment branch protection evaluation to the executing ref. That’s the right security move, but it means some jobs may stop matching your existing branch patterns or start running with different assumptions. Let’s make sure you’re not finding this out in production on December 8. (github.blog)
What exactly is changing on December 8?
Two big items:
First, pull_request_target will always use the default branch as the workflow source and reference. Concretely, the workflow file and the checkout commit come from the default branch, GITHUB_REF resolves to that default branch, and GITHUB_SHA points to its latest commit—regardless of the PR’s base branch. This aligns ref semantics to the security model and prevents outdated workflows on non‑default branches from running with elevated permissions. (github.blog)
Second, environment branch protections now evaluate against the executing ref for PR‑related events. For pull_request, pull_request_review, and pull_request_review_comment, that means refs/pull/<number>/merge. For pull_request_target, it’s the default branch. If your environment branch rules were matching on the head or base branch names, expect mismatches after the change unless you update filters. (github.blog)
Should we stop using pull_request_target altogether?
Short answer: not necessarily—but use it with intent. pull_request_target exists for elevated scenarios (e.g., labeling, commenting, applying policies) where you need secrets or write permissions on PRs. If your job doesn’t require secrets or admin‑level operations, prefer pull_request. That keeps untrusted code further away from sensitive tokens and secrets, which is the real win. (github.blog)
When you do use pull_request_target, keep permissions minimal, avoid checking out untrusted code into the job context with secrets, and make risky steps require manual approvals or isolated environments. This change reduces certain attack paths, but it doesn’t eliminate the inherent risk in running on user‑supplied PRs with access to secrets. (github.blog)
Your pull_request_target changes checklist (ship before Dec 8)
Let’s get practical. Here’s a focused, one‑sitting checklist you can run with your team.
1) Inventory all PR‑related workflows
Search your org for workflows using pull_request_target or environments on PR events. Prioritize public repos and any repo that accepts external contributions.
2) Decide event strategies per workflow
For each job, ask: Do we actually need secrets or write permissions here? If no, switch to pull_request. If yes, keep pull_request_target but harden permissions and isolate risky steps (e.g., run policy checks on the fork’s code, but gate secret‑using steps behind environment approvals on the default branch).
3) Update environment branch rules
If you use environments on PR workflows, update branch patterns to reflect the new evaluation refs:
- For
pull_requestfamily events, add patterns that matchrefs/pull/*/merge. - For
pull_request_target, ensure the environment matches your default branch name.
Test that environment approvals still trigger when expected after this change. (github.blog)
4) Align permissions and tokens
Set the default token to read‑only at the org or repo level where possible, then grant least‑privilege at the job level with permissions:. If a job only needs contents: read and pull-requests: write, don’t give it more.
5) Double‑check checkouts and artifact flows
Because GITHUB_REF/GITHUB_SHA now point to the default branch for pull_request_target, confirm any actions/checkout usage and downstream steps still operate on the intended code. If you need to analyze the fork’s head commit, fetch it explicitly into a separate path and never run it with secrets in scope. (github.blog)
6) Rehearse with a forked PR
Open a test PR from a fork and verify: environment gates, required approvals, permissions, and artifact upload/download all behave as expected. Watch for jobs that unexpectedly stop matching environments or suddenly pick up default‑branch state.
7) Communicate the policy change
Tell maintainers and contributors about the new behavior. Document which events you use where, why, and what’s required for approvals. The goal: fewer surprises during review week.
How do environment branch protections evaluate now?
People also ask this one a lot. For clarity: after the change, PR events that run the merge commit evaluate against refs/pull/<number>/merge; pull_request_target evaluates against the default branch. If your environment previously allowed feature/* or branch‑name patterns, those won’t catch refs/pull/ paths. Adjust filters accordingly or move those checks to a separate job with a different trigger. (github.blog)
Do forks still run with secrets?
Only if you use pull_request_target and grant access via environments/permissions. That’s exactly why most teams limit pull_request_target to policy and automation tasks, and run builds/tests with pull_request on untrusted code. The Dec 8 change doesn’t alter that principle; it makes the behavior safer and more predictable. (github.blog)
What could break on Dec 8?
Common gotchas: environment filters no longer match because they were written for head/base branches; jobs that assumed GITHUB_REF pointed at the PR’s base branch; or scripts that implicitly used the workflow’s branch context to locate files. All of those now resolve against default branch for pull_request_target. (github.blog)
Other November Actions changes worth your attention
While you’re touching CI, knock out these fresh updates too. You’ll save a second fire drill later this month.
Reusable workflows: higher limits
You can now nest up to 10 reusable workflows and call up to 50 total workflows from a run (previously 4 nested and 20 calls). If you’ve been contorting around the old limits in platform repos, clean that up while you’re here. (github.blog)
M2 macOS runners are GA
M2‑powered macOS runners are now generally available under the macos-*-xlarge labels. If your iOS builds or Metal‑dependent workloads have been bounded on Intel, you may see markedly faster times moving to these labels—budget permitting. (github.blog)
Cache eviction: hourly enforcement
GitHub is enforcing cache eviction hourly (repositories still have 10 GB max). If your pipelines create lots of distinct cache keys in a day, expect more churn. Normalize keys and consolidate steps to reduce thrashing, or you’ll pay with flaky cache hits. (github.blog)
macOS 13 runners: final retirement and brownouts
macOS 13 runner images retire on December 4, 2025, with brownouts already scheduled across November. If any workflow still targets macos-13, migrate—to macos-15/macos-latest on arm64, or macos-15-intel if you must stay on x86_64. (github.blog)
Node 20 deprecation on runners (heads‑up)
GitHub started the Node 20 deprecation process for Actions. Node 24 becomes the default on March 4, 2026, with Node 20 fully removed later in summer 2026. Maintain actions and pin versions that support Node 24. (github.blog)
OIDC tokens now include check_run_id
OIDC token claims now include check_run_id, which helps with fine‑grained ABAC policies and auditing external service calls from jobs. If you gate internal APIs on workload identity, fold this claim into your policy model. (github.blog)
Framework: ref‑safe PR pipelines
Use this small framework to keep PR automation secure and predictable going forward.
1) Separate the concerns
Run build/test on pull_request against the merge commit; run labeling, policy, or repo‑write tasks on pull_request_target behind environments with approvals. Don’t mix them in one job if you can help it.
2) Treat the fork’s code as data
If you must analyze files from a fork, check them out to a temp path and never execute them in the same job that has secrets. Use tools that read/scan only.
3) Prefer least‑privilege, explicit permissions
Lock down GITHUB_TOKEN defaults and grant per‑job permissions explicitly. Rotate long‑lived cred usage to OIDC where possible and validate token claims like repository, actor, run_id, and now check_run_id before authorizing.
4) Make environments do real work
Use environments for secret scoping and manual approvals. After Dec 8, confirm your filters match refs/pull/*/merge or the default branch per event.
5) Bake in policy tests
Add CI that fails a PR if a workflow uses pull_request_target without environment gates or uses blanket permissions: write-all.
Data and dates to keep handy
- Dec 8, 2025:
pull_request_targetand environment branch protection changes take effect. (github.blog) - Nov 2025: cache eviction enforcement moves to hourly; repos still capped at 10 GB. (github.blog)
- Nov 4/11/18/25 brownouts; macOS 13 retires Dec 4, 2025. (github.blog)
- Nov 6, 2025: reusable workflows limits increased to 10 nested and 50 total calls. (github.blog)
- Mar 4, 2026: Node 24 becomes default on runners; Node 20 removed later summer 2026. (github.blog)
Edge cases and gotchas I’ve seen
Monorepos with multiple default‑like branches. Teams sometimes run “release/*” as de facto defaults for backports. After Dec 8, pull_request_target will use the actual default branch. If you depended on release‑branch workflows, you’ll need to restructure or move logic to reusable workflows keyed by events. (github.blog)
Implicit path lookups. Scripts that assumed the running ref pointed at the base branch may suddenly read the wrong config. Make those paths explicit or pass configuration as inputs.
Environment filters on legacy patterns. Anything matching feature/* will not match refs/pull/*/merge. Update to the new ref pattern or use a different gate.
Cache thrash after hourly eviction. Workflows that generate high‑cardinality cache keys (commit‑scoped, platform‑scoped, matrix‑scoped) may churn. Normalize keys and cull caches proactively. (github.blog)
macOS 13 pinning. A surprising number of iOS pipelines still pin macos-13. Expect outright failures during brownouts; move now, and consider M2 runners for speed. (github.blog)
What to do next (this week)
- Audit all workflows for
pull_request_targetand environments; switch non‑privileged jobs topull_request. - Update environment filters to
refs/pull/*/mergeor your default branch depending on the event. - Lock down
permissions:per job; favor read‑only defaults. - Rehearse with a forked PR and verify approvals, secrets exposure, and artifact paths.
- Clean up CI debt with the new reusable‑workflow limits; review cache keys before hourly eviction lands everywhere.
- Unpin
macos-13; evaluate M2 runners; note Node 24 timeline for action maintainers.
If you want a quick companion playbook, see our focused guide on what to fix for pull_request_target right now, plus our running list in GitHub Actions: what to fix this week. If you’re also juggling Apple builds, read our notes on the macOS 13 runner deprecation. And if you just need a team to ship the changes, talk to us.
Zooming out
GitHub’s tightening of PR semantics is overdue. Security incidents taught us that untrusted PRs plus secrets plus permissive defaults is a combustible mix. By anchoring pull_request_target to the default branch and rekeying environment checks to the executing ref, GitHub reduces subtle footguns while preserving the power of privileged automation. It’s our job to make sure our workflows reflect that intent—cleanly, explicitly, and before December 8. (github.blog)
When you’re done, capture the rules in your platform repo: which events are allowed for what, which environments guard which secrets, and how forks are handled. Your future self—and your on‑call engineer—will thank you.