diff --git a/packages/core/src/policy/policy-engine.test.ts b/packages/core/src/policy/policy-engine.test.ts index b6c11a079b..5606c49793 100644 --- a/packages/core/src/policy/policy-engine.test.ts +++ b/packages/core/src/policy/policy-engine.test.ts @@ -1762,6 +1762,39 @@ describe('PolicyEngine', () => { }); describe('shell command parsing failure', () => { + it('should return ALLOW in YOLO mode for dangerous commands due to heuristics override', async () => { + // Create an engine with YOLO mode and a sandbox manager that flags a command as dangerous + const rules: PolicyRule[] = [ + { + toolName: '*', + decision: PolicyDecision.ALLOW, + priority: 999, + modes: [ApprovalMode.YOLO], + }, + ]; + + const mockSandboxManager = new NoopSandboxManager(); + mockSandboxManager.isDangerousCommand = vi.fn().mockReturnValue(true); + mockSandboxManager.isKnownSafeCommand = vi.fn().mockReturnValue(false); + + engine = new PolicyEngine({ + rules, + approvalMode: ApprovalMode.YOLO, + sandboxManager: mockSandboxManager, + }); + + const result = await engine.check( + { + name: 'run_shell_command', + args: { command: 'powershell echo "dangerous"' }, + }, + undefined, + ); + + // Even though the command is flagged as dangerous, YOLO mode should preserve the ALLOW decision + expect(result.decision).toBe(PolicyDecision.ALLOW); + }); + it('should return ALLOW in YOLO mode even if shell command parsing fails', async () => { const { splitCommands } = await import('../utils/shell-utils.js'); const rules: PolicyRule[] = [ diff --git a/packages/core/src/policy/policy-engine.ts b/packages/core/src/policy/policy-engine.ts index eb5b141ba5..a9e049c74d 100644 --- a/packages/core/src/policy/policy-engine.ts +++ b/packages/core/src/policy/policy-engine.ts @@ -312,6 +312,13 @@ export class PolicyEngine { const parsedArgs = parsedObjArgs.map(extractStringFromParseEntry); if (this.sandboxManager.isDangerousCommand(parsedArgs)) { + if (this.approvalMode === ApprovalMode.YOLO) { + debugLogger.debug( + `[PolicyEngine.check] Command evaluated as dangerous, but YOLO mode is active. Preserving decision: ${command}`, + ); + return decision; + } + debugLogger.debug( `[PolicyEngine.check] Command evaluated as dangerous, forcing ASK_USER: ${command}`, ); diff --git a/packages/core/src/tools/shell.ts b/packages/core/src/tools/shell.ts index 44f0c85316..ad90423686 100644 --- a/packages/core/src/tools/shell.ts +++ b/packages/core/src/tools/shell.ts @@ -45,6 +45,7 @@ import { } from '../utils/shell-utils.js'; import { SHELL_TOOL_NAME } from './tool-names.js'; import { PARAM_ADDITIONAL_PERMISSIONS } from './definitions/base-declarations.js'; +import { ApprovalMode } from '../policy/types.js'; import type { MessageBus } from '../confirmation-bus/message-bus.js'; import { getShellDefinition } from './definitions/coreTools.js'; import { resolveToolDeclaration } from './definitions/resolver.js'; @@ -249,6 +250,10 @@ export class ShellToolInvocation extends BaseToolInvocation< abortSignal: AbortSignal, forcedDecision?: ForcedToolDecision, ): Promise { + if (this.context.config.getApprovalMode() === ApprovalMode.YOLO) { + return super.shouldConfirmExecute(abortSignal, forcedDecision); + } + if (this.params[PARAM_ADDITIONAL_PERMISSIONS]) { return this.getConfirmationDetails(abortSignal); }