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_target runs 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:

  1. Make sure it never checks out or runs code from github.event.pull_request.head.
  2. Give it minimal permissions: (for example, pull-requests: write and nothing else).
  3. Explicitly allow pull_request_target for 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.