On December 8, 2025, GitHub changes how GitHub Actions pull_request_target evaluates workflow source and refs, and how environments enforce branch protections for pull‑request events. If you run CI on public forks, gate deployments with environments, or rely on branch patterns for approvals, this update touches you. The fixes aren’t hard, but you do need to be precise—especially if you’ve mixed pull_request_target with checkouts of PR code or depend on environment filters.
What exactly changes on December 8, 2025?
Two coupled updates land the same day:
1) pull_request_target always runs from the default branch. After the change, the workflow file and the checkout ref GitHub uses for GITHUB_REF and GITHUB_SHA will come from your repository’s default branch—regardless of the PR’s base branch. Historically, some teams used backport or release branches as the base; those workflows will no longer be sourced from those branches. Security‑wise, this closes classes of exploits where outdated workflow files on non‑default branches were executed.
2) Environment branch protection rules evaluate against the execution ref. For pull_request family events, environments evaluate against refs/pull/<number>/merge. For pull_request_target, environments evaluate against the default branch. If you filtered environments with patterns like release/* and expected PR jobs to match, you’ll need to adjust.
Why this matters beyond a minor ref tweak
Here’s the thing: lots of pipelines quietly depend on the old semantics. I’ve reviewed repos where a single release/10.x base branch carried a slightly different workflow (paths filters, cache strategy, even different build scripts). On December 8, the running workflow will be pulled from main instead. In the best case, your build logic behaves differently; in the worst case, a deployment environment no longer grants secrets because the ref no longer matches the environment’s branch filter.
There’s also the security angle. pull_request_target gives read/write token permissions and access to secrets by default unless you lock it down. That’s why checking out and executing untrusted PR code in a pull_request_target workflow is a classic footgun. The new default‑branch behavior helps, but it doesn’t eliminate the core risk of combining privileged tokens with attacker‑controlled inputs.
Primary impacts you’ll see on day one
Based on audits we’ve done this month, expect the following hot spots:
- Environments stop matching: Branch filters like
releases/*won’t matchrefs/pull/<number>/merge. Jobs referencing environments may queue forever waiting for an approval that never appears, or simply skip environment secrets if your gates are misconfigured. - Monorepo backport workflows flip source: Any PR targeting
release/*will still run, but the workflow file andGITHUB_REFwill come frommain. If you customized CI per branch, those customizations won’t apply. - Audits and cache keys drift: Scripts that computed paths or cache keys assuming the base branch ref now see different values. If you switch architectures (Intel to Apple Silicon) or labels at the same time, cache thrash multiplies.
- PR labeling/commenting still fine—but builds aren’t: The safe use of
pull_request_targetis metadata operations (labels, comments). Building PR code there remains risky; keep builds inpull_requestand hand results to a privilegedworkflow_run.
Use the right tool: When to keep or drop pull_request_target
Ask one question: do you need write permissions or environment secrets during the PR event itself? If not, don’t use pull_request_target. Use pull_request with the default read‑only token. If yes, isolate untrusted code from privileges:
- Pattern A (recommended):
pull_requestruns build/test with no secrets, uploads artifacts; a separateworkflow_runworkflow (privileged) reads artifacts and posts results or deploys. - Pattern B (gated): Keep
pull_request_targetfor labels/comments only. If you must touch code, require a manual “safe to test” label and nevercheckoutthe head commit with credentials persisted.
Remediation checklist you can finish this week
Print this and work down the list. You don’t need a committee meeting to ship these.
- Inventory usage. Search your org for
on: pull_request_target. Flag any job that checks outgithub.event.pull_request.head.shaor reads untrusted files. - Reduce permissions. In affected workflows, set
permissions: contents: readby default and explicitly enable only what you need per job. Avoid blanketwrite-all. - Split privileges. Move builds to
pull_request; create a privilegedworkflow_runthat comments, updates statuses, or deploys from artifacts. - Fix environment rules. For PR jobs, update environment branch filters to match
refs/pull/*/merge. Forpull_request_target, add your default branch to the allowed rules. - Lock checkouts. If you must use
pull_request_target, setactions/checkoutwithpersist-credentials: falseand never checkout the untrusted head with secrets loaded. - Add policy tests. Add a CI check that fails if a workflow uses
pull_request_targetand requests elevated permissions or has no environment gates. It’s cheap insurance. - Audit caches. Normalize cache keys and proactively delete stale entries. GitHub is enforcing more frequent cache eviction; high‑cardinality keys will churn.
- Enable CodeQL on workflows. It now flags common Actions anti‑patterns. Turn it on for the repo and org. It’s free for public repos.
- Tighten OIDC policies. OIDC tokens now include
check_run_id, which makes it easier to write attribute‑based policies per job. Use it to scope cloud roles. - Document the decision. Add a short
SECURITY.mdsection explaining why you use—or don’t use—pull_request_target. Future‑you will thank you.
FAQ: the “People also ask” version
Should my team stop using pull_request_target entirely?
No. Keep it for PR metadata operations that need privileges (labels, comments, backport automation) and for internal contributors where risk is lower. Don’t build or run untrusted PR code under pull_request_target. If you need to post build results, switch to the pull_request → workflow_run pattern.
What happens to GITHUB_REF and GITHUB_SHA after the change?
For pull_request_target, both effectively point at the latest commit on your default branch. For the pull_request family, the environment gate evaluates against refs/pull/<number>/merge, which is a synthetic merge ref used during checks. If you assumed GITHUB_REF matched the PR base branch name, you’ll see drift.
Our environment gate is set to “Selected branches: release/*.” Will PR deployments still pass?
Not unless you add a matching pattern for refs/pull/*/merge, or you move the deployment decision to the privileged workflow that runs later. For pull_request_target, add your default branch to the environment’s allowed list.
Is a label like “safe to test” enough?
It’s better than nothing, but it’s not foolproof. Attackers can push new commits after labeling, and humans make mistakes. Use it only as a temporary gate, and never with persisted credentials.
How do forks affect secrets?
Workflows triggered by pull_request from forks run with a read‑only token and no repository or environment secrets. That’s by design. If you promote results (coverage, lint) back to the PR, do it in a separate privileged workflow that reads artifacts.
Concrete examples: safe patterns to copy
Example: build on pull_request, comment via workflow_run. The PR build runs without secrets and uploads a coverage summary as an artifact. A second workflow, triggered by workflow_run on success, reads the artifact and posts a comment with a privileged token. The PR never receives secrets, and you keep the ability to post helpful feedback.
Example: environment‑gated deploy preview. A PR job builds a preview image, uploads it, and marks the artifact with a ref. A privileged workflow, scoped by environment reviewers and branch rules that include the default branch and refs/pull/*/merge, deploys the preview only after manual approval.
Adjacent deadlines you should handle in the same sprint
There’s a cluster of CI changes around the same window. Bundle fixes to minimize churn:
- macOS 13 runner retirement: Brownouts hit November 11, 18, and 25 (14:00–00:00 UTC), and the image is retired December 4, 2025. Migrate to
macos-15ormacos-latest; anmacos-15-intellabel exists if you still need x86_64. - npm classic tokens end November 19, 2025: New classic tokens are already blocked; remaining ones are revoked on the 19th. Move to granular access tokens with write permissions gated by 2FA, or your publishes and provenance steps will start failing.
- Node 24 becomes default on Actions runners March 4, 2026: Update actions and self‑hosted runners that hard‑pin Node 20 or depend on older OS/arch combos.
If you want a guided plan, our team’s walk‑through covers the moving pieces, from runner labels to reusable workflows. Start with our step‑by‑step Dec 8 Playbook and keep the changes small and observable.
A practical, 2‑hour refactor plan
When time is tight, this sequence gets the biggest risk reduction fast:
- Kill dangerous checkouts: In every
pull_request_targetworkflow, remove any checkout of the PR’s head or setpersist-credentials: falseand avoid running package managers or build scripts. - Split the workflow: Move build/test to
pull_request. Add aworkflow_runthat posts comments or updates statuses. Verify you can delete the old privileged build job. - Update environments: Add your default branch and
refs/pull/*/mergeto allowed refs, or switch the decision to the privileged workflow entirely. Add one “required reviewer” to throttled deploy previews. - Lock permissions: Set
permissions: contents: readat the top and grant least privilege per job (e.g.,pull-requests: writeonly where you comment). - Enable CodeQL for workflows: Turn it on and fix the top issues it flags in your actions.
- Tighten OIDC trust: Add
check_run_idto your attribute‑based policies so cloud roles are bound per job, not per repo. - Ship a safety test: Add a guard job that fails if
pull_request_targetappears with broad write permissions or without an environment.
Need a second set of eyes? We help teams land these changes quickly and safely. See how we run CI/CD security reviews or talk to us about a 1‑day remediation sprint.
Common edge cases and how to handle them
Backport branches as “default.” Some monorepos treat release/* as de facto defaults for long‑lived LTS lines. After Dec 8, pull_request_target always uses the actual default branch for source and ref. Move branch‑specific logic into reusable workflows and call them conditionally from the default‑branch workflow.
Path‑based config discovery. Teams that derive paths or tenant config from GITHUB_REF will start reading from the wrong branch. Pass configuration as typed inputs or resolve explicitly from github.event.pull_request.base.ref when you need the PR’s base for metadata—not as an execution ref.
Cache poisoning and scope bleed. Remember that caches in pull_request_target share scope with the base branch. Don’t save caches when inputs are untrusted. Prefer pull_request and segregate caches per job matrix key.
Self‑hosted runners. If you rely on pull_request_target to run jobs that touch internal networks, double‑isolate. Use ephemeral runners, pin runner versions, and keep the privileged work in workflow_run after artifacts are sanitized.
What to do next
- Today: Find every
pull_request_targetworkflow; downgrade permissions; remove untrusted checkouts. - Within 48 hours: Split builds to
pull_requestand wire aworkflow_runfor comments/status. Fix environment rules and test with a PR from a fork. - This week: Turn on CodeQL for workflows, tighten OIDC policies using the new claims, and delete unused environment secrets.
- This month: Migrate macOS 13 runners and finish the npm token migration before Nov 19 so CI releases don’t fail unexpectedly.
Want a deeper, hands‑on guide to the December changes with copy‑paste snippets? Read our focused explainer: Fix it by Dec 8. And if you’re juggling .NET and mobile pipelines too, our blog keeps a running checklist of platform deadlines that hit build systems.
If you only remember three things
First, pull_request_target now always runs from the default branch—so update any logic that depended on other base branches. Second, environments evaluate PR jobs against the execution ref (refs/pull/*/merge or the default branch). Third, never build untrusted PR code with privileges. Keep privileges and untrusted code in different workflows, and keep your permissions tight.
If you’d like help shipping the fixes across many repos in a day, we do that. Start with a quick note via our contact form and we’ll tailor a short engagement to land the changes safely.
