fix(agents): honor effective exec policy for Claude live Bash#86330
Conversation
|
Codex review: needs maintainer review before merge. Reviewed May 25, 2026, 11:15 AM ET / 15:15 UTC. Summary PR surface: Source +213, Tests +771, Docs +5. Total +989 across 10 files. Reproducibility: yes. The linked issue gives a live Claude CLI reproduction path, and source inspection of current main confirms the live JSONL bridge has no Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge Security Review detailsBest possible solution: Land this as the canonical fix for #80819 after maintainer approval of the OpenClaw-authoritative permission-mode contract and green required checks; otherwise revise code, docs, and tests to preserve the older raw-arg override contract. Do we have a high-confidence way to reproduce the issue? Yes. The linked issue gives a live Claude CLI reproduction path, and source inspection of current main confirms the live JSONL bridge has no Is this the best way to solve the issue? Yes, with maintainer sign-off. The PR fixes the implicated bridge and launch-policy path with focused tests and docs, but the raw-args compatibility decision is the part that should remain a human approval gate. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 8da8bc4aadfc. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +213, Tests +771, Docs +5. Total +989 across 10 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
Opened this as a separate maintainer PR rather than pushing over #81971 because the follow-up changes go beyond the original contributor’s patch: they adjust the Claude live Bash security boundary, add session/approval policy plumbing, and add regression coverage. This branch intentionally preserves the original #81971 commits and layers one maintainer fix commit on top so CI can validate the complete stack cleanly. Once this is reviewed, we'll land this PR directly rather than use as the reference patch for #81971. This PR required much back & forth iteration and testing, so needed to own it here. |
|
ClawSweeper PR egg ✨ Hatched: 💎 rare Pearl Clawlet Hatch commandComment Hatchability rules:
Rarity: 💎 rare. What is this egg doing here?
|
f845db2 to
51f7160
Compare
|
Maintainer clarification on the compatibility point:
|
4de972d to
40a9070
Compare
40a9070 to
c6ffd0e
Compare
… policy Claude CLI emits stream-json control_request frames with subtype can_use_tool when it wants to use a native tool. The Claude live-session bridge previously dropped these frames, leaving Claude waiting for a control_response until the 180/600s no-output timeout fired (see openclaw#80819). Resolve the effective OpenClaw exec policy (per-agent tools.exec -> global tools.exec -> allowlist/on-miss defaults) once at session-start time and thread it through fingerprinting and the session record. When a can_use_tool request arrives: - Allow native Bash when the resolved policy is security=full, ask=off (matching the bypassPermissions semantics OpenClaw already documents). - Otherwise deny with a message that names the resolved policy and points the agent at OpenClaw MCP tools. Unsupported control_request subtypes get a structured error response instead of a silent no-op, and stray control_response frames are silently dropped. Adds spawn-test coverage for both allow and deny paths. Fixes openclaw#80819
…faults Resolve the effective exec policy through the same defaults that extensions/anthropic/cli-shared.ts:isOpenClawRequestedYolo and src/agents/exec-defaults.ts:resolveExecDefaults already use (security ?? "full", ask ?? "off") instead of falling back to a hand-rolled allowlist/on-miss default that disagreed with the rest of the codebase. Without this, a default-config OpenClaw deployment launches Claude with --permission-mode bypassPermissions but the bridge would still deny Bash control_requests, re-creating the openclaw#80819 stall for the very default-config case the issue reports. Also thread the effective Claude permission mode into the policy decision. Prefer the operator's explicit --permission-mode in argv, falling back to what normalizeClaudePermissionArgs would have inserted for an un-overridden launch. Native Bash is auto-allowed only when the effective mode is bypassPermissions AND tools.exec resolves to full/no-ask, so explicit raw-arg overrides like --permission-mode default or acceptEdits broaden Claude's native prompting and are honored by routing through deny. Adds a no-config regression test (default deployment allows Bash, no stall) and a permission-mode-override test (tools.exec full/off plus explicit --permission-mode default in raw args denies). Existing allow/deny tests continue to pass via the synthesized-mode fallback.
9580109 to
86c861e
Compare
…aw#86330) * fix(agents): answer Claude live control_request can_use_tool via exec policy Claude CLI emits stream-json control_request frames with subtype can_use_tool when it wants to use a native tool. The Claude live-session bridge previously dropped these frames, leaving Claude waiting for a control_response until the 180/600s no-output timeout fired (see openclaw#80819). Resolve the effective OpenClaw exec policy (per-agent tools.exec -> global tools.exec -> allowlist/on-miss defaults) once at session-start time and thread it through fingerprinting and the session record. When a can_use_tool request arrives: - Allow native Bash when the resolved policy is security=full, ask=off (matching the bypassPermissions semantics OpenClaw already documents). - Otherwise deny with a message that names the resolved policy and points the agent at OpenClaw MCP tools. Unsupported control_request subtypes get a structured error response instead of a silent no-op, and stray control_response frames are silently dropped. Adds spawn-test coverage for both allow and deny paths. Fixes openclaw#80819 * fix(agents): align Claude live control_request policy with backend defaults Resolve the effective exec policy through the same defaults that extensions/anthropic/cli-shared.ts:isOpenClawRequestedYolo and src/agents/exec-defaults.ts:resolveExecDefaults already use (security ?? "full", ask ?? "off") instead of falling back to a hand-rolled allowlist/on-miss default that disagreed with the rest of the codebase. Without this, a default-config OpenClaw deployment launches Claude with --permission-mode bypassPermissions but the bridge would still deny Bash control_requests, re-creating the openclaw#80819 stall for the very default-config case the issue reports. Also thread the effective Claude permission mode into the policy decision. Prefer the operator's explicit --permission-mode in argv, falling back to what normalizeClaudePermissionArgs would have inserted for an un-overridden launch. Native Bash is auto-allowed only when the effective mode is bypassPermissions AND tools.exec resolves to full/no-ask, so explicit raw-arg overrides like --permission-mode default or acceptEdits broaden Claude's native prompting and are honored by routing through deny. Adds a no-config regression test (default deployment allows Bash, no stall) and a permission-mode-override test (tools.exec full/off plus explicit --permission-mode default in raw args denies). Existing allow/deny tests continue to pass via the synthesized-mode fallback. * fix(agents): honor effective exec policy for Claude live Bash --------- Co-authored-by: Guillaume Thirry <[email protected]>
…aw#86330) * fix(agents): answer Claude live control_request can_use_tool via exec policy Claude CLI emits stream-json control_request frames with subtype can_use_tool when it wants to use a native tool. The Claude live-session bridge previously dropped these frames, leaving Claude waiting for a control_response until the 180/600s no-output timeout fired (see openclaw#80819). Resolve the effective OpenClaw exec policy (per-agent tools.exec -> global tools.exec -> allowlist/on-miss defaults) once at session-start time and thread it through fingerprinting and the session record. When a can_use_tool request arrives: - Allow native Bash when the resolved policy is security=full, ask=off (matching the bypassPermissions semantics OpenClaw already documents). - Otherwise deny with a message that names the resolved policy and points the agent at OpenClaw MCP tools. Unsupported control_request subtypes get a structured error response instead of a silent no-op, and stray control_response frames are silently dropped. Adds spawn-test coverage for both allow and deny paths. Fixes openclaw#80819 * fix(agents): align Claude live control_request policy with backend defaults Resolve the effective exec policy through the same defaults that extensions/anthropic/cli-shared.ts:isOpenClawRequestedYolo and src/agents/exec-defaults.ts:resolveExecDefaults already use (security ?? "full", ask ?? "off") instead of falling back to a hand-rolled allowlist/on-miss default that disagreed with the rest of the codebase. Without this, a default-config OpenClaw deployment launches Claude with --permission-mode bypassPermissions but the bridge would still deny Bash control_requests, re-creating the openclaw#80819 stall for the very default-config case the issue reports. Also thread the effective Claude permission mode into the policy decision. Prefer the operator's explicit --permission-mode in argv, falling back to what normalizeClaudePermissionArgs would have inserted for an un-overridden launch. Native Bash is auto-allowed only when the effective mode is bypassPermissions AND tools.exec resolves to full/no-ask, so explicit raw-arg overrides like --permission-mode default or acceptEdits broaden Claude's native prompting and are honored by routing through deny. Adds a no-config regression test (default deployment allows Bash, no stall) and a permission-mode-override test (tools.exec full/off plus explicit --permission-mode default in raw args denies). Existing allow/deny tests continue to pass via the synthesized-mode fallback. * fix(agents): honor effective exec policy for Claude live Bash --------- Co-authored-by: Guillaume Thirry <[email protected]>
…aw#86330) * fix(agents): answer Claude live control_request can_use_tool via exec policy Claude CLI emits stream-json control_request frames with subtype can_use_tool when it wants to use a native tool. The Claude live-session bridge previously dropped these frames, leaving Claude waiting for a control_response until the 180/600s no-output timeout fired (see openclaw#80819). Resolve the effective OpenClaw exec policy (per-agent tools.exec -> global tools.exec -> allowlist/on-miss defaults) once at session-start time and thread it through fingerprinting and the session record. When a can_use_tool request arrives: - Allow native Bash when the resolved policy is security=full, ask=off (matching the bypassPermissions semantics OpenClaw already documents). - Otherwise deny with a message that names the resolved policy and points the agent at OpenClaw MCP tools. Unsupported control_request subtypes get a structured error response instead of a silent no-op, and stray control_response frames are silently dropped. Adds spawn-test coverage for both allow and deny paths. Fixes openclaw#80819 * fix(agents): align Claude live control_request policy with backend defaults Resolve the effective exec policy through the same defaults that extensions/anthropic/cli-shared.ts:isOpenClawRequestedYolo and src/agents/exec-defaults.ts:resolveExecDefaults already use (security ?? "full", ask ?? "off") instead of falling back to a hand-rolled allowlist/on-miss default that disagreed with the rest of the codebase. Without this, a default-config OpenClaw deployment launches Claude with --permission-mode bypassPermissions but the bridge would still deny Bash control_requests, re-creating the openclaw#80819 stall for the very default-config case the issue reports. Also thread the effective Claude permission mode into the policy decision. Prefer the operator's explicit --permission-mode in argv, falling back to what normalizeClaudePermissionArgs would have inserted for an un-overridden launch. Native Bash is auto-allowed only when the effective mode is bypassPermissions AND tools.exec resolves to full/no-ask, so explicit raw-arg overrides like --permission-mode default or acceptEdits broaden Claude's native prompting and are honored by routing through deny. Adds a no-config regression test (default deployment allows Bash, no stall) and a permission-mode-override test (tools.exec full/off plus explicit --permission-mode default in raw args denies). Existing allow/deny tests continue to pass via the synthesized-mode fallback. * fix(agents): honor effective exec policy for Claude live Bash --------- Co-authored-by: Guillaume Thirry <[email protected]>
…aw#86330) * fix(agents): answer Claude live control_request can_use_tool via exec policy Claude CLI emits stream-json control_request frames with subtype can_use_tool when it wants to use a native tool. The Claude live-session bridge previously dropped these frames, leaving Claude waiting for a control_response until the 180/600s no-output timeout fired (see openclaw#80819). Resolve the effective OpenClaw exec policy (per-agent tools.exec -> global tools.exec -> allowlist/on-miss defaults) once at session-start time and thread it through fingerprinting and the session record. When a can_use_tool request arrives: - Allow native Bash when the resolved policy is security=full, ask=off (matching the bypassPermissions semantics OpenClaw already documents). - Otherwise deny with a message that names the resolved policy and points the agent at OpenClaw MCP tools. Unsupported control_request subtypes get a structured error response instead of a silent no-op, and stray control_response frames are silently dropped. Adds spawn-test coverage for both allow and deny paths. Fixes openclaw#80819 * fix(agents): align Claude live control_request policy with backend defaults Resolve the effective exec policy through the same defaults that extensions/anthropic/cli-shared.ts:isOpenClawRequestedYolo and src/agents/exec-defaults.ts:resolveExecDefaults already use (security ?? "full", ask ?? "off") instead of falling back to a hand-rolled allowlist/on-miss default that disagreed with the rest of the codebase. Without this, a default-config OpenClaw deployment launches Claude with --permission-mode bypassPermissions but the bridge would still deny Bash control_requests, re-creating the openclaw#80819 stall for the very default-config case the issue reports. Also thread the effective Claude permission mode into the policy decision. Prefer the operator's explicit --permission-mode in argv, falling back to what normalizeClaudePermissionArgs would have inserted for an un-overridden launch. Native Bash is auto-allowed only when the effective mode is bypassPermissions AND tools.exec resolves to full/no-ask, so explicit raw-arg overrides like --permission-mode default or acceptEdits broaden Claude's native prompting and are honored by routing through deny. Adds a no-config regression test (default deployment allows Bash, no stall) and a permission-mode-override test (tools.exec full/off plus explicit --permission-mode default in raw args denies). Existing allow/deny tests continue to pass via the synthesized-mode fallback. * fix(agents): honor effective exec policy for Claude live Bash --------- Co-authored-by: Guillaume Thirry <[email protected]>
…aw#86330) * fix(agents): answer Claude live control_request can_use_tool via exec policy Claude CLI emits stream-json control_request frames with subtype can_use_tool when it wants to use a native tool. The Claude live-session bridge previously dropped these frames, leaving Claude waiting for a control_response until the 180/600s no-output timeout fired (see openclaw#80819). Resolve the effective OpenClaw exec policy (per-agent tools.exec -> global tools.exec -> allowlist/on-miss defaults) once at session-start time and thread it through fingerprinting and the session record. When a can_use_tool request arrives: - Allow native Bash when the resolved policy is security=full, ask=off (matching the bypassPermissions semantics OpenClaw already documents). - Otherwise deny with a message that names the resolved policy and points the agent at OpenClaw MCP tools. Unsupported control_request subtypes get a structured error response instead of a silent no-op, and stray control_response frames are silently dropped. Adds spawn-test coverage for both allow and deny paths. Fixes openclaw#80819 * fix(agents): align Claude live control_request policy with backend defaults Resolve the effective exec policy through the same defaults that extensions/anthropic/cli-shared.ts:isOpenClawRequestedYolo and src/agents/exec-defaults.ts:resolveExecDefaults already use (security ?? "full", ask ?? "off") instead of falling back to a hand-rolled allowlist/on-miss default that disagreed with the rest of the codebase. Without this, a default-config OpenClaw deployment launches Claude with --permission-mode bypassPermissions but the bridge would still deny Bash control_requests, re-creating the openclaw#80819 stall for the very default-config case the issue reports. Also thread the effective Claude permission mode into the policy decision. Prefer the operator's explicit --permission-mode in argv, falling back to what normalizeClaudePermissionArgs would have inserted for an un-overridden launch. Native Bash is auto-allowed only when the effective mode is bypassPermissions AND tools.exec resolves to full/no-ask, so explicit raw-arg overrides like --permission-mode default or acceptEdits broaden Claude's native prompting and are honored by routing through deny. Adds a no-config regression test (default deployment allows Bash, no stall) and a permission-mode-override test (tools.exec full/off plus explicit --permission-mode default in raw args denies). Existing allow/deny tests continue to pass via the synthesized-mode fallback. * fix(agents): honor effective exec policy for Claude live Bash --------- Co-authored-by: Guillaume Thirry <[email protected]>
…aw#86330) * fix(agents): answer Claude live control_request can_use_tool via exec policy Claude CLI emits stream-json control_request frames with subtype can_use_tool when it wants to use a native tool. The Claude live-session bridge previously dropped these frames, leaving Claude waiting for a control_response until the 180/600s no-output timeout fired (see openclaw#80819). Resolve the effective OpenClaw exec policy (per-agent tools.exec -> global tools.exec -> allowlist/on-miss defaults) once at session-start time and thread it through fingerprinting and the session record. When a can_use_tool request arrives: - Allow native Bash when the resolved policy is security=full, ask=off (matching the bypassPermissions semantics OpenClaw already documents). - Otherwise deny with a message that names the resolved policy and points the agent at OpenClaw MCP tools. Unsupported control_request subtypes get a structured error response instead of a silent no-op, and stray control_response frames are silently dropped. Adds spawn-test coverage for both allow and deny paths. Fixes openclaw#80819 * fix(agents): align Claude live control_request policy with backend defaults Resolve the effective exec policy through the same defaults that extensions/anthropic/cli-shared.ts:isOpenClawRequestedYolo and src/agents/exec-defaults.ts:resolveExecDefaults already use (security ?? "full", ask ?? "off") instead of falling back to a hand-rolled allowlist/on-miss default that disagreed with the rest of the codebase. Without this, a default-config OpenClaw deployment launches Claude with --permission-mode bypassPermissions but the bridge would still deny Bash control_requests, re-creating the openclaw#80819 stall for the very default-config case the issue reports. Also thread the effective Claude permission mode into the policy decision. Prefer the operator's explicit --permission-mode in argv, falling back to what normalizeClaudePermissionArgs would have inserted for an un-overridden launch. Native Bash is auto-allowed only when the effective mode is bypassPermissions AND tools.exec resolves to full/no-ask, so explicit raw-arg overrides like --permission-mode default or acceptEdits broaden Claude's native prompting and are honored by routing through deny. Adds a no-config regression test (default deployment allows Bash, no stall) and a permission-mode-override test (tools.exec full/off plus explicit --permission-mode default in raw args denies). Existing allow/deny tests continue to pass via the synthesized-mode fallback. * fix(agents): honor effective exec policy for Claude live Bash --------- Co-authored-by: Guillaume Thirry <[email protected]>
…aw#86330) * fix(agents): answer Claude live control_request can_use_tool via exec policy Claude CLI emits stream-json control_request frames with subtype can_use_tool when it wants to use a native tool. The Claude live-session bridge previously dropped these frames, leaving Claude waiting for a control_response until the 180/600s no-output timeout fired (see openclaw#80819). Resolve the effective OpenClaw exec policy (per-agent tools.exec -> global tools.exec -> allowlist/on-miss defaults) once at session-start time and thread it through fingerprinting and the session record. When a can_use_tool request arrives: - Allow native Bash when the resolved policy is security=full, ask=off (matching the bypassPermissions semantics OpenClaw already documents). - Otherwise deny with a message that names the resolved policy and points the agent at OpenClaw MCP tools. Unsupported control_request subtypes get a structured error response instead of a silent no-op, and stray control_response frames are silently dropped. Adds spawn-test coverage for both allow and deny paths. Fixes openclaw#80819 * fix(agents): align Claude live control_request policy with backend defaults Resolve the effective exec policy through the same defaults that extensions/anthropic/cli-shared.ts:isOpenClawRequestedYolo and src/agents/exec-defaults.ts:resolveExecDefaults already use (security ?? "full", ask ?? "off") instead of falling back to a hand-rolled allowlist/on-miss default that disagreed with the rest of the codebase. Without this, a default-config OpenClaw deployment launches Claude with --permission-mode bypassPermissions but the bridge would still deny Bash control_requests, re-creating the openclaw#80819 stall for the very default-config case the issue reports. Also thread the effective Claude permission mode into the policy decision. Prefer the operator's explicit --permission-mode in argv, falling back to what normalizeClaudePermissionArgs would have inserted for an un-overridden launch. Native Bash is auto-allowed only when the effective mode is bypassPermissions AND tools.exec resolves to full/no-ask, so explicit raw-arg overrides like --permission-mode default or acceptEdits broaden Claude's native prompting and are honored by routing through deny. Adds a no-config regression test (default deployment allows Bash, no stall) and a permission-mode-override test (tools.exec full/off plus explicit --permission-mode default in raw args denies). Existing allow/deny tests continue to pass via the synthesized-mode fallback. * fix(agents): honor effective exec policy for Claude live Bash --------- Co-authored-by: Guillaume Thirry <[email protected]>
Summary
Follow-up to #81971. The original live Claude
control_requesthandling fixed the stdio response path, but the Bash allow/deny decision still needed to match OpenClaw's effective exec policy.This patch:
security=full,ask=offlaunches Claude with--permission-mode bypassPermissions, even if raw Claude args requested a non-bypass mode--permission-mode default, even if raw Claude args requestedbypassPermissionsor omitted--permission-modeagentIdorsessionKey, and sandbox-derived defaultssessionEntrythrough user and cron CLI pathsFixes #80819.
Related review context: #81971 and ClawSweeper comment #81971 (comment).
Real Behavior Proof
Ran a local real Claude CLI/API-backed proof against this branch with OpenClaw state isolated in a temporary
OPENCLAW_HOME,OPENCLAW_STATE_DIR, and config path. The proof exercised OpenClaw's Claude live stdio bridge throughexecutePreparedCliRun, not a mocked Claude process.Observed behavior:
security=full,ask=off) launched Claude with--permission-mode bypassPermissionssecurity=allowlist,ask=on-miss) launched Claude with--permission-mode defaultcontrol_requestcontrol_responseRedacted proof summary:
{ "realClaudeCli": true, "tempOpenClawHome": true, "allow": { "permissionMode": "bypassPermissions", "sideEffectObserved": true }, "deny": { "permissionMode": "default", "bashControlRequests": 1, "denyResponses": 1, "sideEffectBlocked": true } }Testing
node scripts/run-vitest.mjs src/agents/cli-runner.spawn.test.tsnode scripts/run-vitest.mjs src/agents/cli-backends.test.ts src/agents/cli-runner.spawn.test.ts src/cron/isolated-agent/run.session-key-isolation.test.ts src/cron/isolated-agent/run.cron-model-override-forwarding.test.tspnpm checkpnpm check:changelog-attributions.agents/skills/autoreview/scripts/autoreview --mode local --prompt 'Review PR #86330 after fixing the restrictive no-permission-mode path. OpenClaw effective exec policy is authoritative over Claude native permission mode, matching Codex: effective OpenClaw full/off must normalize Claude live to --permission-mode bypassPermissions and allow native Bash even if raw Claude args requested default/acceptEdits; any restrictive effective OpenClaw policy must normalize Claude live to --permission-mode default, including when Claude args omitted --permission-mode, and deny/route native Bash control_request through OpenClaw policy. Check for regressions, compatibility issues, missing tests, and stale PR/comment wording.' --parallel-tests 'node scripts/run-vitest.mjs src/agents/cli-runner.spawn.test.ts src/cron/isolated-agent/run.session-key-isolation.test.ts src/cron/isolated-agent/run.cron-model-override-forwarding.test.ts'Final autoreview result:
autoreview clean: no accepted/actionable findings reported.