| |
| import { resolveAgentConfig } from "../agents/agent-scope-config.js"; |
| import type { OpenClawConfig } from "../config/types.openclaw.js"; |
| import { |
| requiresExecApproval, |
| resolveExecModePolicy, |
| type ExecAsk, |
| type ExecSecurity, |
| } from "../infra/exec-approvals.js"; |
| import { applyExecPolicyLayer } from "../infra/exec-policy.js"; |
|
|
| |
| export function resolveNodeExecConfigPolicy(params: { |
| cfg: OpenClawConfig; |
| agentId: string | undefined; |
| defaultSecurity: ExecSecurity; |
| defaultAsk: ExecAsk; |
| }) { |
| const agentExec = params.agentId |
| ? resolveAgentConfig(params.cfg, params.agentId)?.tools?.exec |
| : undefined; |
| const globalExec = params.cfg.tools?.exec; |
| const layered = applyExecPolicyLayer( |
| applyExecPolicyLayer({ security: params.defaultSecurity, ask: params.defaultAsk }, globalExec), |
| agentExec, |
| ); |
| return { agentExec, globalExec, ...resolveExecModePolicy(layered) }; |
| } |
|
|
| type ExecApprovalDecision = "allow-once" | "allow-always" | null; |
|
|
| type SystemRunPolicyDecision = { |
| analysisOk: boolean; |
| allowlistSatisfied: boolean; |
| shellWrapperBlocked: boolean; |
| windowsShellWrapperBlocked: boolean; |
| requiresAsk: boolean; |
| approvalDecision: ExecApprovalDecision; |
| approvedByAsk: boolean; |
| } & ( |
| | { |
| allowed: true; |
| } |
| | { |
| allowed: false; |
| eventReason: "security=deny" | "approval-required" | "allowlist-miss"; |
| errorMessage: string; |
| } |
| ); |
|
|
| |
| export function resolveExecApprovalDecision(value: unknown): ExecApprovalDecision { |
| if (value === "allow-once" || value === "allow-always") { |
| return value; |
| } |
| return null; |
| } |
|
|
| function formatSystemRunAllowlistMissMessage(params?: { |
| windowsShellWrapperBlocked?: boolean; |
| }): string { |
| if (params?.windowsShellWrapperBlocked) { |
| return ( |
| "SYSTEM_RUN_DENIED: allowlist miss " + |
| "(Windows shell wrappers like cmd.exe /c require approval; " + |
| "approve once/always or run with --ask on-miss|always)" |
| ); |
| } |
| return "SYSTEM_RUN_DENIED: allowlist miss"; |
| } |
|
|
| |
| export function evaluateSystemRunPolicy(params: { |
| security: ExecSecurity; |
| ask: ExecAsk; |
| analysisOk: boolean; |
| allowlistSatisfied: boolean; |
| durableApprovalSatisfied?: boolean; |
| approvalDecision: ExecApprovalDecision; |
| approved?: boolean; |
| isWindows: boolean; |
| cmdInvocation: boolean; |
| shellWrapperInvocation: boolean; |
| }): SystemRunPolicyDecision { |
| |
| |
| |
| |
| const windowsShellWrapperBlocked = |
| params.security === "allowlist" && |
| params.shellWrapperInvocation && |
| params.isWindows && |
| params.cmdInvocation; |
| const shellWrapperBlocked = windowsShellWrapperBlocked; |
| const analysisOk = shellWrapperBlocked ? false : params.analysisOk; |
| const allowlistSatisfied = shellWrapperBlocked ? false : params.allowlistSatisfied; |
| const approvedByAsk = params.approvalDecision !== null || params.approved === true; |
| const requiresAsk = |
| params.security !== "deny" && |
| requiresExecApproval({ |
| ask: params.ask, |
| security: params.security, |
| analysisOk, |
| allowlistSatisfied, |
| durableApprovalSatisfied: params.durableApprovalSatisfied, |
| }); |
| const context = { |
| analysisOk, |
| allowlistSatisfied, |
| shellWrapperBlocked, |
| windowsShellWrapperBlocked, |
| requiresAsk, |
| approvalDecision: params.approvalDecision, |
| approvedByAsk, |
| }; |
|
|
| if (params.security === "deny") { |
| return { |
| allowed: false, |
| eventReason: "security=deny", |
| errorMessage: "SYSTEM_RUN_DISABLED: security=deny", |
| ...context, |
| }; |
| } |
|
|
| if (requiresAsk && !approvedByAsk) { |
| return { |
| allowed: false, |
| eventReason: "approval-required", |
| errorMessage: "SYSTEM_RUN_DENIED: approval required", |
| ...context, |
| }; |
| } |
|
|
| if ( |
| params.security === "allowlist" && |
| (!analysisOk || !allowlistSatisfied) && |
| !approvedByAsk && |
| !params.durableApprovalSatisfied |
| ) { |
| return { |
| allowed: false, |
| eventReason: "allowlist-miss", |
| errorMessage: formatSystemRunAllowlistMissMessage({ |
| windowsShellWrapperBlocked, |
| }), |
| ...context, |
| }; |
| } |
|
|
| return { |
| allowed: true, |
| ...context, |
| }; |
| } |
|
|