CI: check-docs fails on every fork PR — pull_request_target checkout blocked by actions/checkout fork guard
Symptom
The check-docs job (.github/workflows/claude-docs-check.yml) fails about 6 seconds into the run on fork-based PRs, at the checkout step — before any documentation analysis runs. The Read the Docs build itself succeeds, so this is purely a workflow-configuration issue, not a content/docs problem.
Reproducible on multiple unrelated fork PRs:
The job logs:
##[error]Refusing to check out fork pull request code from a 'pull_request_target' workflow.
This workflow runs with the base repository's GITHUB_TOKEN, secrets, default-branch
cache scope, and runner access. Fetching and executing a fork's code in that trusted
context commonly leads to "pwn request" vulnerabilities. To opt in, review the risks
at https://gh.io/securely-using-pull_request_target and set
'allow-unsafe-pr-checkout: true' on the actions/checkout step.Root cause
The workflow triggers on pull_request_target (so it runs with the base repository's token and secrets), but its Checkout repository step explicitly checks out the fork's head commit:
- name: Checkout repository
uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0Current actions/checkout@v4 refuses exactly this combination — untrusted fork code inside a privileged pull_request_target context — unless allow-unsafe-pr-checkout: true is set.
For context, #2606 previously fixed a different fork failure in this same job (the claude-code-action "Actor does not have write permissions" error) by adding allowed_non_write_users: "*", which is present. That fix is correct but orthogonal: the run now trips the checkout guard before the action is ever reached.
Impact
Every external (fork) PR touching src/**/*.py gets a red check-docs check regardless of its content, which is noise for both contributors and reviewers and can mask genuinely required reviews.
Suggested fixes
Option 1 — Quick (opt into the checkout): add allow-unsafe-pr-checkout: true to the checkout step. This is the smallest change, but it deliberately re-enables the "pwn request" path. The job is read-only in intent, yet it currently grants pull-requests: write, issues: write, and id-token: write, and claude-code-action reads repository instructions (e.g. CLAUDE.md) from the checked-out tree — so untrusted fork code in this context is a real attack surface. If you go this route, I'd also suggest trimming permissions: to the minimum (the action only needs to post a PR comment/label — likely just pull-requests: write, dropping issues: write and id-token: write).
Option 2 — Safer (recommended): keep the privileged pull_request_target job from checking out fork code at all. Run on the base checkout and obtain the diff via gh pr diff <number> (the action's prompt already instructs it to do this), or split into two jobs: an unprivileged pull_request job that performs the analysis, and a pull_request_target job that only posts the resulting comment/label.
Note: because this is a
pull_request_targetworkflow, edits to it from a fork PR do not take effect until they land on the default branch, so this needs a maintainer-side merge/push (a fork PR can't validate the fix on its own CI).
Related
- #2692 ([Security] Agentic Workflow Injection in Claude Docs Check) and #2666 ([Security] Security issue in your GitHub CI workflow YAML files) report the security side of this same workflow — running untrusted fork code in a privileged
pull_request_targetcontext. The hard failure described here is essentially the checkout guard now enforcing exactly that risk: the current guard makes the job red on every fork PR, and the safer remediation (Option 2, not checking out fork code in the privileged job) addresses both the injection risk and this breakage together.
Happy to open a PR with whichever approach you prefer.
Source: vibrantlabsai/ragas