BYBOWU > Blog > Web development

Pull_request_target Is Changing: Your Dec 8 Plan

blog hero image
On December 8, 2025, GitHub will change how pull_request_target works and how environment branch protections are evaluated for PR events. If you ship with Actions, this isn’t a “nice to know” update—it can block deployments and silently bypass filters you thought were protecting secrets. Here’s the practical, 30‑minute triage plan I’m using with teams, plus safe migration patterns and testable examples. We’ll also flag adjacent deadlines (hello, macOS 13 retirement) so you don...
📅
Published
Nov 14, 2025
🏷️
Category
Web development
⏱️
Read Time
10 min

On December 8, 2025, GitHub will change how pull_request_target resolves workflow source and how environment branch protections are evaluated for pull‑request events. If you run CI/CD on Actions, you have a few weeks to adjust before approvals stop matching and jobs fail in confusing ways. Here’s the plain‑English version, what breaks, and a fast migration plan you can run today. (github.blog)

What exactly is changing in pull_request_target?

Two core shifts land on December 8:

First, pull_request_target always sources from the default branch. The workflow file and checkout commit used during pull_request_target will be taken from your repo’s default branch—no exceptions. Previously, GitHub could execute outdated workflow files from a non‑default base branch. After Dec 8, the ref and SHA align to the default branch so remediation happens in one place. (github.blog)

Second, environment branch protection rules evaluate against the executing ref, not the PR head. For the pull_request family, evaluation happens against the merge ref (for example, refs/pull/123/merge). For pull_request_target, evaluation happens against the default branch. If your environment filters were targeting feature branches (like release/*), they may no longer match. (github.blog)

Who’s at risk—and what breaks on Dec 8?

If you depend on pull_request_target for cross‑repo or fork PRs and you also rely on environment branch filtering to guard secrets, expect surprises. Typical symptoms: jobs suddenly show “no matching environments,” approvals disappear, or deployments skip with policy errors. Teams that pinned protections to release/* or similar will notice non‑matches because refs/pull/<id>/merge and the default branch don’t fit those patterns. (github.blog)

Security‑wise, this is the right direction: it closes known classes of issues where outdated workflows executed under pull_request_target. But it means you must verify every place you were counting on environment filters to arbitrate secrets for PR jobs. (github.blog)

30‑minute triage checklist

Block off half an hour. You’ll surface 80% of the issues before lunch.

  1. Find all workflows using pull_request_target. Search your org for on: [pull_request_target] and on: blocks with that event. Note every job that touches secrets, environments, deploys, or approvals. Pair this with a review of your reusable workflow callers. (github.blog)
  2. Map environment filters to the new evaluation refs. For pull_request, plan on refs/pull/*/merge. For pull_request_target, plan on your default branch name (often main). Update environment rules or split environments if needed. (github.blog)
  3. Check least privilege. Set permissions: contents: read by default and grant minimal write scopes per job. If you don’t need secrets, prefer pull_request over pull_request_target. (github.blog)
  4. Run a safe rehearsal. Open a draft PR from a fork (or a throwaway fork) and watch which environments attach. Confirm that approvals appear where expected and that no deployment leaks happen.
  5. Document who approves what. If environments move or split, ensure your approvers list follows the new targets rather than the old branch patterns.

Before/after patterns that survive Dec 8

Here are practical swaps that keep your PR flows safe without slowing developers to a crawl.

Replace risky PR automation with a workflow_run gate

For builds that only need compile/test on PRs, use pull_request. For anything that needs secrets or deploy rights, trigger a workflow_run after merge to default. It’s boring—and robust.

# Before: PR deploy using pull_request_target
name: pr-deploy
on: [pull_request_target]
jobs:
  deploy:
    permissions: write-all
    environment: preview
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy-preview.sh

# After: build on PR, deploy on merge to default
name: pr-build
on: [pull_request]
jobs:
  build:
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test
---
name: deploy-on-merge
on:
  workflow_run:
    workflows: ["pr-build"]
    types: ["completed"]
jobs:
  deploy:
    if: ${{ github.event.workflow_run.conclusion == 'success' && github.ref == 'refs/heads/main' }}
    environment: preview
    permissions:
      contents: read
      deployments: write
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy-preview.sh

This pattern avoids running deployments in PR context entirely, eliminating the thorny edge cases pull_request_target was meant to solve. (github.blog)

If you must keep pull_request_target, make it tight

Some checks—like applying repository labels or commenting with ephemeral data—may justify pull_request_target. When you can’t avoid it:

  • Use permissions with least‑privilege scopes; keep the default token read‑only unless a specific step needs write. (github.blog)
  • Don’t execute user‑controlled code. Avoid run steps that parse PR content or shell out with untrusted input.
  • Move deploy logic to a separate, post‑merge workflow or a dispatch that requires an approver.
  • Update environment branch filters to match the default branch for pull_request_target. (github.blog)

Adjust your environment patterns

Many orgs used environment branch rules like release/* or feature/*. After Dec 8, those won’t match PR events. Use patterns that explicitly include refs/pull/*/merge for pull_request, and your default branch for pull_request_target. Example:

# environment "preview"
allowed-branches:
  - refs/pull/*/merge   # PR builds
# environment "production"
allowed-branches:
  - refs/heads/main     # pull_request_target evaluation happens here

This mirrors the new evaluation semantics and prevents confusion when approvers don’t see prompts. (github.blog)

People also ask

Do I have to stop using pull_request_target?

No—but you should default to pull_request unless you have a real need for elevated permissions with forked PRs. If you keep pull_request_target, treat it like production code with strict permissions and no untrusted inputs. The Dec 8 change makes it safer by sourcing from the default branch, but not risk‑free. (github.blog)

Will my approvals disappear?

They might, if your environment branch filters don’t match the new refs. Add refs/pull/*/merge for pull_request jobs and the default branch for pull_request_target jobs. Test with a draft PR from a fork to be sure. (github.blog)

How does this interact with other November/December deadlines?

Two notable dates collide this season. First, the macOS 13 runner image retires on December 4, 2025, with brownouts already staged across November; migrate to macos-latest or specific 14/15 labels, preferably arm64 where possible. Second, image deprecations removed Node.js 18 and Ruby 3.1 from hosted runners starting early November on a rolling schedule—pin your required versions or upgrade. (github.blog)

Key dates, versions, and gotchas

  • Dec 8, 2025: pull_request_target and environment branch evaluation changes take effect. Validate environments and approvals before this date. (github.blog)
  • Dec 4, 2025: macOS 13 runner image retirement; multiple November brownouts were scheduled to nudge migration. If you still target macos-13, fix that now. (github.blog)
  • From Nov 3, 2025: runner image cleanups removed legacy toolchains (e.g., Node 18, Ruby 3.1, Android NDK 26; GCC 9/10 on Ubuntu 22.04). Expect failures if you implicitly relied on them. (github.com)

Gotchas I’ve seen this week: environment names reused across repos with different rules; reusable workflows that silently inherit stricter permissions; and branch filters defined in UI while developers edit YAML—causing mismatched expectations between code and policy. Do a side‑by‑side check.

Let’s get practical: a safe ref and checkout strategy

With the new semantics, I recommend checking out a known‑good ref explicitly in PR builds, and keeping deploy steps in post‑merge workflows. Example:

jobs:
  build:
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}
          fetch-depth: 1
      - run: npm ci && npm test -- --reporter=junit

This reads the PR head SHA for building and testing without granting deploy rights during the PR. Combine with a separate workflow_run deployment after a protected merge to default.

Policy hygiene for bigger organizations

If you manage dozens of repos, centralize the change. Use the Actions settings APIs to audit and update who can run forked PR workflows, artifact retention, and self‑hosted runner policies. This keeps policy in sync instead of repo‑by‑repo spelunking. (github.blog)

Also consider aligning CI/CD ownership with custom repository roles for Actions—separate who manages runners, environments, and secrets from general maintainers so a single misconfiguration can’t derail shipping. (github.blog)

What to do next (developers)

  • Search every repo for pull_request_target and annotate where secrets/environments are used.
  • Switch deploys to workflow_run on protected merges; keep PR builds on pull_request.
  • Update environment branch rules: add refs/pull/*/merge and ensure the default branch is included where needed. (github.blog)
  • Run a forked PR dress rehearsal and confirm approvals appear and secrets stay sealed.
  • Pin runner images and tool versions explicitly; don’t assume Node, Ruby, or NDK versions are present. (github.com)

What to do next (engineering managers & product owners)

  • Assign a named owner for Actions policy and environments across your org.
  • Schedule a 60‑minute “Dec 8 readiness” session this week with a live PR rehearsal.
  • Track macOS runner migration separately. If you still see macos-13 in your fleet, block time to move to 14/15 labels before Dec 4. (github.blog)
  • Budget one sprint hour for policy checks using the Actions settings APIs to keep repos aligned. (github.blog)

Related deep dives and hands‑on help

If you want a prescriptive walk‑through of the ref and environment changes, our piece on pull_request_target fixes by Dec 8 includes diffs you can paste into real pipelines. If you’re also racing the macOS deadline, see the macOS 13 deprecation guide. For a broader November roundup, use what to fix this week in GitHub Actions, then bring us in via services if you want us to audit and harden your org’s CI in one pass.

Diagram showing default branch as the trusted source for pull_request_target

Risk, limitations, and edge cases

Edge case one: monorepos with multiple release branches. Because pull_request_target always resolves to the default branch, branch‑specific workflows won’t execute under PR context anymore. Move those checks to post‑merge jobs or gate them behind dispatches approved by maintainers. (github.blog)

Edge case two: environments reused across repos. A shared “preview” environment with divergent branch rules causes inconsistent approvals after the change. Fork the environment or consolidate the policies centrally so the evaluation ref is predictable everywhere. (github.blog)

Edge case three: implicit toolchain assumptions. If your setup-node step didn’t pin a version, runner image updates may put you on a newer major overnight—Node 18 is gone from November images and Ruby 3.1 was dropped. Always pin. (github.com)

Developer adjusting environment branch protection rules in a CI dashboard

A quick migration diff you can copy

This trims risk while keeping contributor ergonomics intact.

# 1) Build and test on PRs without secrets
name: pr-checks
on: [pull_request]
permissions:
  contents: read
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci && npm run lint && npm test

# 2) Deploy after protected merge to default
name: deploy
on:
  workflow_run:
    workflows: ["pr-checks"]
    types: [completed]
jobs:
  deploy:
    if: ${{ github.event.workflow_run.conclusion == 'success' && github.ref == 'refs/heads/main' }}
    environment: production
    permissions:
      contents: read
      deployments: write
      id-token: write
    steps:
      - uses: actions/checkout@v4
      - run: ./infra/deploy.sh

Result: your PRs stay fast and useful; your secrets and deploys stay on trusted refs with approvals.

Zooming out

Between the Dec 8 semantics change and the Dec 4 macOS 13 retirement, GitHub is converging on a simpler security model: trusted refs, pinned tools, and explicit approvals. That’s good news for teams who’ve been juggling exceptions and duct‑tape workarounds. But you need to do the boring work now so you can ignore it later. If you’re short on time, start with the 30‑minute checklist, ship the obvious fixes, and schedule a deeper policy pass next week. (github.blog)

Written by Roman Sulzhyk · BYBOWU
3,929 views

Get in Touch

Ready to start your next project? Let's discuss how we can help bring your vision to life

Email Us

[email protected]

We'll respond within 24 hours

Call Us

+1 (602) 748-9530

Available Mon-Fri, 9AM-6PM

Live Chat

Start a conversation

Get instant answers

Visit Us

Phoenix, AZ / Spain / Ukraine

Digital Innovation Hub

Send us a message

Tell us about your project and we'll get back to you

💻
🎯
🚀
💎
🔥