( input: z.infer<typeof BashTool.inputSchema>, toolPermissionContext: ToolPermissionContext, compoundCommandHasCd?: boolean, astCommand?: SimpleCommand, )
| 1048 | } |
| 1049 | |
| 1050 | export const bashToolCheckPermission = ( |
| 1051 | input: z.infer<typeof BashTool.inputSchema>, |
| 1052 | toolPermissionContext: ToolPermissionContext, |
| 1053 | compoundCommandHasCd?: boolean, |
| 1054 | astCommand?: SimpleCommand, |
| 1055 | ): PermissionResult => { |
| 1056 | const command = input.command.trim() |
| 1057 | |
| 1058 | // 1. Check exact match first |
| 1059 | const exactMatchResult = bashToolCheckExactMatchPermission( |
| 1060 | input, |
| 1061 | toolPermissionContext, |
| 1062 | ) |
| 1063 | |
| 1064 | // 1a. Deny/ask if exact command has a rule |
| 1065 | if ( |
| 1066 | exactMatchResult.behavior === 'deny' || |
| 1067 | exactMatchResult.behavior === 'ask' |
| 1068 | ) { |
| 1069 | return exactMatchResult |
| 1070 | } |
| 1071 | |
| 1072 | // 2. Find all matching rules (prefix or exact) |
| 1073 | // SECURITY FIX: Check Bash deny/ask rules BEFORE path constraints to prevent bypass |
| 1074 | // via absolute paths outside the project directory (HackerOne report) |
| 1075 | // When AST-parsed, the subcommand is already atomic — skip the legacy |
| 1076 | // splitCommand re-check that misparses mid-word # as compound. |
| 1077 | const { matchingDenyRules, matchingAskRules, matchingAllowRules } = |
| 1078 | matchingRulesForInput(input, toolPermissionContext, 'prefix', { |
| 1079 | skipCompoundCheck: astCommand !== undefined, |
| 1080 | }) |
| 1081 | |
| 1082 | // 2a. Deny if command has a deny rule |
| 1083 | if (matchingDenyRules[0] !== undefined) { |
| 1084 | return { |
| 1085 | behavior: 'deny', |
| 1086 | message: `Permission to use ${BashTool.name} with command ${command} has been denied.`, |
| 1087 | decisionReason: { |
| 1088 | type: 'rule', |
| 1089 | rule: matchingDenyRules[0], |
| 1090 | }, |
| 1091 | } |
| 1092 | } |
| 1093 | |
| 1094 | // 2b. Ask if command has an ask rule |
| 1095 | if (matchingAskRules[0] !== undefined) { |
| 1096 | return { |
| 1097 | behavior: 'ask', |
| 1098 | message: createPermissionRequestMessage(BashTool.name), |
| 1099 | decisionReason: { |
| 1100 | type: 'rule', |
| 1101 | rule: matchingAskRules[0], |
| 1102 | }, |
| 1103 | } |
| 1104 | } |
| 1105 | |
| 1106 | // 3. Check path constraints |
| 1107 | // This check comes after deny/ask rules so explicit rules take precedence. |
no test coverage detected