Key takeaways
- pull_request_target runs a pull request workflow in the base repository's context, with its GITHUB_TOKEN and secrets. pull_request gives fork pull requests a read-only token and no secrets.
- The danger is a pwn request: a pull_request_target workflow that checks out the pull request's code and then runs it, handing secrets to an attacker.
- From 2 December 2026, GitHub blocks pull_request_target by default in public repositories on GitHub Free, Team and Enterprise Cloud, unless an Actions event policy allows it. Private and internal repositories and GitHub Enterprise Server are not affected.
- Brownouts on 9 and 16 November 2026, 16:00 to 18:00 UTC, enforce the block early. Blocked runs fail with an error annotation.
- Check Policy insights now, move workflows that do not need secrets to pull_request, and allow pull_request_target only for specific workflow files that never run pull request code.
On this page
- pull_request vs pull_request_target in plain terms
- Why pull_request_target is risky: pwn requests
- What GitHub is changing, and when
- How to check whether your workflows are affected
- Three ways to fix a pull_request_target workflow
- The new actions/checkout safety switch
- Quick checklist for maintainers
- What is confirmed and what is not stated
pull_request_target is a GitHub Actions event that runs a workflow for a pull request in the context of the base repository. It uses the workflow file from the default branch and gets the base repository's GITHUB_TOKEN and secrets. The plain pull_request event runs the pull request's own merge commit and, for forks, gets a read-only token and no secrets. Because pull_request_target is so often misused, GitHub will block it by default in public repositories from 2 December 2026.
This explainer covers the difference between the two events, why the privileged one is risky, the exact dates of the change, and what maintainers should do before the first brownout on 9 November 2026.
pull_request
Untrusted. Runs the pull request's merge commit. Forks get a read-only GITHUB_TOKEN and no secrets.
pull_request_target
Trusted. Runs the workflow file from the default branch, with the base repository's token and secrets.
Pwn request
A pull_request_target workflow that checks out pull request code and runs it, so the attacker's code gets your secrets.
The block
Default block in public repositories from 2 December 2026, with brownouts on 9 and 16 November.
pull_request vs pull_request_target in plain terms
When someone opens a pull request, GitHub can start a workflow. The question is whose code and whose permissions that workflow gets.
- pull_request treats the pull request as untrusted. It runs the workflow from the pull request's merge commit, so a contributor's changes are what get built and tested. For a pull request from a fork, the events reference says the
GITHUB_TOKEN"has read-only permissions", and other secrets are not passed to the runner. - pull_request_target treats the run as trusted. It runs the workflow file from the base repository's default branch, and GitHub Docs say its jobs receive "the base repository's
GITHUB_TOKENand access to repository and organization secrets."
That trust is useful. GitHub names labeling, triage and posting authenticated status checks on fork pull requests as the intended uses. These jobs need write access, but they do not need to run the contributor's code.
| pull_request | pull_request_target | |
|---|---|---|
| Workflow file used | From the pull request's merge commit | From the base repository's default branch |
Default actions/checkout | The merge commit | The default branch |
GITHUB_SHA | Last merge commit of the pull request merge branch | Last commit on the default branch |
GITHUB_TOKEN for forks | Read-only | The base repository's token |
| Secrets for forks | Not passed (except GITHUB_TOKEN) | Repository and organization secrets |
| Default activity types | opened, synchronize, reopened | opened, synchronize, reopened |
| Good for | Building and testing contributor code | Labels, triage, comments, status checks |
Why pull_request_target is risky: pwn requests
The problem starts when a pull_request_target workflow fetches the pull request's code and runs it. GitHub calls this a "pwn request" and says the pattern is behind multiple supply-chain compromises. The attacker controls the code, and the code runs with your token and secrets.
Here is the insecure example from GitHub Docs:
# INSECURE. Provided as an example only.
on:
pull_request_target:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Test
run: make test
GitHub points out that the checkout alone is not the exploit. The attack completes in the next step: make test runs a Makefile that came from the pull request. The same thing can happen through npm install, build scripts, config files or dependencies, even when no step looks like it runs code.
GitHub Docs list other shapes of the same mistake:
- Checking out the head commit or
refs/pull/<number>/merge, then building or testing it. - Setting
repository:to the fork so the fork's branch is pulled in directly. - Fetching pull request code another way, such as
git fetch,gh pr checkoutor a downloaded artifact, and running it.
The docs also note that any event with secrets can have this problem, including issue_comment and workflow_run. Cache poisoning is a further risk if a privileged workflow can write to shared caches. GitHub Docs say pull_request_target runs get read-only access to the default branch cache scope, so they can restore entries but not save them, and that declaring a write-capable cache-mode brings the poisoning risk back.
What GitHub is changing, and when
The GitHub Changelog post of 2 November 2026 sets out the change. GitHub Actions will block pull_request_target events by default in public repositories, unless an Actions event policy explicitly allows them. Enforcement was previously scheduled for 2 November 2026 and now starts on 2 December 2026.
| Date | What happens |
|---|---|
| Now until 2 December 2026 | The default rule runs in evaluate mode. Workflows run, with warnings, and insights show which runs would be blocked. |
| 9 November 2026, 16:00 to 18:00 UTC | Brownout: the block is enforced. Blocked attempts create failed runs with an error annotation and do not execute. |
| 16 November 2026, 16:00 to 18:00 UTC | Second brownout, same behaviour. |
| 2 December 2026 | The default block is enforced. |
Who is affected
- Affected: public repositories on GitHub Free, GitHub Team and GitHub Enterprise Cloud.
- Not affected: private and internal repositories, and GitHub Enterprise Server.
- The default policy does not replace an Actions event policy you have already configured, according to GitHub Docs.
How to check whether your workflows are affected
- Search your repositories for
pull_request_targetin.github/workflows/. - Open Policy insights. GitHub Docs place it directly under the Actions policies page in the sidebar of the repository, organization or enterprise. It lists runs that would have been blocked.
- For each workflow, ask one question: does it need secrets or a write token? If not, move it to
pull_request. If it does, check that it never runs pull request code. - Do this before 9 November 2026, as the changelog advises, so the first brownout does not surprise your contributors.
Three ways to fix a pull_request_target workflow
1. Switch to pull_request
This is GitHub's first recommendation when secrets are not needed. For the insecure example above, our adapted version changes the trigger and drops the custom ref, because pull_request already checks out the merge commit (GitHub Docs describe this fix in prose but give no fixed example):
on:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- name: Test
run: make test
The tests now run with a read-only token and no secrets for fork pull requests, which is what untrusted code should get.
2. Split the work into two workflows
Sometimes you need both: test the contributor's code, then post a comment or status with a write token. The GitHub Security Lab guide on pwn requests describes a two-workflow pattern:
- An unprivileged workflow on
pull_requestbuilds and tests the code, saves the results (such as the pull request number or test output) as artifacts, and exits. - A privileged workflow on
workflow_runstarts after the first one finishes, downloads the artifacts and acts on them, for example by commenting on the pull request.
The key rule from that guide is that "the untrusted code must be handled via the pull_request trigger". Treat the artifacts as data only. GitHub Docs warn that artifacts from other workflows can come from a fork, so never execute them in the privileged workflow.
3. Keep pull_request_target and allow it on purpose
If a workflow truly needs the base repository's context, such as a labeler that never checks out pull request code, you can keep it. GitHub's guidance:
- Make sure any checked-out code is "only ever inspected as data and never executed."
- Give the
GITHUB_TOKENthe least permissions it needs, and only the secrets the job uses. - Create or update an Actions event policy that explicitly allows pull_request_target. For a repository or organization, go to Settings, then under Actions click Policies. Use the Restrict events rule and target the specific workflow file paths, so other workflows stay blocked.
- On self-hosted runners, keep them away from internal resources and do not reuse them across runs.
- Turn on CodeQL for GitHub Actions to scan workflows for these patterns.
Note that the docs say the Evaluate enforcement status for your own policies is available only on GitHub Enterprise Cloud. A policy set to Active blocks non-compliant runs.
The new actions/checkout safety switch
The actions/checkout README says that from v7, checkout "refuses to check out fork pull request code by default" in workflows triggered by pull_request_target or workflow_run. To opt in, you set allow-unsafe-pr-checkout: true, which defaults to false.
GitHub Docs warn that this input covers only fork pull request refs fetched by checkout. It does not cover git fetch, gh pr checkout, other repositories or downloaded artifacts. Setting it is a statement that you have checked the code is never run. Security Lab also notes that persist-credentials defaults to true, so the token is stored on disk for later steps unless you turn it off.
Quick checklist for maintainers
- List every workflow that uses pull_request_target.
- Check Policy insights before 9 November 2026.
- Move test and build jobs to
pull_request. - Split jobs that need a write token into a
pull_requestandworkflow_runpair. - For workflows you keep, remove any checkout of pull request code, cut token permissions, and allow the event for those workflow files only.
- Close or rebase open pull requests that could still run an old vulnerable workflow version, as Security Lab advises.
Keeping your own devices patched matters just as much as your pipelines. Our guide on how to check your Android security patch level covers the phone side, and more security explainers are on the Dexaloop Tech hub.
What is confirmed and what is not stated
- Confirmed by GitHub: the 2 December 2026 default block, the 9 and 16 November 2026 brownouts at 16:00 to 18:00 UTC, the affected plans, and that private repositories, internal repositories and GitHub Enterprise Server are excluded.
- Not stated: the exact text of the error annotation on a blocked run, and the step-by-step form for selecting events in the Restrict events rule. Check them in your own settings rather than relying on third-party screenshots.
Questions
What is the difference between pull_request and pull_request_target?
pull_request runs the workflow from the pull request's merge commit. For a fork, GitHub gives it a read-only GITHUB_TOKEN and passes no other secrets. pull_request_target runs the workflow file from the base repository's default branch, with the base repository's GITHUB_TOKEN and access to repository and organization secrets.
When does GitHub block pull_request_target?
GitHub's changelog says the default block starts on 2 December 2026 in public repositories, unless an Actions event policy explicitly allows the event. It was first planned for 2 November 2026. Two brownouts, on 9 and 16 November 2026 from 16:00 to 18:00 UTC, apply the block early.
Are private repositories affected?
No. GitHub says private and internal repositories are not affected. The change applies to public repositories on GitHub Free, GitHub Team and GitHub Enterprise Cloud, and not to GitHub Enterprise Server.
How do I keep pull_request_target working for one workflow?
Create or update an Actions event policy that explicitly allows pull_request_target, and target it at that workflow file. Policies live in Settings, under Actions, then Policies, for a repository or organization. Do this only if the workflow never runs code from the pull request.
How do I see which workflows the block would stop?
Open Policy insights, which sits under the Actions policies page in the sidebar of the repository, organization or enterprise. Until enforcement, the default policy runs in evaluate mode, so insights show runs that would have been blocked while they still run.
Is a 'safe to test' label enough to protect pull_request_target?
GitHub Security Lab calls label gating only a temporary fix, because new commits can be pushed after the label is added. Keep untrusted code in a pull_request workflow instead.
Sources
- pull_request_target blocked by default December 2 (GitHub Changelog, 2 Nov 2026) (accessed 7 November 2026)
- Securely using pull_request_target (GitHub Docs) (accessed 7 November 2026)
- Controlling workflow execution (GitHub Docs) (accessed 7 November 2026)
- Events that trigger workflows (GitHub Docs) (accessed 7 November 2026)
- Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests (GitHub Security Lab) (accessed 7 November 2026)
- actions/checkout README (GitHub) (accessed 7 November 2026)
Written from the sources above and checked against them before publishing. Found a mistake? Email support@dexaloop.com and we will correct it.