import { requiresExecApproval, type ExecAsk, type ExecSecurity } from "../infra/exec-approvals.js"; export type ExecApprovalDecision = "allow-once" | "allow-always" | null; export 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; } export function formatSystemRunAllowlistMissMessage(params?: { shellWrapperBlocked?: boolean; 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)" ); } if (params?.shellWrapperBlocked) { return ( "SYSTEM_RUN_DENIED: allowlist miss " + "(shell wrappers like sh/bash/zsh -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; approvalDecision: ExecApprovalDecision; approved?: boolean; isWindows: boolean; cmdInvocation: boolean; shellWrapperInvocation: boolean; }): SystemRunPolicyDecision { const shellWrapperBlocked = params.security === "allowlist" && params.shellWrapperInvocation; const windowsShellWrapperBlocked = shellWrapperBlocked && params.isWindows && params.cmdInvocation; const analysisOk = shellWrapperBlocked ? false : params.analysisOk; const allowlistSatisfied = shellWrapperBlocked ? false : params.allowlistSatisfied; const approvedByAsk = params.approvalDecision !== null || params.approved === true; if (params.security === "deny") { return { allowed: false, eventReason: "security=deny", errorMessage: "SYSTEM_RUN_DISABLED: security=deny", analysisOk, allowlistSatisfied, shellWrapperBlocked, windowsShellWrapperBlocked, requiresAsk: false, approvalDecision: params.approvalDecision, approvedByAsk, }; } const requiresAsk = requiresExecApproval({ ask: params.ask, security: params.security, analysisOk, allowlistSatisfied, }); if (requiresAsk && !approvedByAsk) { return { allowed: false, eventReason: "approval-required", errorMessage: "SYSTEM_RUN_DENIED: approval required", analysisOk, allowlistSatisfied, shellWrapperBlocked, windowsShellWrapperBlocked, requiresAsk, approvalDecision: params.approvalDecision, approvedByAsk, }; } if (params.security === "allowlist" && (!analysisOk || !allowlistSatisfied) && !approvedByAsk) { return { allowed: false, eventReason: "allowlist-miss", errorMessage: formatSystemRunAllowlistMissMessage({ shellWrapperBlocked, windowsShellWrapperBlocked, }), analysisOk, allowlistSatisfied, shellWrapperBlocked, windowsShellWrapperBlocked, requiresAsk, approvalDecision: params.approvalDecision, approvedByAsk, }; } return { allowed: true, analysisOk, allowlistSatisfied, shellWrapperBlocked, windowsShellWrapperBlocked, requiresAsk, approvalDecision: params.approvalDecision, approvedByAsk, }; }