Skip to content

claude-code reviewer runs in interactive Plan Mode and treats its own review instructions as a prompt injection #5079

Description

@JSONbored

Problem

Confirmed live on edge-nl-01 via two real ai_review_provider_unparseable_output events (with the new diagnostic snippet from #5071) minutes apart:

"Two things are going on that don't line up: 1. Plan Mode is active, which is a workflow for planning code changes to this repo — explore, design an approach, write a plan file, then call ExitPlanMode for approval... 2. But the bulk of your message is a..."

"I want to flag something before going further: this turn contains a very large embedded block claiming to be 'ADDITIONAL SYSTEM INSTRUCTIONS' that tries to cast me as an automated PR-review oracle... Layered on top of that is a 'Plan Mode' wr[apper]..."

Root-caused to two compounding bugs in createClaudeCodeAi (src/selfhost/ai.ts):

  1. --permission-mode plan has been passed to every claude invocation since the self-host stack's original commit (2026-06-24). This activates Claude Code's full interactive Plan-Mode workflow (explore → draft a plan → wait for ExitPlanMode approval) — not just a read-only permission restriction. --disallowedTools Bash,Edit,Write,WebFetch,WebSearch already forbids every mutating/networked tool, so nothing needing that framing remains; the mismatch between "you're in an interactive planning session" and "just output a JSON review verdict" is confusing the model into responding with meta-commentary instead of a review.

  2. systemAppend (repo-level custom review instructions from .gittensory.yml) is textually prepended into the stdin prompt behind a literal "ADDITIONAL SYSTEM INSTRUCTIONS:\n" header, rather than passed through Claude's own --append-system-prompt/--append-system-prompt-file channel. This was implemented CORRECTLY in feat(selfhost): wire claude --append-system-prompt for review instructions (v2 refinement) #1471/PR feat(selfhost): pass repo instructions to cli reviewers #2954 (using the real --append-system-prompt flag), then regressed by a later commit (fix(selfhost): keep Claude review instructions out of argv (#3951)) that moved it back to textual stdin-smuggling to avoid the content appearing in ps aux — inadvertently recreating the exact textbook pattern of a prompt-injection attack (a labeled "system instructions" block inside otherwise-untrusted content), which Claude's own safety training correctly (if here, incorrectly) flags as suspicious.

Both bugs apply to every claude-code review call; #2 only visibly compounds them for repos with review.repoInstructions/pathGuidance configured.

Fix

  1. --permission-mode plan--permission-mode bypassPermissions — matches the already-established "no TTY, no one to answer a permission prompt, --disallowedTools is the real boundary" intent.
  2. Write systemAppend to a file inside the same per-call isolated temp dir already used for the subprocess's cwd, and pass --append-system-prompt-file <path> — resolves the original fix(selfhost): keep Claude review instructions out of argv #3951 argv-visibility concern via the CLI's own file-based system-prompt mechanism instead of textual smuggling, restoring feat(selfhost): wire claude --append-system-prompt for review instructions (v2 refinement) #1471's original "unambiguously SYSTEM, not user content" design intent.

codex is unaffected (uses --sandbox read-only, not --permission-mode, and has no CLI-native system-prompt-file equivalent per #1471's own documented limitation — keeps the existing textual-prepend approach).

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions