What happened?
In PolicyEngine.checkShellCommand(), when the shell parser fails (no subcommands or syntax error), the YOLO-mode branch returns ALLOW whenever the matched rule happens to have no argsPattern — overriding an explicit ruleDecision === ASK_USER from the user's policy file. Every other path in this function preserves or tightens the decision (DENY is respected immediately; non-YOLO falls back to defaultDecision). This branch alone upgrades a restrictive decision to the most permissive one.
Affected code
packages/core/src/policy/policy-engine.ts:429-459:
if (subCommands.length === 0 || parsed?.hasError) {
if (ruleDecision === PolicyDecision.DENY) {
return { decision: PolicyDecision.DENY, rule };
}
if (this.approvalMode === ApprovalMode.YOLO) {
if (rule?.argsPattern) { /* ... DENY ... */ }
return { decision: PolicyDecision.ALLOW, rule }; // ignores ruleDecision === ASK_USER
}
return { decision: this.defaultDecision, rule };
How can this be reproduced?
- Workspace TOML:
{ toolName = "run_shell_command", decision = "ask_user" } (no commandPrefix/regex → no argsPattern).
- Run with YOLO approval mode a command the parser cannot fully parse (e.g., certain malformed/subshell constructs).
- Observed: executes with no prompt. Expected: ASK_USER per the explicit rule.
Why it matters
User-authored policy is a safety control; silently upgrading ask_user to allow-on-parse-failure inverts its meaning exactly in the scenario (unparseable commands) where conservatism matters most.
Suggested direction
In the YOLO branch, respect a matched rule's decision: if ruleDecision is set (ASK_USER/DENY), return it; only fall back to ALLOW when no rule matched or the matched decision was already allow.
Found by source audit on current main (commit 5411f113c); platform-independent. Open PR #26540 touches a different branch (shouldDowngradeForRedirection) and does not address this.
What happened?
In
PolicyEngine.checkShellCommand(), when the shell parser fails (no subcommands or syntax error), the YOLO-mode branch returnsALLOWwhenever the matched rule happens to have noargsPattern— overriding an explicitruleDecision === ASK_USERfrom the user's policy file. Every other path in this function preserves or tightens the decision (DENY is respected immediately; non-YOLO falls back todefaultDecision). This branch alone upgrades a restrictive decision to the most permissive one.Affected code
packages/core/src/policy/policy-engine.ts:429-459:How can this be reproduced?
{ toolName = "run_shell_command", decision = "ask_user" }(no commandPrefix/regex → no argsPattern).Why it matters
User-authored policy is a safety control; silently upgrading
ask_userto allow-on-parse-failure inverts its meaning exactly in the scenario (unparseable commands) where conservatism matters most.Suggested direction
In the YOLO branch, respect a matched rule's decision: if
ruleDecisionis set (ASK_USER/DENY), return it; only fall back to ALLOW when no rule matched or the matched decision was already allow.Found by source audit on current
main(commit5411f113c); platform-independent. Open PR #26540 touches a different branch (shouldDowngradeForRedirection) and does not address this.