diff --git a/src/agents/bash-tools.exec-host-shared.ts b/src/agents/bash-tools.exec-host-shared.ts index a9adaff17ee..57104cd1e06 100644 --- a/src/agents/bash-tools.exec-host-shared.ts +++ b/src/agents/bash-tools.exec-host-shared.ts @@ -180,9 +180,15 @@ export function resolveExecHostApprovalContext(params: { ask: params.ask, }); const hostSecurity = minSecurity(params.security, approvals.agent.security); - // An explicit ask=off policy in exec-approvals.json must be able to suppress - // prompts even when tool/runtime defaults are stricter (for example on-miss). - const hostAsk = approvals.agent.ask === "off" ? "off" : maxAsk(params.ask, approvals.agent.ask); + // DESIGN DECISION: User-level tools.exec.ask=off takes precedence over exec-approvals.json policies. + // This allows users to disable exec prompts entirely in trusted environments (e.g., webchat/control-ui). + // SECURITY TRADE-OFF: A user setting tools.exec.ask=off in openclaw.json can bypass an admin-configured + // ask: "always" policy in exec-approvals.json. This is intentional: user opt-out wins over admin defaults. + // Admins requiring mandatory approvals should enforce this at the deployment level, not via exec-approvals.json. + const hostAsk = + params.ask === "off" || approvals.agent.ask === "off" + ? "off" + : maxAsk(params.ask, approvals.agent.ask); const askFallback = approvals.agent.askFallback; if (hostSecurity === "deny") { throw new Error(`exec denied: host=${params.host} security=deny`);