September 27, 2026 路 #11
The pull request that brought its own script
This workflow labels pull requests and runs the repo's script. What is the exploit?
name: label
on:
pull_request_target:
types: [opened]
jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
ref: ${{ github.event.pull_request.head.sha }}
- run: ./scripts/label.shNext puzzle in UTC
One try. The first answer sticks.
How to fix it
pull_request_target runs in the base repo context, with secrets. Checking out the PR head and executing it lets a fork rewrite label.sh and steal those secrets.
pull_request_target exists so a workflow can comment on a pull request from a fork. It runs with the base repository's GITHUB_TOKEN and with access to secrets. The code it runs is supposed to be the base branch, which you trust.
This workflow then checks out github.event.pull_request.head.sha, which is the attacker's commit, and executes ./scripts/label.sh from that commit. The script can print GITHUB_TOKEN, call the API, or exfiltrate any secret in the environment. Pinning actions/checkout does not help. The untrusted code is the script, not the action.
Fix
Keep pull_request_target only for steps that do not execute PR content. Pass the title and number in as environment variables if you must read them.
on:
pull_request:
permissions:
contents: read
If you need to run the PR's own tests, use the pull_request event, leave secrets unavailable, and set permissions: contents: read. Never combine pull_request_target, a checkout of the head ref, and a run step.