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