Dec 8: Fix GitHub Actions pull_request_target Now
On December 8, GitHub changes how GitHub Actions pull_request_target resolves its workflow source and reference, and how environment branch protections apply to pull‑request events. Teams that label PRs from forks, run triage automations, or gate PR workflows with environments will see behavior changes—some welcome, some breaking—unless you adjust. (github.blog)
What’s changing on December 8 (and why now)
Two things flip at the same time. First, pull_request_target will always source the workflow and its ref from your repository’s default branch—no matter which branch a PR targets. In practice, that means GITHUB_REF points to the default branch and GITHUB_SHA resolves to the latest commit on that branch. Second, environment branch protection rules for pull‑request events will evaluate against the execution ref, not the PR’s head. For the pull_request family, that’s refs/pull/<number>/merge; for pull_request_target, it’s the default branch. (github.blog)
Why the change? The old behavior let outdated workflows on non‑default branches run with elevated permissions. Attackers have abused that class of edge cases, especially when maintainers accidentally executed untrusted code under pull_request_target. Pinning execution to the default branch and aligning policy evaluation to the executing ref lowers that risk. (github.blog)
How the new GitHub Actions pull_request_target semantics affect you
Here’s the thing: many teams used pull_request_target for convenience—labels, PR comments, a quick gh command, auto‑assign reviewers—and sometimes to unlock secrets for preview deployments. After Dec 8, those jobs still run, but their ref context changes. Your workflow file and checkout ref now come from the default branch, not a release branch or feature branch that the PR happens to target. If you carry branch‑specific workflow logic outside default, it won’t be read anymore during pull_request_target runs. (github.blog)
On the policy side, environments that were previously gated by branch patterns tied to PR heads won’t match the same way. For pull_request and related review events, environment rules evaluate against refs/pull/<number>/merge. For pull_request_target, they evaluate against your default branch. That can silently bypass or over‑match environment filters if you don’t update them. (github.blog)
Will my environment branch protections still work?
Short answer: only if they’re written for the new refs. Environments that used patterns like branches: [release/*] on PR events will not match refs/pull/<number>/merge without explicit updates. And if you intended to restrict pull_request_target jobs to certain release branches, those checks now evaluate against the default branch instead. Adjust filters or move the gate earlier (for example, via rulesets or required checks) to preserve intent. (github.blog)
When should I use pull_request_target versus pull_request?
Use pull_request if you don’t need secrets or write privileges—it runs untrusted PR code with a read‑only token and no secrets, which dramatically reduces blast radius. Reserve pull_request_target for workflows that must comment, label, or otherwise write back to the repo, and never execute untrusted PR code in that context. Keep permissions minimal, fetch only trusted sources, and treat all PR inputs as untrusted. (docs.github.com)
90‑minute cutover checklist
If you start from a typical org setup—some PR automations, some preview deployments—this plan gets you compliant fast:
- Inventory usage (15 minutes). Search for
on: pull_request_targetacross repos. Note any jobs that use environments or secrets. Tip: use your code search or a quickghscript to list workflow files by trigger. (docs.github.com) - Lock down permissions (10 minutes). In each
pull_request_targetworkflow, set an explicitpermissionsblock—ideallycontents: readand only the one or two scopes you need (e.g.,pull-requests: writefor labelers). Avoidwrite-all. (github.blog) - Stop executing PR code (10 minutes). Replace
actions/checkoutof the PR head inpull_request_targetjobs. If you must test the PR code, do it in apull_requestjob without secrets, or gate a separate job that runs post‑approval. (github.blog) - Update environment filters (15 minutes). For PR workflows using environments, change branch filters to match the new refs. Use
refs/pull/*/mergeforpull_requestand your default branch name forpull_request_target. Validate by opening a draft PR from a fork. (github.blog) - Add a guardrail rule (10 minutes). Create a repository ruleset that fails CI when a
pull_request_targetworkflow includes disallowed steps (for example, shelling out to build the PR head) or a missingpermissionsblock. It’s not perfect, but it catches the worst mistakes. (docs.github.com) - Dry‑run on a test PR (15 minutes). From a fork, open a PR that trips your automations—labeling, triage comments, maybe a docs‑only change. Verify the workflow used your default branch, that environment gates behaved, and that secrets weren’t exposed where they shouldn’t be. (github.blog)
- Document and merge (5 minutes). Add a short note to the repo’s
SECURITY.mdorCONTRIBUTING.mdexplaining the policy: “PR checks run with read‑only permissions; privileged automations usepull_request_targetbut never execute PR code.” (github.blog)
Concrete examples: before and after
Before (risky): pull_request_target workflow that checks out the PR head to run a script and then labels the PR. This mixes untrusted execution with write permissions and secrets exposure.
After (safer): Split into two jobs. Job A triggers on pull_request, checks out the PR head, runs tests with a read‑only token and no secrets, and produces artifacts. Job B triggers on pull_request_target, never checks out the PR head, and only labels or comments based on metadata. Environment gates evaluate against the executing ref: refs/pull/<number>/merge for job A; the default branch for job B. (docs.github.com)
People also ask: Why is my deploy environment no longer matching?
Because after Dec 8, GitHub evaluates environment branch filters against the execution ref, not the PR head. If your environment expected release/* (a branch name), it won’t match refs/pull/<number>/merge. Update the filter to match the PR merge ref or trigger your deployment from a different event. (github.blog)
People also ask: Do I still need pull_request_target for labelers?
Often, yes—adding labels typically requires write permission on pull requests. But run that labeler with least privilege, don’t check out PR code in that job, and keep a tight permissions block. The official labeler docs also warn about pull_request_target pitfalls when updating versions. (github.com)
Common failure modes to watch for
Environment filters that stop firing. If you tied deploy‑preview secrets to feature/*, those won’t match refs/pull/<number>/merge. Fix the patterns or switch to rulesets that key off labels or required checks instead of branch names. (github.blog)
Monorepos using release branches as “defaults.” Some teams treat release/2025.11 like a default branch. After Dec 8, pull_request_target will read from the actual default branch only. Move shared workflow logic to default or into reusable workflows called by both. (github.blog)
Workflows that quietly gain or lose access. With environment evaluation shifting to execution refs, a job might suddenly gain access to a secreted environment if your default branch filter is too broad, or lose access if it never matches the PR merge ref. Audit environments with a test PR today. (github.blog)
Cache poisoning and secret exfiltration paths. If a privileged PR workflow executes PR code—or consumes attacker‑controlled inputs in shell steps—you can still get burned. Default‑branch sourcing helps, but you must also refuse to run untrusted code in pull_request_target. (github.blog)
Related changes shipping in November (don’t ignore these)
Reusable workflow limits increased. You can now nest up to 10 reusable workflows and call up to 50 in a run, making it easier to centralize policy checks as you refactor. Use this to pull risky logic out of PR‑privileged jobs. (github.blog)
macOS 13 retirement and brownouts. If you ship Apple builds, the macos-13 runner retires on December 4, 2025, with scheduled brownouts on November 11, 18, and 25 (14:00–00:00 UTC). Migrate labels to macOS 14/15 and test arm64. (github.blog)
Actions cache policy enforcement. GitHub moved cache eviction from daily to hourly. High‑cardinality keys can start thrashing; normalize your cache keys before you notice flaky rebuilds. (github.blog)
If you want a deeper playbook for the broader runner and policy changes, we’ve been writing hands‑on guides you can reuse across repos: see our Dec 8 survival guide, the step‑by‑step cutover plan, and our note on npm token migration if your release job publishes to npm.
A pragmatic framework for safe PR automation
Use this three‑layer model to keep PR checks helpful without becoming a security risk:
- Layer 1: Untrusted checks. Trigger on
pull_request. Build and test the PR head with a read‑only token and zero secrets. Publish artifacts for later jobs. Enforce required checks on this layer. (docs.github.com) - Layer 2: Trusted automations. Trigger on
pull_request_target. Never check out the PR head. Perform repo‑write actions only—labeling, commenting, or running tiny bots—under least privilege. (github.blog) - Layer 3: Deployment gates. Use environments and rulesets to gate any job that touches secrets or deploys. After Dec 8, ensure the branch filters reflect execution refs for PR events. Consider manual approvals or custom protection apps for high‑risk paths. (docs.github.com)
Data and dates you’ll be asked about
• Dec 8, 2025: pull_request_target sources from default branch; environment rules evaluate against execution refs for PR events. (github.blog)
• Nov 6, 2025: New limits for reusable workflows—10 nested, 50 total calls. (github.blog)
• Dec 4, 2025: macOS 13 runner image retired; brownouts on Nov 11, 18, 25 from 14:00–00:00 UTC. (github.blog)
What to do next
Developers: update patterns and PR jobs this week. Then open a forked PR to verify the new behavior. Keep the risky bits under pull_request, and keep pull_request_target boring and permission‑minimal.
Engineering managers: add a lightweight policy check to flag dangerous pull_request_target usage, templatize safe workflows as reusable components, and schedule brownout drills for macOS runner changes.
Security/platform teams: audit environment filters, codify least‑privilege permissions in org‑level defaults, and enable CodeQL scanning for workflow code. (github.blog)
Further reading and rollout helpers
If you want a deeper dive before you flip the switch, we’ve published several focused guides you can copy into your team chats: the fast “fix it” rundown in Dec 8 Deadline: Fix pull_request_target, a practical PR automation hardening checklist, and the scenario‑based cutover scenarios article with real examples.
Bottom line
This isn’t a cosmetic tweak. It’s a security hardening that changes ref semantics and policy evaluation on PR events. If you handle it now, your PR automations get safer and your secrets stay where they belong. If you ignore it until December 8, you’ll chase confusing environment mismatches and risky defaults during a production day. Make pull_request_target boring, keep PR code untrusted, and use environments and rulesets that match the refs that will actually execute.