Jitsumi

GitHub Actions pull_request_target: Audit the December 2026 Rule

GitHub will enforce a default pull_request_target policy for affected public repositories on 2 December 2026. Audit fork PR workflows before creating exceptions.

Conceptual security gate separating untrusted pull-request code from protected repository credentials.

A GitHub Actions workflow can be working today and still be headed for a policy failure. GitHub has announced a default restriction on pull_request_target for affected public repositories, scheduled for enforcement on 2 December 2026. Until then, the default rule runs in evaluate mode: it records which workflows would be blocked without stopping those runs.

If your repository uses pull_request_target, audit what that workflow does before adding an exception. Ordinary tests of contributor code should usually run on pull_request. Reserve pull_request_target for narrow tasks that need the base repository's authority, such as applying a label, and do not execute untrusted fork code in that privileged context.

This guide is for GitHub.com maintainers with permission to review workflows and repository Actions policies. It is based on GitHub's current documentation and first-party announcements, not a claim of testing a live repository.

What changes on 2 December—and what does not

GitHub's 17 September 2026 announcement describes new Actions execution policies, including rules for actors, triggering events and specific workflow files. For public repositories without an already applicable event policy, GitHub is introducing a default policy that blocks pull_request_target. The policy is currently in evaluate mode. GitHub says it will enforce that default on 2 December 2026 for affected repositories that were using the default policy before general availability.

The announcement carries an editor's note dated 21 October 2026 changing the enforcement date to 2 December. Older copies of the guidance can show a different date. The current GitHub security reference and Actions policy guide agree on the updated date.

This does not mean every repository using the event will suddenly fail. GitHub says the default rule does not apply to private or internal repositories and does not replace an existing applicable Actions event policy. Organization or enterprise rules can still restrict a repository. Check the effective policy stack before treating an exception as necessary.

The correct response follows the job's real purpose:

What the job does

Preferred path

Policy decision

Builds, lints or tests PR code

Use pull_request with restricted token permissions

No pull_request_target exception required

Labels or comments on a PR using repository authority

Keep metadata-only pull_request_target with minimal scope

Consider a workflow-specific allow policy after security review

Executes code from an untrusted fork with secrets or a privileged token

Redesign the job; never preserve this pattern just to avoid failure

Do not broadly allow the unsafe workflow

An event name is not a security assessment. The crucial question is whether untrusted content can become code execution in a privileged job.

Why pullrequest and pullrequest_target have different trust

A fork contributor controls the proposed files, build scripts and dependencies. The normal pull_request event is designed to test those changes with GitHub's fork protections. For forked PRs, GitHub ordinarily limits GITHUB_TOKEN to read-only access, withholds repository secrets and applies the repository's approval policy. Default checkout selects the PR merge commit.

pull_request_target uses the workflow definition from the base repository's default branch. It can access the base repository's token and secrets. By default, checkout also selects the trusted base branch, so the mere use of this trigger does not execute fork code. These distinctions are documented in GitHub's dedicated security guide.

Trouble begins when a privileged job explicitly checks out the fork's head or merge commit and then runs it. Build tools, dependency installers and test runners can execute contributor-controlled scripts. GitHub's Security Lab write-up on pwn requests gives the mechanism and examples. The risk also applies when privileged workflow_run jobs download and execute artifacts produced by untrusted runs.

The boundary is therefore straightforward: untrusted code can be tested in the restricted CI context; repository credentials belong on the trusted side.

Identify the unsafe checkout-and-build combination

This deliberately unsafe example is included for recognition, not deployment:

# UNSAFE — DO NOT COPY INTO PRODUCTION
on:
  pull_request_target:

jobs:
  build-pr:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - run: npm ci
      - run: npm test

Here head.sha points checkout at the PR author's code. The npm commands may execute that code and its dependency scripts while the workflow has the more privileged base-repository context. Checkout alone is not execution; the subsequent commands complete the dangerous chain.

Do not assume removing actions/checkout fixes the issue if a job still uses git fetch, gh pr checkout, a downloaded artifact, or another mechanism to obtain and execute fork content. GitHub's secure-use reference also warns about command injection when attacker-controlled PR fields are interpolated into shell scripts. Pass metadata as data to a structured API or through appropriately quoted environment variables.

Audit existing workflows before changing policy

An initial inventory can be performed without triggering or modifying a workflow. Use a trusted checkout of the repository's default branch. On Linux, macOS or Git Bash, this searches for common patterns:

grep -RInE 'pull_request_target|allow-unsafe-pr-checkout|pull_request\.head|refs/pull/|gh pr checkout|workflow_run' .github/workflows/

An empty match is not proof of safety. Composite actions, reusable workflows, scripts, alternate checkout commands and downloaded artifacts also deserve inspection. Review the full job, not only the matching line.

For every affected workflow, record the event, whether it checks out PR code, whether it executes that code, which secrets it can access, its GITHUB_TOKEN permission set, and the action that would replace it. Look closely at npm ci, make, test commands, project scripts and build configuration: these may execute untrusted content without an obviously malicious command.

Next, check the token's real scope. GitHub's workflow syntax documentation allows explicit permissions at workflow or job level. Once you specify the granted scopes, unspecified scopes become none. Grant a job only the permissions it needs, rather than relying on the repository-wide defaults or giving every job write-all.

Finally, inspect the policy: at repository or organization level, navigate to Settings → Actions → Policies; enterprise owners use Policies → Actions → Policies. The governance documentation places Policy insights under the Actions policies sidebar. During evaluation, it can identify runs that would be denied when a rule becomes active. If policy controls are unavailable to your role, ask the relevant administrator rather than changing unrelated security settings.

Replace an ordinary PR test with pull_request

For a Node project with a committed lockfile and a working test script, this is an illustrative restricted CI workflow:

name: PR tests
on:
  pull_request:

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - run: npm ci
      - run: npm test

The pull_request event is the key security choice. The sample also requests only read access to repository contents; it does not ask for deployment secrets. Projects may need a supported Node setup, services or a different build command. Review and pin dependencies and actions according to your own policy before deployment. This example has not been executed against a live repository.

If a build needs a private registry, do not solve the problem by injecting a production credential into untrusted PR tests. Consider a limited test credential, a public dependency source, or a separate trusted post-review workflow. GitHub's secure-use guidance recommends least-privilege credentials and keeping privileged workflows away from untrusted code execution.

Keep privileged labelling separate from code execution

Some projects legitimately need the base repository to label incoming fork PRs. A narrow pull_request_target workflow can do this without running the PR's source. The following example assumes the repository already contains a label named needs-review and uses the GitHub CLI available on a GitHub-hosted Ubuntu runner:

name: Triage new pull requests
on:
  pull_request_target:
    types: [opened, reopened]

permissions:
  pull-requests: write

jobs:
  label:
    runs-on: ubuntu-latest
    steps:
      - name: Add the review label
        env:
          GH_TOKEN: ${{ github.token }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
        run: |
          gh api -X POST \
            "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/labels" \
            -f "labels[]=needs-review"

There is no source checkout and no command that executes PR-controlled files. The PR number comes from event metadata, the label is a constant chosen by the maintainer, and the workflow requests only the necessary write scope. GitHub's labels API reference describes the endpoint and confirms that pull-requests: write is sufficient for adding labels to pull requests.

The example still requires an effective policy that allows the event. Other approvals, branch filters or repository settings can influence its execution. It has not been live-tested and should be checked in a low-risk test repository first.

Do not add actions/checkout for the fork head and then run build commands in this job. If you also need tests, keep them in a separate restricted pull_request workflow. If metadata is taken from PR titles or bodies, avoid injecting it into shell source: use the API as data.

Decide which exception, if any, is justified

If your workflow can move to pull_request, leave GitHub's default pull_request_target restriction in place. If a reviewed metadata-only workflow must keep the privileged event, an authorized administrator can create or update an applicable event policy that explicitly permits it. GitHub's September release notice describes workflow-file targeting, so a carefully scoped exception is possible where the policy feature is available.

That is different from broadly allowing the event across every workflow. An allowlist is an execution-control decision; it is not proof that the allowed workflow is secure. GitHub also documents an allow-unsafe-pr-checkout: true opt-out for particular checkout protections. Do not use it to revive a privileged fork build that executes untrusted code.

Before enforcement, the meaningful sequence is to inspect policy insights, remove unneeded privileged workflows, review any truly necessary ones, apply only justified scoped policy exceptions, and record the expected workflow paths.

Verify behaviour and know when to stop

After editing workflows, inspect the committed default-branch YAML and verify the dangerous checkout-and-build path has been removed. Then check a fork PR in a non-sensitive repository under its normal approval settings. The ordinary pull_request tests should run without privileged secrets. Any retained pull_request_target metadata job should perform only its narrow action and use limited token scopes.

If a run does not start, check the event filter, fork approval requirements and Policy insights before assuming the YAML is broken. If a label operation fails, review its API response, the label's existence, permission scopes and policy; do not escalate to write-all as a diagnostic shortcut. Verify that your intended policy permits the reviewed workflow and does not accidentally open the same privilege to unrelated jobs.

The most durable design is to separate untrusted computation from privileged repository changes. That principle remains useful even in repositories unaffected by the December default policy.

Sources and further reading

Documentation and release guidance checked on 3 November 2026.

Share this post