If you maintain a public GitHub repository, there’s a deadline to know about. Starting 2 December 2026, GitHub Actions will block pull_request_target events by default in all public repositories, unless an Actions event policy explicitly allows them. Enforcement had previously been scheduled for 2 November. GitHub is also running two “brownouts” first, and the first one has already happened. This guide explains the change, shows you how to find affected workflows, and walks through the safest way to migrate.
What GitHub Announced
From the official changelog post:
- The rule is part of GitHub’s workflow execution protections. It runs in evaluate mode until enforcement, so you can see which runs would be blocked.
- Private and internal repositories aren’t affected.
- It applies to public repositories on GitHub Free, GitHub Team and GitHub Enterprise Cloud, but not GitHub Enterprise Server.
- During a brownout, blocked
pull_request_targetruns show up as failed runs with an error annotation, and the workflows don’t execute. Afterwards the rule goes back to evaluate mode.
Brownout schedule
- 9 November 2026, 16:00–18:00 UTC (21:30–23:30 IST). This one is done.
- 16 November 2026, 16:00–18:00 UTC (21:30–23:30 IST)
If a bot, labeler or preview-deploy workflow failed on your repo last night, this is probably why.
Why pull_request_target Is Risky
pull_request_target runs in the context of the base repository and can access its secrets. That’s what makes it useful for labelling or commenting on pull requests from forks. It’s also why it’s dangerous. If the workflow checks out and runs code from the fork’s pull request, for example npm install or npm test on the PR branch, that untrusted code can steal secrets or tamper with your pipeline.
# ❌ The classic dangerous pattern
on: pull_request_target
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # untrusted fork code
- run: npm ci && npm test # runs it with secrets available
Step 1: Find Every Workflow That Uses It
In a single repository:
grep -rn "pull_request_target" .github/workflows/
Across all your repositories, use GitHub code search in the browser, for example org:your-org pull_request_target path:.github/workflows, or the GitHub CLI:
gh search code "pull_request_target" --owner your-org --limit 100
GitHub’s changelog also suggests checking Insights to see which workflow runs the default rule would block.
Step 2: Decide Per Workflow
For each hit, ask one question: does this workflow really need secrets or write access while handling a fork PR?
- No (builds, tests, linting): switch to plain
pull_request. It runs fork code without your secrets, which is what you want. - Yes, but it only needs results from the build (posting coverage comments, uploading previews): use the two-workflow pattern below.
- Yes, and it never runs PR code (for example, a labeler that only reads metadata): you can keep
pull_request_target, but you must explicitly allow it in an Actions event policy, using workflow file targeting so only that workflow is allowed.
Step 3: Migrate to pull_request + workflow_run
The safe pattern splits the work in two. The untrusted part runs with no secrets. The trusted part runs later in the base repository’s context and only reads artifacts. It never runs the PR’s code.
Workflow A: untrusted build (no secrets)
# .github/workflows/pr-build.yml
name: PR build
on:
pull_request:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- run: npm ci
- run: npm test -- --coverage
- name: Save PR number and coverage summary
run: |
mkdir -p out
echo "${{ github.event.pull_request.number }}" > out/pr_number
cp coverage/coverage-summary.json out/ || echo "{}" > out/coverage-summary.json
- uses: actions/upload-artifact@v4
with:
name: pr-results
path: out/
Workflow B: trusted follow-up (has a token, never runs PR code)
# .github/workflows/pr-comment.yml
name: PR comment
on:
workflow_run:
workflows: ["PR build"]
types: [completed]
permissions:
pull-requests: write
actions: read
jobs:
comment:
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: pr-results
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
path: results
- name: Comment on the PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
run: |
PR=$(tr -cd '0-9' < results/pr_number)
test -n "$PR" || exit 1
gh pr comment "$PR" --repo "$REPO" --body "✅ Build passed. Coverage summary attached as artifact."
Two details matter here:
- Treat artifact contents as untrusted input. They were produced by fork code. That’s why the PR number is stripped down to digits only, and why nothing from the artifact gets executed.
- Never check out the PR head in Workflow B. Once you do that, you’re back to the original problem.
Step 4: If You Must Keep pull_request_target
Some workflows, such as a labeler that reads only the PR’s metadata and changed file paths, are reasonable uses. To keep one working after 2 December:
- Make sure it never checks out or runs code from
github.event.pull_request.head. - Give it minimal
permissions:(for example,pull-requests: writeand nothing else). - Explicitly allow
pull_request_targetfor that specific workflow file in your Actions event policy. GitHub’s documentation on controlling workflow execution covers where to configure this for your organisation or repository.
# A metadata-only labeler: acceptable with an explicit policy allowance
on: pull_request_target
permissions:
contents: read
pull-requests: write
jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5 # reads changed paths; does not run PR code
Step 5: Verify Before 16 November
- Open a test PR from a fork, or ask a contributor to, and confirm the new workflows behave as expected.
- Check Insights again to make sure nothing is still flagged unexpectedly.
- Watch the 16 November brownout window (21:30–23:30 IST) for failures you missed.
Takeaway
GitHub is closing one of the most commonly abused CI patterns. Find every pull_request_target workflow now. Move builds and tests to pull_request, move secret-needing follow-ups to workflow_run with artifacts, and only allow pull_request_target explicitly for metadata-only workflows. Finishing before the 16 November brownout means 2 December will be a non-event. Source: GitHub Changelog.
