pull_request_target blocked by default from 2 Dec 2026: fix your workflows

Public-repo pull_request_target workflows fail from 2 Dec 2026 unless a policy allows them. Audit them before the 16 Nov brownout.

Read time
19 min
Word count
2.4K
Sections
11
FAQs
8
Share
Two engineers at a desk with a laptop before a Release Plan board, with GitHub Actions workflow changes title text
On this page · 11 sections
  1. What changes on 2 December 2026
  2. Why GitHub is blocking it: the pwn request
  3. Step 1: find every affected workflow
  4. Step 2: triage each workflow
  5. Step 3: replace the trigger with safe patterns
  6. Step 4: allow what must stay with a scoped event policy
  7. An organisation-wide fix plan
  8. Worked example: an organisation with 60 public repositories
  9. India and Enterprise Server notes
  10. How eCorpIT can help
  11. References

Summary. From 2 December 2026, GitHub Actions blocks pull_request_target events by default in public repositories on GitHub Free, GitHub Team and GitHub Enterprise Cloud, unless an applicable Actions event policy explicitly allows them. Enforcement was first set for 2 November 2026; GitHub moved it to 2 December in an editor's note dated 21 October and confirmed the new date with a brownout schedule on 2 November. The first brownout ran on 9 November. The last one is on Monday 16 November 2026, 16:00 to 18:00 UTC (21:30 to 23:30 IST), when blocked runs fail with an error annotation and the workflow does not execute. Private and internal repositories and GitHub Enterprise Server are not affected. The reason is the pwn request: a pull_request_target job holds the base repository's secrets and a read/write GITHUB_TOKEN, so running a fork's code in it hands both to the pull request author. For most teams the fix is not to allow the trigger back. Move build and test jobs to pull_request, split jobs that need secrets into an unprivileged pull_request half and a privileged workflow_run half, and allow pull_request_target only for narrow jobs such as labelling, through a policy scoped to that workflow file.

About this article. For platform, DevOps and security teams and open-source maintainers with public GitHub repositories. We checked dates, scope, behaviour, action versions and plan limits against the GitHub Changelog, GitHub Docs, the GitHub Security Lab, official actions/* release pages and GitHub's pricing page on 11 November 2026. GitHub has moved this date once, so re-check before 2 December. No vendor paid for inclusion. Researched with AI assistance and reviewed by our editorial team.

What changes on 2 December 2026

The block is a default rule inside workflow execution protections, the policy layer in the Policies section of Actions settings. GitHub opened that feature as a public preview on 18 June 2026 and made it generally available on 17 September 2026, adding workflow file targeting, policy insights and a REST API. The default rule currently runs in evaluate mode: workflows still run, and GitHub records the runs it would block.

The timeline below comes from GitHub's 2 November changelog post and the 17 September GA post.

Date (2026) What happens Time
17 September Default rule announced with workflow execution protections GA, enforcement set for 2 November Not applicable
21 October Editor's note moves enforcement to 2 December Not applicable
2 November Changelog confirms 2 December and publishes two brownouts Not applicable
9 November Brownout 1, the default rule enforced temporarily (passed) 16:00 to 18:00 UTC
16 November Brownout 2, the last temporary enforcement 16:00 to 18:00 UTC, 21:30 to 23:30 IST, 17:00 to 19:00 CET, 11:00 to 13:00 EST
2 December Permanent enforcement in affected public repositories Time not published

Who is in scope, according to GitHub's changelog and its pull_request_target reference page:

Repository or product Affected on 2 December?
Public repositories on GitHub Free, Team or Enterprise Cloud Yes, unless an applicable event policy allows pull_request_target
Private repositories No
Internal repositories No
GitHub Enterprise Server No
Public repositories with an applicable event policy you already configured The default rule does not replace that policy

A blocked run fails with an error naming the disallowed event and the workflow file. Enforcement applies to affected repositories that were on the default pull_request_target policy before general availability. If you created event policies during the preview, check what they allow, because an applicable policy you configured takes the place of the default.

Why GitHub is blocking it: the pwn request

A pull_request workflow runs the workflow file from the pull request's merge commit. For a fork, someone without write access controls that commit, so GitHub gives the job a read-only GITHUB_TOKEN, withholds other secrets and applies fork approval policies.

pull_request_target changes the source: the workflow file, and any actions/checkout step without a ref, come from the base repository's default branch. Because only trusted code runs, GitHub grants secrets and a read/write token, which suits labelling, triage and commenting on fork pull requests. Since 8 December 2025 the workflow source is always the latest commit on the default branch, whatever the pull request's base branch.

The danger appears when a workflow author overrides that default to test the fork's code with secrets available:


            # INSECURE. Do not copy. Shown to recognise the pattern.
on:
  pull_request_target:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7   # v7 now refuses this ref unless allow-unsafe-pr-checkout is true
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - run: make test              # would run the fork's Makefile with your secrets
          

The checkout alone executes nothing. The next step does: make test, npm install, a test runner or a build configuration file all run attacker-controlled code with your token and secrets. GitHub's docs call this a pwn request and say the pattern has caused multiple supply-chain compromises. The GitHub Security Lab adds a point teams often miss: even with no secrets referenced, the job holds the read/write repository token, and actions/checkout persists credentials by default, so later steps can read the token from disk. Since v6 that credential sits in a file under $RUNNER_TEMP rather than .git/config, but it is still persisted by default.

GitHub lists three common vulnerable shapes:

  • Checking out the pull request head or merge ref (head.sha, refs/pull/<n>/merge) and then building or testing it.
  • Pointing repository: at the fork (github.event.pull_request.head.repo.full_name).
  • Fetching pull request code another way, such as git fetch, gh pr checkout or an artifact from a fork's run, and then running it.

The problem is not unique to this trigger: an issue_comment or workflow_run workflow that fetches and runs fork code is exposed in the same way. Our guide to npm trusted publishing and cache-poisoning hardening shows how this chain plays out in a release pipeline.

Two related protections are already live:

  • Checkout refuses fork code. actions/checkout v7 (18 June 2026) refuses to fetch fork pull request head or merge code in pull_request_target workflows, and in workflow_run workflows triggered by a pull request event. On 20 July 2026 GitHub backported the check to every supported major except v1; floating major tags such as @v4 picked it up, but SHA, minor or patch pins did not. The opt-out input, allow-unsafe-pr-checkout: true, is named so reviewers and scanners notice it; treat it as a review flag.
  • Read-only cache. Since 26 June 2026, untrusted triggers such as pull_request_target, issue_comment and fork-driven workflow_run runs get read-only access to the default branch's cache, which closes a common cache-poisoning route.

Step 1: find every affected workflow

Do this before 16 November so the second brownout confirms your list.

Use policy insights first. GitHub tells owners to check insights to see which runs the rule would block. The Policy insights page sits under Actions policies in the repository, organisation or enterprise sidebar, and lists runs that were blocked or would have been blocked.

Check the 9 November brownout. Any public repository whose pull_request_target workflow fired between 16:00 and 18:00 UTC on 9 November should show a failed run with an error annotation. That is a real-world list of workflows that broke.

Search code across the organisation. In GitHub code search:


            org:your-org path:.github/workflows pull_request_target
          

Sweep every public repository locally. Search results are not a complete inventory, so a shallow clone and grep is a dependable cross-check:


            ORG=your-org
gh repo list "$ORG" --visibility public --no-archived --limit 2000 \
  --json nameWithOwner -q '.[].nameWithOwner' > public-repos.txt

mkdir -p audit && cd audit
while read -r repo; do
  gh repo clone "$repo" "$repo" -- --depth 1 --quiet 2>/dev/null || true
done < ../public-repos.txt

# 1. Every workflow that uses the trigger
grep -rl 'pull_request_target' */*/.github/workflows/ > prt-workflows.txt

# 2. The risky shapes inside them (fork code checked out or fetched)
xargs grep -nE 'head\.sha|head\.ref|refs/pull/|head\.repo\.full_name|gh pr checkout|git fetch|allow-unsafe-pr-checkout' \
  < prt-workflows.txt

# 3. Other privileged triggers to review in the same pass
grep -rlE '^[[:space:]]*(workflow_run|issue_comment)[[:space:]]*:' */*/.github/workflows/
          

Turn on CodeQL for GitHub Actions. GitHub recommends it for finding common workflow vulnerabilities. It catches the patterns above and injection bugs that a grep will not.

Record each hit with four facts: repository, workflow file, whether it checks out or fetches pull request code, and which secrets or token permissions it uses. Those four columns decide the fix.

Step 2: triage each workflow

What the workflow does Uses fork code? Needs secrets or write token? Fix
Labels, triages or comments on PRs, no checkout of PR code No Write token only Keep pull_request_target; allow it by a policy scoped to this workflow file; set least-privilege permissions
Builds and tests fork code, no secrets needed Yes No Switch to pull_request
Builds fork code, then posts coverage or results with a token Yes Yes, after the build Split: pull_request builds and uploads an artifact; workflow_run reads it as data and posts
Deploys a preview of fork changes Yes Yes Split as above, and put the privileged job behind a protected environment with required reviewers
Runs on closed for post-merge tasks such as release notes or backports Usually no Yes Move to push on the default branch, or allow by scoped policy if it never touches PR code
Uses a safe-to-test label to gate fork code with secrets Yes Yes Replace; label gating is a temporary, race-prone fix
Checks out PR head with allow-unsafe-pr-checkout set to true Yes Yes Prove the code is only read as data, never executed, or redesign

The Security Lab treats label gating as a stopgap because an attacker can push new commits after the label is applied and before the run starts. The closed row catches people out: GitHub documents pull_request_target with the closed type as the way to act when a pull request merges, so those workflows are blocked too.

Step 3: replace the trigger with safe patterns

Pattern A: plain pull_request with least privilege

For most build and test workflows, this is the whole fix. pull_request runs the fork's code without your secrets and with a read-only token.


            name: ci
on:
  pull_request:
permissions:
  contents: read
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7   # pin to a full commit SHA in production
        with:
          persist-credentials: false
      - run: npm ci && npm test
          

Fork runs may need a maintainer to approve them, depending on your approval settings. GitHub asks maintainers to look closely at any change under .github/workflows/ before approving, and runs left waiting for approval for more than 30 days expire and are marked as failed.

Pattern B: pull_request plus workflow_run

When a result must be written back with a token, split the work. This is the GitHub Security Lab's recommended pattern, adapted with current action versions.


            # .github/workflows/receive-pr.yml  (unprivileged)
name: Receive PR
on:
  pull_request:
permissions:
  contents: read
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - run: ./build.sh && ./coverage.sh > coverage.txt
      - run: |
          mkdir -p out
          echo "${{ github.event.number }}" > out/NR
          cp coverage.txt out/
      - uses: actions/upload-artifact@v7
        with:
          name: pr-result
          path: out/
          

            # .github/workflows/comment-pr.yml  (privileged, runs from the default branch)
name: Comment on PR
on:
  workflow_run:
    workflows: ["Receive PR"]
    types: [completed]
permissions:
  actions: read
  pull-requests: write
jobs:
  comment:
    runs-on: ubuntu-latest
    if: >
      github.event.workflow_run.event == 'pull_request' &&
      github.event.workflow_run.conclusion == 'success'
    steps:
      - uses: actions/download-artifact@v8
        with:
          name: pr-result
          path: ${{ runner.temp }}/pr   # never extract into the workspace
          run-id: ${{ github.event.workflow_run.id }}
          github-token: ${{ secrets.GITHUB_TOKEN }}
      - uses: actions/github-script@v9
        with:
          script: |
            const fs = require('fs');
            const n = Number(fs.readFileSync(`${process.env.RUNNER_TEMP}/pr/NR`, 'utf8').trim());
            if (!Number.isInteger(n)) throw new Error('bad PR number');
            const body = fs.readFileSync(`${process.env.RUNNER_TEMP}/pr/coverage.txt`, 'utf8').slice(0, 60000);
            await github.rest.issues.createComment({ ...context.repo, issue_number: n, body });
          

Two rules make this safe. The artifact is attacker-influenced, so the privileged job only reads it as text and never runs a binary or script from it. And the artifact goes to runner.temp, not the workspace, so nothing in it can shadow a script a later step might call. The Security Lab also notes that the PR number in the artifact is attacker-controlled, which it considers acceptable for posting a comment and nothing more powerful.

Pattern C: protected environments for privileged jobs

If the privileged half needs real credentials, for example to publish a preview build to a sandbox bucket, put that job behind an environment:


            jobs:
  deploy-preview:
    runs-on: ubuntu-latest
    environment: fork-preview        # required reviewers + prevent self-review
    permissions:
      contents: read
      id-token: write                # prefer OIDC over stored cloud keys
    steps:
      - run: echo "deploy the downloaded static files to an isolated preview target"
          

Environment secrets stay locked until one of the required reviewers approves the job. You can name up to six users or teams as reviewers, and the prevent self-review option stops the person who started the run from approving it. Plan limits matter: on GitHub Free, Pro and Team, required reviewers and wait timers work only in public repositories, which is exactly where this change applies. GitHub's pricing page lists environment protection rules for private repositories only on Enterprise. Our breakdown of GitHub Enterprise pricing with Copilot included covers that plan in detail.

Pattern D: keep pull_request_target for narrow jobs

Labelling and triage are what the trigger was built for. Keep them, but keep them small:


            name: labeler
on:
  pull_request_target:
    types: [opened, synchronize, reopened]
permissions:
  contents: read
  pull-requests: write
jobs:
  label:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/labeler@<full-commit-sha>   # no checkout of PR code anywhere
          

Step 4: allow what must stay with a scoped event policy

GitHub gives two options: keep the default block, or explicitly allow pull_request_target in an applicable Actions event policy, using workflow file targeting to allow only specific workflows. In the interface, open Settings, then Actions, then Policies at repository or organisation level; at enterprise level, open the Policies tab, then Actions, then Policies. There you name the policy, pick an enforcement status, target repositories and workflows, and add event rules. Execution protections cover all public repositories, so Free organisations can create these policies too.

The same thing through the Actions policies REST API:


            gh api -X POST /orgs/your-org/actions/policies \
  -H "X-GitHub-Api-Version: 2026-03-10" --input - <<'JSON'
{
  "name": "Allow pull_request_target for labeler only",
  "enforcement": "active",
  "conditions": {
    "repository_name": { "include": ["~ALL"], "exclude": [] },
    "workflow_path": { "include": [".github/workflows/labeler.yml"], "exclude": [] }
  },
  "rules": [
    { "type": "restrict_action_events",
      "parameters": { "allowed_events": ["pull_request_target"] } }
  ]
}
JSON
          

Three cautions before you run it:

  1. Event rules are an allowlist. The API field is allowed_events, and GitHub describes execution protections as an allowlist of permitted events. For the workflows a policy targets, an event you leave out is not permitted. If labeler.yml also runs on workflow_dispatch, list both or that trigger stops working.
  1. Scope by workflow path. Leaving out workflow_path targets every workflow, so this policy would allow pull_request_target for every workflow in every targeted repository and, under the same allowlist logic, disallow every event you did not list.
  1. Evaluate mode needs Enterprise. The guide marks Evaluate as GitHub Enterprise Cloud only, and the API says the same. On Free and Team, test a new active policy on one repository first.

GitHub's docs do not say outright whether a policy scoped to one workflow file counts as applicable for the rest of a repository's workflows. Confirm the outcome in policy insights, or watch the 16 November brownout, before you rely on it.

An organisation-wide fix plan

When (2026) Owner Action
11 to 13 November Platform team Run the inventory: policy insights, 9 November failed runs, code search, local sweep. Build the four-column list
13 to 15 November Repo owners Triage each workflow with the table above. Switch simple build and test jobs to pull_request
15 November Organisation owners Create scoped allow policies for labelling and triage workflows only
16 November, 16:00 to 18:00 UTC Platform on-call Watch the brownout. Every failure is either a missed workflow or a policy scoping mistake
17 to 27 November Repo owners Build pull_request plus workflow_run splits and protected environments for the remaining jobs
28 November to 1 December Security Review every allow-unsafe-pr-checkout, label gate and workflow_run consumer, and every SHA-pinned checkout older than the July backport
2 December Everyone Enforcement. Watch for failed runs with error annotations and fix forward
December Platform team Add actor rules, require actions pinned to full commit SHAs, and adopt workflow lockfiles

The SHA check in the 28 November row matters because the July checkout backport reached floating major tags only; a workflow pinned to an older commit still lacks the fork-checkout guard. One old worry no longer applies: because pull_request_target now always runs the workflow from the latest default-branch commit, a fix merged there also covers pull requests opened before it.

For the December row, actor rules let you separate who can contribute code from who can run CI, and they can name GitHub Apps, Copilot and Dependabot. The Require lockfile protection blocks targeted workflows that are not onboarded to the repository's lockfile. Workflow lockfiles became generally available on 28 October 2026 for GitHub Enterprise Cloud, Team and Free. Our software supply chain hardening guide covers SHA pinning and related controls. If your teams run coding agents that open pull requests, actor rules also belong in your AI coding agent rollout and governance plan.

Worked example: an organisation with 60 public repositories

This scenario is illustrative. The counts and hours are planning assumptions, not measurements; only the GitHub behaviours are verified.

An open-source SDK publisher has 60 public repositories. The inventory finds 14 workflows using pull_request_target across 11 repositories:

Workflow group Count Fix Assumed hours each Hours
Labelling and triage bots, no PR checkout 6 Keep, allow through 1 scoped organisation policy 0.5 3
Build and test of fork code, no secrets 4 Switch to pull_request 1 4
Coverage comment needing a write token 3 Split into pull_request plus workflow_run 4 12
Preview deploy needing cloud credentials 1 Split, plus protected environment and OIDC 6 6
Total 14 25

Arithmetic: (6 × 0.5) + (4 × 1) + (3 × 4) + (1 × 6) = 3 + 4 + 12 + 6 = 25 engineer-hours, or a little over three eight-hour days for one person (25 ÷ 8 = 3.1). The split adds no Actions charge: minutes on standard GitHub-hosted runners are free in public repositories, as is self-hosted runner usage, though larger runners are billed even there.

The six labelling workflows are the cheapest fix and the easiest to get wrong: one organisation policy with a workflow_path condition covers all six if they share a file name; if three repositories call it triage.yml, you need both paths in the include list.

India and Enterprise Server notes

India. The final brownout runs from 21:30 to 23:30 IST on Monday 16 November, outside most Indian working hours. Teams that maintain public SDKs or open-source tools for global customers should put someone on call for that window. Where a stolen secret could reach systems holding personal data, a leak is a data protection question too, so list which secrets a fork-triggered job can ever see.

GitHub Enterprise Server. GitHub says Enterprise Server is not covered by this change. The risk still applies there. Run the same inventory and apply the same patterns on your own schedule. Nothing in GitHub's notice varies by region.

How eCorpIT can help

eCorpIT is a Gurugram-based technology organisation, founded in 2021, assessed at CMMI Level 5 and MSME certified, with senior-led engineering teams working across AWS, Microsoft and Google platforms. Our cybersecurity services team can inventory your workflows, rebuild risky pull_request_target, workflow_run and issue_comment jobs, and write scoped Actions policies you manage through the API. Talk to us at /contact-us/.

Last updated: 11 November 2026.

References

  1. pull_request_target blocked by default December 2, 2 November 2026 — GitHub Changelog
  1. Workflow execution protections in GitHub Actions generally available, 17 September 2026 (editor's note 21 October 2026) — GitHub Changelog
  1. Securely using pull_request_target — GitHub Docs
  1. Control who and what triggers GitHub Actions workflows, 18 June 2026 — GitHub Changelog
  1. About Actions policies — GitHub Docs
  1. Events that trigger workflows — GitHub Docs
  1. Actions pull_request_target and environment branch protections changes, 7 November 2025 — GitHub Changelog
  1. Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests, 3 August 2021 — GitHub Security Lab
  1. Releases, actions/checkout — GitHub
  1. Safer pull_request_target defaults for GitHub Actions checkout, 18 June 2026 (editor's note 15 July 2026) — GitHub Changelog
  1. Read-only Actions cache for untrusted triggers, 26 June 2026 — GitHub Changelog
  1. Governing execution of GitHub Actions workflows — GitHub Docs
  1. Approving workflow runs from forks — GitHub Docs
  1. Deployments and environments — GitHub Docs
  1. GitHub plans and pricing — GitHub
  1. REST API endpoints for GitHub Actions policies — GitHub Docs
  1. GitHub Actions workflow lockfiles are generally available, 28 October 2026 — GitHub Changelog
  1. GitHub Actions billing — GitHub Docs

Frequently asked

Quick answers.

01 What happens to pull_request_target workflows in public repositories after 2 December 2026?
GitHub Actions blocks them by default unless an applicable Actions event policy explicitly allows the event. Blocked attempts create failed runs with an error annotation, and the workflow does not execute. The rule covers public repositories on GitHub Free, Team and Enterprise Cloud. Private and internal repositories and GitHub Enterprise Server are not affected.
02 When is the last brownout, and what happens during it?
The last brownout is on Monday 16 November 2026 from 16:00 to 18:00 UTC, which is 21:30 to 23:30 IST. During the window GitHub enforces the default rule, so blocked pull_request_target workflows fail with an error annotation. Afterwards the rule returns to evaluate mode, and workflows run with warnings until enforcement on 2 December.
03 How do I allow pull_request_target for a workflow that needs it?
Create an Actions event policy at repository, organisation or enterprise level that lists pull_request_target in its allowed events, and target it at the specific workflow file with a workflow path condition. Because event rules are an allowlist, also list any other events that workflow uses. Allow only jobs that never run pull request code.
04 Is switching to pull_request enough?
For build and test jobs that need no secrets, yes. The pull_request event runs the fork's code with a read-only token and no repository secrets, which is the safe default. If a job must post results or use credentials, split it into an unprivileged pull_request workflow and a privileged workflow_run workflow that reads only artifact data.
05 Why is workflow_run safer than pull_request_target?
The privileged workflow_run job runs from your default branch and never executes the fork's code; it only reads results the unprivileged build uploaded. GitHub warns it becomes unsafe if it fetches and runs fork code or artifacts. Treat every artifact as untrusted data, extract it outside the workspace, and keep its permissions narrow.
06 What is a pwn request?
A pwn request is a workflow that runs with secrets or a write token and checks out, fetches or downloads pull request code from a fork, then executes it. Build scripts, package installs and test runners all count as execution. GitHub says this pattern has caused multiple supply-chain compromises, which is why the default block exists.
07 Will the fix increase our GitHub Actions bill?
Usually not for public repositories. GitHub says Actions minutes on standard GitHub-hosted runners are free for public repositories, so splitting one workflow into a pull_request and workflow_run pair adds no minute charges there. Larger runners are billed even for public repositories, and self-hosted runners still use your own compute.
08 Does blocking pull_request_target make our CI secure?
No. It removes the most common path, but GitHub notes that issue_comment and workflow_run workflows that fetch and run fork code are exposed in the same way. Review those triggers, enable CodeQL for GitHub Actions, set least-privilege token permissions, pin actions to full commit SHAs and consider workflow lockfiles.

About the author

Manu Shukla

Founder & Director

Founder of eCorpIT. Hands-on engineer leading senior-only delivery for AI apps, custom software, and cloud systems for global clients.

Subscribe

One engineering note a week. No fluff, no spam.

Senior-architect playbooks on AI agents, mobile apps, cloud, security, data, and marketing — delivered every Wednesday.

Past the reading

Read enough. Let's build something.

A senior architect responds in 24 working hours with scope, indicative cost, and a timeline. NDA before any technical conversation.

Loading search…

Browse all guides