fix: ensure commands require explicit approval when auto-approval is disabled

- Fixed issue where commands were being auto-executed without user consent
- Added check to ensure BOTH autoApprovalEnabled AND alwaysAllowExecute must be true
- Added comprehensive tests to verify the fix
- Fixes #9606
This commit is contained in:
Roo Code 2025-11-26 09:47:38 +00:00
parent 3806b3d27d
commit 5b3b8c7384
2 changed files with 171 additions and 1 deletions

View file

@ -0,0 +1,165 @@
import { describe, it, expect } from "vitest"
import { checkAutoApproval } from "../index"
describe("checkAutoApproval", () => {
describe("command auto-approval", () => {
it("should ask user when autoApprovalEnabled is false, even if alwaysAllowExecute is true", async () => {
const state = {
autoApprovalEnabled: false,
alwaysAllowExecute: true,
allowedCommands: ["*"],
deniedCommands: [],
}
const result = await checkAutoApproval({
state,
ask: "command",
text: "echo hello",
})
expect(result.decision).toBe("ask")
})
it("should ask user when alwaysAllowExecute is false, even if autoApprovalEnabled is true", async () => {
const state = {
autoApprovalEnabled: true,
alwaysAllowExecute: false,
allowedCommands: ["*"],
deniedCommands: [],
}
const result = await checkAutoApproval({
state,
ask: "command",
text: "echo hello",
})
expect(result.decision).toBe("ask")
})
it("should auto-approve when both autoApprovalEnabled and alwaysAllowExecute are true and command is allowed", async () => {
const state = {
autoApprovalEnabled: true,
alwaysAllowExecute: true,
allowedCommands: ["echo"],
deniedCommands: [],
}
const result = await checkAutoApproval({
state,
ask: "command",
text: "echo hello",
})
expect(result.decision).toBe("approve")
})
it("should auto-deny when both autoApprovalEnabled and alwaysAllowExecute are true but command is denied", async () => {
const state = {
autoApprovalEnabled: true,
alwaysAllowExecute: true,
allowedCommands: ["*"],
deniedCommands: ["rm"],
}
const result = await checkAutoApproval({
state,
ask: "command",
text: "rm -rf /",
})
expect(result.decision).toBe("deny")
})
it("should ask user when both autoApprovalEnabled and alwaysAllowExecute are true but command is not in allowlist", async () => {
const state = {
autoApprovalEnabled: true,
alwaysAllowExecute: true,
allowedCommands: ["echo", "ls"],
deniedCommands: [],
}
const result = await checkAutoApproval({
state,
ask: "command",
text: "cat /etc/passwd",
})
expect(result.decision).toBe("ask")
})
it("should ask user when no text is provided", async () => {
const state = {
autoApprovalEnabled: true,
alwaysAllowExecute: true,
allowedCommands: ["*"],
deniedCommands: [],
}
const result = await checkAutoApproval({
state,
ask: "command",
text: undefined,
})
expect(result.decision).toBe("ask")
})
it("should ask user when both flags are undefined", async () => {
const state = {
allowedCommands: ["*"],
deniedCommands: [],
}
const result = await checkAutoApproval({
state,
ask: "command",
text: "echo hello",
})
expect(result.decision).toBe("ask")
})
it("should ask user when state is undefined", async () => {
const result = await checkAutoApproval({
state: undefined,
ask: "command",
text: "echo hello",
})
expect(result.decision).toBe("ask")
})
})
describe("other ask types", () => {
it("should handle non-command asks normally when autoApprovalEnabled is true", async () => {
const state = {
autoApprovalEnabled: true,
alwaysAllowReadOnly: true,
}
const result = await checkAutoApproval({
state,
ask: "tool",
text: JSON.stringify({ tool: "readFile", path: "test.txt" }),
})
expect(result.decision).toBe("approve")
})
it("should ask for non-command asks when autoApprovalEnabled is false", async () => {
const state = {
autoApprovalEnabled: false,
alwaysAllowReadOnly: true,
}
const result = await checkAutoApproval({
state,
ask: "tool",
text: JSON.stringify({ tool: "readFile", path: "test.txt" }),
})
expect(result.decision).toBe("ask")
})
})
})

View file

@ -117,7 +117,9 @@ export async function checkAutoApproval({
return { decision: "ask" }
}
if (state.alwaysAllowExecute === true) {
// Only auto-approve commands if both autoApprovalEnabled AND alwaysAllowExecute are true
// This prevents commands from being executed without user consent when auto-approval is disabled
if (state.autoApprovalEnabled === true && state.alwaysAllowExecute === true) {
const decision = getCommandDecision(text, state.allowedCommands || [], state.deniedCommands || [])
if (decision === "auto_approve") {
@ -128,6 +130,9 @@ export async function checkAutoApproval({
return { decision: "ask" }
}
}
// If auto-approval is disabled or alwaysAllowExecute is false, always ask the user
return { decision: "ask" }
}
if (ask === "tool") {