GitHub Actions will block pull_request_target events by default in all public repositories from 2 December 2026, unless an applicable Actions event policy explicitly allows them. The GitHub changelog entry dated 2 November 2026 says enforcement was previously scheduled for 2 November. Socket reports the same date and says the postponement gives maintainers additional time to review their automation and configure exceptions. Private and internal repositories are not affected, and the GitHub docs say the default policy does not replace an event policy you have already configured.
This guide is not a repeat of our earlier pwn request write-up. It is the migration checklist: what the deadline changes, how to find your exposure, and what to do with each workflow.
What changes and when
Per the GitHub changelog, GitHub is running two brownouts before permanent enforcement, both 16:00 to 18:00 UTC: 9 November and 16 November 2026. During a brownout, blocked pull_request_target runs in affected public repositories fail with an error annotation instead of executing. Between brownouts the default rule goes back to evaluate mode, where workflows run with warnings.
Two details from the GitHub docs matter for planning:
- The GitHub docs mark the Evaluate enforcement option for custom policies as “GitHub Enterprise Cloud only”. The changelog and the docs also say the default rule itself currently runs in evaluate mode and tell you to use policy insights to see which runs it would block. We did not verify which plan tiers can see those insights, so check your own settings rather than assuming.
- If you decide you do not want the trigger, you do nothing: the default policy stays in place and blocks it.
Why the trigger is dangerous
The GitHub docs explain that pull_request_target runs the workflow from the base repository’s default branch, with the base repository’s GITHUB_TOKEN and secrets. That is safe until the workflow checks out and executes fork code. The docs’ own insecure example checks out github.event.pull_request.head.sha and then runs make test, which executes a Makefile controlled by the pull request author.
Socket says variations of this pattern contributed to major supply chain incidents involving Nx, PostHog and TanStack. We did not independently verify those incident details.
This deadline is not the first change. Per Socket, GitHub’s June changes made actions/checkout reject unsafe fork checkouts in privileged workflows unless explicitly overridden. The GitHub docs describe the override: setting allow-unsafe-pr-checkout: true as an actions/checkout input allows checking out pull request head refs from forks, and the docs say to do this only after confirming the checked-out code is never executed. They also say that protection covers fork pull request refs only. Search your workflows for that input too: it is the opt-out that re-creates the risk.
Step 1: find the affected workflows
Search every repository you maintain, including forks that run Actions. The script below is an illustrative line-based scanner, not a YAML parser. It lists workflows that mention pull_request_target and flags ref: or repository: lines that point at the pull request head. We ran it against three sample workflow files (one risky, one label-only, one plain pull_request) with Node.js and it reported the first two and exited with status 1, as intended. It will miss indirect patterns, such as git fetch or gh pr checkout inside a run: step, and expressions such as github.head_ref that are not in its pattern list. It also does not search for allow-unsafe-pr-checkout.
// prt-audit.mjs Usage: node prt-audit.mjs [repo-root]
import { readdirSync, readFileSync, existsSync } from "node:fs";
import { join } from "node:path";
const root = process.argv[2] ?? ".";
const dir = join(root, ".github", "workflows");
if (!existsSync(dir)) {
console.log(`no workflow directory at ${dir}`);
process.exit(0);
}
const TRIGGER = /^\s*(?:on:.*|-\s*)?\bpull_request_target\b/;
const HEAD_REF = /github\.event\.pull_request\.head\.(sha|ref|repo)|refs\/pull\/.*\/merge/;
let risky = 0;
for (const f of readdirSync(dir).filter((n) => /\.ya?ml$/.test(n))) {
const lines = readFileSync(join(dir, f), "utf8").split("\n");
const hit = lines.findIndex((l) => TRIGGER.test(l));
if (hit === -1) continue;
console.log(`${f}:${hit + 1}: uses pull_request_target`);
lines.forEach((l, i) => {
if (HEAD_REF.test(l) && /^\s*(ref|repository):/.test(l)) {
risky++;
console.log(` ${f}:${i + 1}: checks out PR head -> ${l.trim()}`);
}
});
}
process.exit(risky > 0 ? 1 : 0);
Step 2: decide per workflow
The GitHub docs give three options for each affected workflow: switch to a safer event such as pull_request if secrets are not needed; add an event policy that explicitly allows pull_request_target (policies can target individual workflow files); or leave the default in place and accept the block.
Our assessment: most workflows that build or test fork code should move to pull_request, which runs with a read-only token and no secrets for fork pull requests. Label-only and triage workflows are the legitimate remaining use. Keep those minimal:
# Illustrative: label-only workflow that never checks out PR code
on:
pull_request_target:
types: [opened, reopened]
permissions:
contents: read
pull-requests: write
jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5 # pin to a commit SHA in real use
If a privileged step needs build output, the docs point to splitting the work so that untrusted code runs without secrets. If you split via workflow_run, the docs say to treat artifacts from other workflows as untrusted data, since their contents can come from a fork.
Step 3: harden what stays
For workflows you allow, the docs recommend least-privilege GITHUB_TOKEN permissions, only the secrets the job needs, and isolated, ephemeral runners if you self-host. They also state that pull_request_target workflows get read-only access to the default branch’s cache scope, and that declaring a write-capable cache-mode reintroduces cache-poisoning risk.
Before 16 November
The second brownout is three days from the date of this article. A failed run in that window is a cheap way to find a workflow you missed. We found no source describing how the default rule treats workflows on forks of private repositories as of 2026-11-13 in the pages listed below, so confirm that in your own settings.
References
- https://github.blog/changelog/2026-11-02-pull_request_target-blocked-by-default-december-2/
- https://socket.dev/blog/github-to-block-pull-request-target (read via Socket’s Atom feed, https://socket.dev/api/blog/feed.atom)
- https://docs.github.com/en/actions/reference/security/securely-using-pull_request_target
- https://docs.github.com/en/actions/how-tos/administer/control-workflow-execution