MCPcopy Create free account
hub / github.com/Zoo-Code-Org/Zoo-Code / getCommandDecision

Function getCommandDecision

src/core/auto-approval/commands.ts:257–305  ·  view source on GitHub ↗
(
	command: string,
	allowedCommands: string[],
	deniedCommands?: string[],
)

Source from the content-addressed store, hash-verified

255 * @returns Decision indicating whether to approve, deny, or ask user
256 */
257export function getCommandDecision(
258 command: string,
259 allowedCommands: string[],
260 deniedCommands?: string[],
261): CommandDecision {
262 if (!command?.trim()) {
263 return "auto_approve"
264 }
265
266 // Parse into sub-commands (split by &&, ||, ;, |). parseCommand also
267 // detects shell syntax errors (unterminated quotes, unclosed heredocs) and
268 // returns a non-null parseError in that case.
269 const { commands: subCommands, parseError } = parseCommand(command)
270
271 // Reject commands with a shell syntax error. An unterminated quote means
272 // the shell would report a parse error; in a compound command it may
273 // partially execute the well-formed prefix before aborting. Returning a
274 // distinct decision lets callers surface a useful message to the agent
275 // rather than silently presenting the command for user approval.
276 if (parseError !== null) {
277 return "malformed_command"
278 }
279
280 // Check each sub-command and collect decisions
281 const decisions: CommandDecision[] = subCommands.map((cmd) => {
282 // Remove simple PowerShell-like redirections (e.g. 2>&1) before checking
283 const cmdWithoutRedirection = cmd.replace(/\d*>&\d*/, "").trim()
284
285 return getSingleCommandDecision(cmdWithoutRedirection, allowedCommands, deniedCommands)
286 })
287
288 // If any sub-command is denied, deny the whole command
289 if (decisions.includes("auto_deny")) {
290 return "auto_deny"
291 }
292
293 // Require explicit user approval for dangerous patterns
294 if (containsDangerousSubstitution(command)) {
295 return "ask_user"
296 }
297
298 // If all sub-commands are approved, approve the whole command
299 if (decisions.every((decision) => decision === "auto_approve")) {
300 return "auto_approve"
301 }
302
303 // Otherwise, ask user
304 return "ask_user"
305}
306
307/**
308 * Get the decision for a single command using longest prefix match rule.

Callers 2

checkAutoApprovalFunction · 0.90
commands.spec.tsFile · 0.90

Calls 4

parseCommandFunction · 0.90
getSingleCommandDecisionFunction · 0.85
replaceMethod · 0.65

Tested by

no test coverage detected