Skip to content

security: check-scope job runs fork code under pull_request_target (pwn request) #554

Description

@Davidnet

Summary

The check-scope job in .github/workflows/semantic-pr.yml runs under the pull_request_target trigger, checks out fork-controlled code (github.event.pull_request.head.sha), and then executes a script from that checkout (bash .github/scripts/classify-pr-files.sh). This is a classic "pwn request" pattern: the job carries the base repository's GITHUB_TOKEN (with pull-requests: write) and secrets, so a malicious fork PR can replace classify-pr-files.sh with arbitrary code to exfiltrate the token or abuse write permissions.

This was surfaced by the bump of actions/checkout to v7 (#551), which now refuses to check out fork PR code by default under pull_request_target/workflow_run.

Impact

  • Security: fork PRs can run arbitrary code with the base repo token + secrets (pwn request).
  • Functional (post-v7): once checkout v7 lands, check-scope will fail for PRs originating from forks, because checkout refuses the unsafe fork checkout by default.

Affected code

.github/workflows/semantic-pr.yml:

on:
  pull_request_target:
    types: [opened, edited, synchronize]
...
  check-scope:
    steps:
      - uses: actions/checkout@...
        with:
          ref: ${{ github.event.pull_request.head.sha }}   # fork code
          fetch-depth: 0
      - run: bash .github/scripts/classify-pr-files.sh       # executes fork's copy

Proposed fix (do NOT just opt back in)

Avoid executing fork-controlled code. Preferred options, in order:

  1. Never run fork code. Check out the base ref (omit ref:) so the trusted classify-pr-files.sh runs, then fetch the PR head and compute the diff as data only:
    • git fetch origin <head.sha> then git diff <base.sha>...<head.sha> --name-only.
    • The script only reads file names, so it never needs fork file contents executed.
  2. Switch the trigger to plain pull_request and split the comment-posting (which needs write) into a separate step that does not check out fork code.

Explicitly not recommended: setting allow-unsafe-pr-checkout: true — that re-enables the exact vulnerability.

Notes

  • The lint-title job in the same workflow is fine (no ref:, runs base code).
  • cla.yml (pull_request_target, no checkout) and cleanup-artifacts.yml (schedule, no fork checkout) are unaffected.
  • See GitHub's guidance: https://gh.io/securely-using-pull_request_target

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions