Files
kandev/docs/decisions/2026-07-31-isolate-manual-pr-review-content.md
T
2026-07-31 19:51:06 +01:00

3.6 KiB

ADR-2026-07-31-isolate-manual-pr-review-content: Isolate Manual PR Review Content

Status: accepted Date: 2026-07-31 Area: infra, workflow, security

Context

Manual Claude review rounds are triggered by an issue_comment event, whose workflow and credentials come from the trusted default branch. Claude still needs the current pull request files, including files absent from the default branch. Checking an untrusted pull request head out at the workspace root makes repository-controlled instructions available to a privileged agent run and lets checkout-provided GitHub credentials remain in that worktree.

Decision

.github/workflows/claude.yml keeps the default branch at the workspace root and does not check out pull request content. For a top-level pull request comment containing @claude review, Claude may explore trusted default-branch files with read-only local tools and reads the exact current pull request through constrained gh pr diff and gh pr view commands. When semantic review requires a complete current-head file rather than a diff hunk, Claude uses the trusted .github/scripts/claude-read-pr-file.py helper.

The helper is bound to the event's pull request number by workflow environment, resolves that pull request's head SHA through a GET request, and fetches one repository-relative path at that SHA through a second GET request. It rejects path traversal, non-regular files, path mismatches, files over 256 KiB, and non-UTF-8 content. Claude is not granted generic gh api or arbitrary Python access.

The manual review uses an explicit prompt so the Claude action selects its automation mode rather than its write-capable mention mode. Its tool policy is review-only: it may read pull request context and post review comments, but it may not edit repository files, mutate Git history, push, or fetch arbitrary network content. Headless permission requests fail closed. Other @claude issue and comment requests keep the trusted default-branch workspace and the existing generic behavior.

Consequences

  • Manual reviews can inspect trusted surrounding code plus complete changed pull request files without placing pull request content in the privileged runner workspace.
  • The checkout-provided GitHub token is not retained in the trusted worktree. The Claude action still manages its own short-lived authentication, while its review process can write review comments but cannot use repository mutation or arbitrary network tools.
  • Pull request file reads are limited to text files small enough for review; large or binary assets remain available only as diff metadata.
  • Manual review mode is intentionally unsuitable for implementing requested changes; implementation requests continue through the generic mention path.

Alternatives Considered

  • Checking the pull request head out at the workspace root was rejected because it crosses the trusted workflow and untrusted content boundary.
  • Adding only persist-credentials: false was rejected because it does not constrain project instructions, agent tools, or network access.
  • Checking the pull request head out in a separate subdirectory and passing it with --add-dir was rejected because CodeQL still treats the untrusted checkout as flowing into a privileged comment-triggered action.
  • Keeping only the default-branch checkout without read-only pull request API access was rejected because Claude could not inspect newly added files.
  • Granting generic gh api access was rejected because it would let untrusted review content steer the agent to unrelated endpoints or repository writes.