Summary
Code scanning alert #71 (actions/untrusted-checkout/high, CodeQL) is open against main at .github/workflows/claude.yml:91 — the Checkout PR branch step. That is the step #1966 added the is_fork == 'false' guard to, so the alert is current, not stale.
The alert is a false positive on its own terms: after #1966 there is no path where a fork head is checked out. But investigating it surfaced a real cleanup — the flagged step is redundant, and removing it clears the alert legitimately rather than by dismissal.
Why the step is redundant
anthropics/claude-code-action checks out the PR branch itself. From src/github/operations/branch.ts at the pinned v1.0.190:
// Handle open PR: Checkout the PR branch
execGit(["fetch", "origin", ...depthArgs, branchName]);
execGit(["checkout", branchName, "--"]);
The action's own docs/security.md names a bare base-ref checkout (no ref:) as the preferred pattern, and upstream's own .github/workflows/claude.yml is exactly that: one actions/checkout, fetch-depth: 1, no PR lookup and no head checkout. Our Get PR details → Checkout PR branch pair is vestigial from the v1 example workflow inherited in #1869; the action re-checks-out the same branch moments later regardless.
Why CodeQL can't be satisfied any other way
The rule is syntactic: privileged trigger (issue_comment) + actions/checkout with a ref: derived from PR data ⇒ high. It does not evaluate steps.pr.outputs.is_fork (a runtime step output), the job-level author_association gate, or the repo's pull_request_creation_policy: collaborators_only (verified against the API). All three mitigations are invisible to it, so no amount of hardening around the step clears the alert while the step exists.
Proposed change
- Delete the
Checkout PR branch step (the flagged line).
- Make
Checkout repository unconditional — base ref, no ref: input.
- Drop the now-unused
sha output from Get PR details.
- Keep
Get PR details, Decline fork PR, and the is_fork gate on Run Claude Code, unchanged.
That last point is load-bearing. The action will check out a fork PR itself via refs/pull/N/head (if (prData.isCrossRepository) in the same source file), so the is_fork gate is what stops untrusted code reaching the workspace at all. This change removes our untrusted-ref checkout; it must not remove the gate.
Notes
- Workflows run from the default branch, so this is inert until
v2/main reaches main at the next milestone merge; the alert clears on the CodeQL run after that.
- No test surface — workflow file only.
Summary
Code scanning alert #71 (
actions/untrusted-checkout/high, CodeQL) is open againstmainat.github/workflows/claude.yml:91— theCheckout PR branchstep. That is the step #1966 added theis_fork == 'false'guard to, so the alert is current, not stale.The alert is a false positive on its own terms: after #1966 there is no path where a fork head is checked out. But investigating it surfaced a real cleanup — the flagged step is redundant, and removing it clears the alert legitimately rather than by dismissal.
Why the step is redundant
anthropics/claude-code-actionchecks out the PR branch itself. Fromsrc/github/operations/branch.tsat the pinned v1.0.190:The action's own
docs/security.mdnames a bare base-ref checkout (noref:) as the preferred pattern, and upstream's own.github/workflows/claude.ymlis exactly that: oneactions/checkout,fetch-depth: 1, no PR lookup and no head checkout. OurGet PR details→Checkout PR branchpair is vestigial from the v1 example workflow inherited in #1869; the action re-checks-out the same branch moments later regardless.Why CodeQL can't be satisfied any other way
The rule is syntactic: privileged trigger (
issue_comment) +actions/checkoutwith aref:derived from PR data ⇒ high. It does not evaluatesteps.pr.outputs.is_fork(a runtime step output), the job-levelauthor_associationgate, or the repo'spull_request_creation_policy: collaborators_only(verified against the API). All three mitigations are invisible to it, so no amount of hardening around the step clears the alert while the step exists.Proposed change
Checkout PR branchstep (the flagged line).Checkout repositoryunconditional — base ref, noref:input.shaoutput fromGet PR details.Get PR details,Decline fork PR, and theis_forkgate onRun Claude Code, unchanged.That last point is load-bearing. The action will check out a fork PR itself via
refs/pull/N/head(if (prData.isCrossRepository)in the same source file), so theis_forkgate is what stops untrusted code reaching the workspace at all. This change removes our untrusted-ref checkout; it must not remove the gate.Notes
v2/mainreachesmainat the next milestone merge; the alert clears on the CodeQL run after that.