From 9df5b8d2726542265b80b46d522f9919cf409d3b Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 7 Aug 2026 17:26:43 -0500 Subject: [PATCH] claude-code-review.yml: stop checking out the fork PR head actions/checkout refuses to check out a fork PR's head under pull_request_target by default (a hard-coded safety refusal added in a patch-level actions/checkout@v4 release), so review never actually ran against a genuine fork-originated PR - only against same-repo/upstream-branch PRs, which don't hit this check. The fix is not to opt back in via allow-unsafe-pr-checkout: true: claude-code-action already fetches and reads the PR's real content itself, internally, via the base repo's own refs/pull//head, so there is no reason for this job to put the fork's files in the workspace at all. The checkout step now fetches the base ref only, matching claude-code-action's own documented recommended pattern. pull_request_target always runs the workflow file from the base branch, so this PR's own claude-review check will still run the OLD broken workflow and fail the same way - that's expected, not a sign the fix doesn't work. Verification happens on a subsequent PR, after this merges to master. --- .github/workflows/claude-code-review.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 8efa338..51f5c39 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -15,9 +15,14 @@ name: Claude Code Review # head lives in this repo or an external fork (an arbitrary external fork # still can't trigger this secret-bearing job unless it's actually # jnasbyupgrade's own fork). The workflow file always comes from the base -# branch (master), so a PR cannot modify the reviewer that runs on it. We -# check out the PR head only for read context (persist-credentials: false) -# and never build or execute PR code. +# branch (master), so a PR cannot modify the reviewer that runs on it. The +# checkout step below fetches the BASE ref only, never the PR head - +# claude-code-action fetches and reads the PR's actual content itself, +# internally, via the base repo's own refs/pull//head (see its +# docs/security.md). Checking out the PR head directly here would put +# untrusted fork content in the workspace for no reason, and actions/checkout +# refuses to do so anyway under pull_request_target without an explicit +# (and here unnecessary) allow-unsafe-pr-checkout: true opt-in. on: pull_request_target: types: [opened, synchronize, reopened, ready_for_review] @@ -81,14 +86,13 @@ jobs: echo "decision=$decision" >> "$GITHUB_OUTPUT" echo "gate decision: $decision" - - name: Check out PR head (read-only context) + - name: Check out base ref if: steps.gate.outputs.decision == 'run' # Intentionally tracks the major-version tag (not a pinned SHA) so - # upstream fixes are picked up automatically. + # upstream fixes are picked up automatically. No repository:/ref: + # override - see the SECURITY comment at the top of this file for why. uses: actions/checkout@v4 with: - repository: ${{ github.event.pull_request.head.repo.full_name }} - ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 1 persist-credentials: false