( input: z.infer<typeof BashTool.inputSchema>, toolPermissionContext: ToolPermissionContext, commandPrefixResult: CommandPrefixResult | null | undefined, compoundCommandHasCd?: boolean, astParseSucceeded?: boolean, )
| 1182 | * Processes an individual subcommand and applies prefix checks & suggestions |
| 1183 | */ |
| 1184 | export async function checkCommandAndSuggestRules( |
| 1185 | input: z.infer<typeof BashTool.inputSchema>, |
| 1186 | toolPermissionContext: ToolPermissionContext, |
| 1187 | commandPrefixResult: CommandPrefixResult | null | undefined, |
| 1188 | compoundCommandHasCd?: boolean, |
| 1189 | astParseSucceeded?: boolean, |
| 1190 | ): Promise<PermissionResult> { |
| 1191 | // 1. Check exact match first |
| 1192 | const exactMatchResult = bashToolCheckExactMatchPermission( |
| 1193 | input, |
| 1194 | toolPermissionContext, |
| 1195 | ) |
| 1196 | if (exactMatchResult.behavior !== 'passthrough') { |
| 1197 | return exactMatchResult |
| 1198 | } |
| 1199 | |
| 1200 | // 2. Check the command prefix |
| 1201 | const permissionResult = bashToolCheckPermission( |
| 1202 | input, |
| 1203 | toolPermissionContext, |
| 1204 | compoundCommandHasCd, |
| 1205 | ) |
| 1206 | // 2a. Deny/ask if command was explictly denied/asked |
| 1207 | if ( |
| 1208 | permissionResult.behavior === 'deny' || |
| 1209 | permissionResult.behavior === 'ask' |
| 1210 | ) { |
| 1211 | return permissionResult |
| 1212 | } |
| 1213 | |
| 1214 | // 3. Ask for permission if command injection is detected. Skip when the |
| 1215 | // AST parse already succeeded — tree-sitter has verified there are no |
| 1216 | // hidden substitutions or structural tricks, so the legacy regex-based |
| 1217 | // validators (backslash-escaped operators, etc.) would only add FPs. |
| 1218 | if ( |
| 1219 | !astParseSucceeded && |
| 1220 | !isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_COMMAND_INJECTION_CHECK) |
| 1221 | ) { |
| 1222 | const safetyResult = await bashCommandIsSafeAsync(input.command) |
| 1223 | |
| 1224 | if (safetyResult.behavior !== 'passthrough') { |
| 1225 | const decisionReason: PermissionDecisionReason = { |
| 1226 | type: 'other' as const, |
| 1227 | reason: |
| 1228 | safetyResult.behavior === 'ask' && safetyResult.message |
| 1229 | ? safetyResult.message |
| 1230 | : 'This command contains patterns that could pose security risks and requires approval', |
| 1231 | } |
| 1232 | |
| 1233 | return { |
| 1234 | behavior: 'ask', |
| 1235 | message: createPermissionRequestMessage(BashTool.name, decisionReason), |
| 1236 | decisionReason, |
| 1237 | suggestions: [], // Don't suggest saving a potentially dangerous command |
| 1238 | } |
| 1239 | } |
| 1240 | } |
| 1241 |
no test coverage detected