* Checks if a command should be auto-allowed when sandboxed. * Returns early if there are explicit deny/ask rules that should be respected. * * NOTE: This function should only be called when sandboxing and auto-allow are enabled. * * @param input - The bash tool input * @param toolPermissionCo
( input: z.infer<typeof BashTool.inputSchema>, toolPermissionContext: ToolPermissionContext, )
| 1268 | * - passthrough should not occur since we're in auto-allow mode |
| 1269 | */ |
| 1270 | function checkSandboxAutoAllow( |
| 1271 | input: z.infer<typeof BashTool.inputSchema>, |
| 1272 | toolPermissionContext: ToolPermissionContext, |
| 1273 | ): PermissionResult { |
| 1274 | const command = input.command.trim() |
| 1275 | |
| 1276 | // Check for explicit deny/ask rules on the full command (exact + prefix) |
| 1277 | const { matchingDenyRules, matchingAskRules } = matchingRulesForInput( |
| 1278 | input, |
| 1279 | toolPermissionContext, |
| 1280 | 'prefix', |
| 1281 | ) |
| 1282 | |
| 1283 | // Return immediately if there's an explicit deny rule on the full command |
| 1284 | if (matchingDenyRules[0] !== undefined) { |
| 1285 | return { |
| 1286 | behavior: 'deny', |
| 1287 | message: `Permission to use ${BashTool.name} with command ${command} has been denied.`, |
| 1288 | decisionReason: { |
| 1289 | type: 'rule', |
| 1290 | rule: matchingDenyRules[0], |
| 1291 | }, |
| 1292 | } |
| 1293 | } |
| 1294 | |
| 1295 | // SECURITY: For compound commands, check each subcommand against deny/ask |
| 1296 | // rules. Prefix rules like Bash(rm:*) won't match the full compound command |
| 1297 | // (e.g., "echo hello && rm -rf /" doesn't start with "rm"), so we must |
| 1298 | // check each subcommand individually. |
| 1299 | // IMPORTANT: Subcommand deny checks must run BEFORE full-command ask returns. |
| 1300 | // Otherwise a wildcard ask rule matching the full command (e.g., Bash(*echo*)) |
| 1301 | // would return 'ask' before a prefix deny rule on a subcommand (e.g., Bash(rm:*)) |
| 1302 | // gets checked, downgrading a deny to an ask. |
| 1303 | const subcommands = splitCommand(command) |
| 1304 | if (subcommands.length > 1) { |
| 1305 | let firstAskRule: PermissionRule | undefined |
| 1306 | for (const sub of subcommands) { |
| 1307 | const subResult = matchingRulesForInput( |
| 1308 | { command: sub }, |
| 1309 | toolPermissionContext, |
| 1310 | 'prefix', |
| 1311 | ) |
| 1312 | // Deny takes priority — return immediately |
| 1313 | if (subResult.matchingDenyRules[0] !== undefined) { |
| 1314 | return { |
| 1315 | behavior: 'deny', |
| 1316 | message: `Permission to use ${BashTool.name} with command ${command} has been denied.`, |
| 1317 | decisionReason: { |
| 1318 | type: 'rule', |
| 1319 | rule: subResult.matchingDenyRules[0], |
| 1320 | }, |
| 1321 | } |
| 1322 | } |
| 1323 | // Stash first ask match; don't return yet (deny across all subs takes priority) |
| 1324 | firstAskRule ??= subResult.matchingAskRules[0] |
| 1325 | } |
| 1326 | if (firstAskRule) { |
| 1327 | return { |
no test coverage detected