Repository navigation
[AI-1292] Unattended review-flow reviewer auto-approves its kcap MCP tools - #304
Conversation
…tools An unattended review-flow reviewer auto-approves its kcap-owned MCP tools via a per-reviewer LocalPermissionBridge token (daemon-originated secret → secure, race-free), alongside the unchanged unconditional submit_review_result carve-out. Daemon-only; requester-independent; interactive sessions unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- token secrecy: CSPRNG, unique, unlogged; note PtyEnvScrub already scrubs KCAP_DAEMON_URL - explicit request-classification order: validate live token BEFORE tool carve-outs - drop spoofable tool-name "kcap-owned" filter; token+MCP-config-lock is the authorization - bound each reviewer token to its launch allowlist (not a global kcap set) - add Lifecycle & concurrency (revoke-after-exit, concurrent reviewers, relaunch, submit-vs-teardown) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- explicit body parse/validation step before any approval; reviewer-token approval requires a well-formed tool-call (non-empty tool_name); malformed/missing → 400 - enforce the token-bound allowlist: orchestrator computes the post-strip allowlist ONCE (shared by MCP config + token registration), asserts kcap-owned + non-flow-starting before minting (else fall back to shared token); bridge enforces server-qualified names - broaden secrecy to real leak surfaces (transcript, run metadata, launch logs, recorded env) + matching absence assertions Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- invalid allowlist on a ReviewFlow launch now FAILS FAST (LaunchFailedAsync, no reviewer), never a shared-token fallback that would hang the unattended reviewer - make server-level granularity a deliberate, documented security contract (bare Codex names + no-hang requirement); reject exact-per-tool binding (would hang on an un-curated tool); add a contract guard test: review-flow-eligible kcap servers expose only read/submit tools - add missing/empty session_id body-validation tests (reviewer + shared token) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- reviewer token + out-of-allowlist server-qualified call → DENY directly (deny decision + diagnostic), never fall through to RequestPermissionAsync (that would hang the unattended reviewer); shared-token prompt path unchanged; a reviewer token never reaches the prompt step - back the server-level safety contract with an explicit, machine-checkable unattended-safe tool classification; guard derives the eligible server set from the flow catalog ∩ KcapMcpRegistry and checks each server's actual tools/list against that classification Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…fication + resolver Read-only ReviewFlowAutoApprovableServers (kcap-review, kcap-sessions), an explicit ReviewFlowUnattendedSafeTools classification, and TryResolveReviewFlowAllowlist (fail on any unknown/flow-starting/write server). Foundation for the reviewer-token auth. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…in LocalPermissionBridge Live-token registry (shared + per-reviewer tokens, each with its own listener prefix + bound read-only allowlist). CSPRNG tokens. Request classification: validate token then body; auto-approve submit_review_result (any live token, unchanged) and kcap tools on a reviewer token (bare → config-lock bounded; server-qualified → must be in the bound allowlist, else DENY not defer); shared token unchanged. Reviewer token never logged. +10 tests (33/33). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eview-flow launches On a ReviewFlow launch (bridge listening), resolve the read-only allowlist and mint a per-reviewer token, threading its URL as the reviewer's KCAP_DAEMON_URL; an invalid allowlist fails the launch fast (never a shared-token fallback that would hang). Revoke on every teardown path (early-return, catch, and CleanupAgentAsync after exit). AgentInstance carries the token. +4 orchestrator tests (45/45, stable). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…PtyEnvScrub Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ll-suite load Verify mint/revoke via a ReviewerTokenCountForTest seam + deterministic CleanupAgentForTest instead of real HTTP round-trips (a 5s-timeout-under-load risk); drop the port bind from the Default-launch test (no mint happens there). Fewer loopback binds → no "Address already in use" contention in the full parallel suite. Green 45/45 in isolation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PR Summary by QodoUnattended ReviewFlow: mint per-reviewer bridge token to auto-approve kcap MCP tools
AI Description
Diagram
High-Level Assessment
Files changed (8)
|
Code Review by Qodo
1.
|
…n, comments) - Security (bare tools auto-approved): gate the reviewer-token MINT on cmd.Vendor==codex, and make the bridge vendor-aware — a bare tool name is auto-approved ONLY for codex (config-lock bounded); any other vendor's bare name (e.g. Claude's built-in Bash) is DENIED. +tests (claude bare→deny, claude ReviewFlow→no token minted). - Correctness (under-validated body): reject whitespace/empty session_id and reviewer tool_name (IsNullOrWhiteSpace) before any auto-approval. +tests (whitespace session_id/tool_name → 400). - Rule: strip Linear issue IDs from new code comments; trim the verbose ones. Spec updated to the codex-gated model. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Problem
A daemon-hosted review-flow reviewer (Codex) is launched unattended (
--ask-for-approval never), but Codex still fires aPermissionRequesthook for MCP tool calls even under that flag.LocalPermissionBridgeauto-approved onlysubmit_review_result; every other MCP tool call routed toserver.RequestPermissionAsync— an interactive UI prompt with no human present. Now that the code-review flow whitelistskcap-reviewfor the reviewer, its firstget_pr_summarycall blocked the flow until someone manually clicked Allow, defeating the "unattended reviewer" promise. (Requester-independent: the code-review reviewer is always Codex; Claude reviewers avoid it viabypassPermissions.)Surfaced during the AI-1224 MCP-autoconfig E2E.
Fix
An unattended review-flow launch mints a per-reviewer
LocalPermissionBridgetoken (CSPRNG, its own listener prefix) bound to the launch's read-only kcap allowlist, and gives the reviewer that token's URL asKCAP_DAEMON_URL. Requests on a reviewer token auto-approve the reviewer's kcap tools; the shared (interactive) token is unchanged.Design highlights (spec went 5 rounds with the Codex spec-review flow —
docs/superpowers/specs/2026-07-09-reviewer-auto-approve-design.md):ReviewFlowAutoApprovableServers(kcap-review,kcap-sessions) — the reviewer's Codex MCP config already confines its callable tools to the launch allowlist.kcap-memory(writes) /kcap-flows(flow-starting) are not auto-approvable.session_id/tool_name→ 400) →submit_review_result(any live token, unchanged) → reviewer token (bare Codex name → allow; server-qualified → must be in the bound allowlist, else deny outright — never defer to a prompt that would hang).LaunchFailedAsync); it never falls back to the shared token.CleanupAgentAsyncafter the process exits). Concurrent reviewers get independent tokens.KCAP_DAEMON_URLwhichPtyEnvScrubalready scrubs.Tests
29 new tests, all green in isolation (repeatedly):
KcapMcpRegistryReviewFlowTests(8) — resolver accept/reject + static classification guard.LocalPermissionBridgeTests(+10, 33 total) — reviewer-token auto-approve (bare + qualified), out-of-allowlist deny, malformed/missing body → 400, shared-token unchanged (no escalation), revoked → 404, concurrency, token-never-logged.AgentOrchestratorVendorTests(+4, 45 total) — mint on ReviewFlow + revoke on cleanup, Default → no token, invalid allowlist → fail-fast,KCAP_DAEMON_URLscrubbed.Notes for review
tools/listcross-check (catching a new mutating tool added to an already-classified server) is left as a follow-up rather than spawning MCP servers in a unit test — happy to add it if you'd prefer it in-scope.SIGSEGVat startup on one run, a pre-existingTimeoutExceptionin an unrelatedFake_records_…test on another, and loopback-port contention). This is pre-existing native/PTY suite instability, not from this pure-managed-code change; the new tests are green in isolation. CI is the authoritative full-suite check.Closes AI-1292.