frontdoor rule · FD012

GitHub OIDC accepts the pull_request context

The pull_request context is accepted

Amazon Web ServicesUpdated Source on GitHub

Severity: high.

What it means

The subject condition accepts the pull-request context:

text
repo:acme/api:pull_request

GitHub issues that subject for every pull request in the repository. It carries no pull-request number, no source branch, no author, and no indication of whether the pull request came from a fork. There is nothing further to pin it to.

So a condition written against it does not say "this pull request". It says "any pull request in this repository, forever".

Why the credentials arrive before the review does

The usual mental model is that a pull request is safe because it has to be reviewed and merged before anything happens. The OIDC token does not wait for that. It is minted when the workflow starts, which is when the pull request is opened.

Required reviews, required status checks and branch protection all govern what gets merged. None of them govern what a pull-request workflow may do while it runs. If that workflow can assume a role, the role is reachable from an unmerged, unreviewed change.

Who can reach it depends on the repository:

  • Anyone with write access can open a pull request from a branch and get a token. If this role deploys to production, every collaborator can deploy to production without going through review, whatever the branch rules say.
  • Fork pull requests are more restricted by default: GitHub does not grant id-token: write to a workflow triggered by a fork's pull request unless the repository or organisation has been configured to send write tokens to them. That setting exists and is not rare. If it is on, the reachable set is everyone on GitHub.

What is deliberately not claimed here

An earlier version of this page said repo:acme/api:pull_request is the subject a pull_request_target workflow runs under. GitHub does not document the subject pull_request_target emits, and since December 2025 that trigger resolves GITHUB_REF to the repository's default branch, which points the other way.

The finding stands on what GitHub does document: one subject covers every pull request. pull_request_target is still worth auditing on its own, because it runs with the base repository's permissions against code from the pull request, but that is a separate problem from this condition and this rule does not assert a link between them.

How to fix it

  1. Work out which workflow needs this role, and whether it has to run on pull requests at all. Most roles caught by this rule are deployment roles that were given a subject wide enough to cover a test run once.

  2. Move the credentials to a subject a pull request cannot produce. A protected environment is the strongest option, because the environment claim is stable across trigger types and can carry its own reviewers:

    jsonc
    "StringEquals": {
      "token.actions.githubusercontent.com:sub": "repo:acme/api:environment:production"
    }

    A branch ref works too, if the deploy only ever runs from one branch:

    jsonc
    "StringEquals": {
      "token.actions.githubusercontent.com:sub": "repo:acme/api:ref:refs/heads/main"
    }
  3. If a pull-request workflow genuinely needs cloud access, to run integration tests for example, give it a separate role with read-only permissions in a test account, and suppress this finding against that role with the reason written down in .frontdoorignore.

  4. Check the fork token setting while you are here. Whether the repository sends write tokens to workflows from fork pull requests decides whether the reachable set is your collaborators or the internet.

A note on the immutable subject format

Repositories created after 15 July 2026 use subjects that carry numeric ids:

text
repo:acme@123456/api@456789:pull_request

The rule fires the same way, and any fix frontdoor suggests keeps the ids, because a condition written without them would not match the token at all.

frontdoor answers this once, when you run it; Secorvia checks it continuously across every account you connect, on a free tier that needs no card.