Skip to content

ci: drop the redundant PR-head checkout from claude.yml (code scanning alert 71) #2069

Description

@cliffhall

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 detailsCheckout 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.

Metadata

Metadata

Assignees

Labels

choreMaintenance: deps, build tooling, CI, cleanup — no user-facing behavior changev2Issues and PRs for v2

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions