( command: string, config: Config, sessionAllowlist?: Set<string>, )
| 300 | * @returns An object detailing which commands are not allowed. |
| 301 | */ |
| 302 | export function checkCommandPermissions( |
| 303 | command: string, |
| 304 | config: Config, |
| 305 | sessionAllowlist?: Set<string>, |
| 306 | ): { |
| 307 | allAllowed: boolean; |
| 308 | disallowedCommands: string[]; |
| 309 | blockReason?: string; |
| 310 | isHardDenial?: boolean; |
| 311 | } { |
| 312 | // Disallow command substitution for security. |
| 313 | if (detectCommandSubstitution(command)) { |
| 314 | return { |
| 315 | allAllowed: false, |
| 316 | disallowedCommands: [command], |
| 317 | blockReason: |
| 318 | 'Command substitution using $(), <(), or >() is not allowed for security reasons', |
| 319 | isHardDenial: true, |
| 320 | }; |
| 321 | } |
| 322 | |
| 323 | const SHELL_TOOL_NAMES = ['run_shell_command', 'ShellTool']; |
| 324 | const normalize = (cmd: string): string => cmd.trim().replace(/\s+/g, ' '); |
| 325 | |
| 326 | const isPrefixedBy = (cmd: string, prefix: string): boolean => { |
| 327 | if (!cmd.startsWith(prefix)) { |
| 328 | return false; |
| 329 | } |
| 330 | return cmd.length === prefix.length || cmd[prefix.length] === ' '; |
| 331 | }; |
| 332 | |
| 333 | const extractCommands = (tools: string[]): string[] => |
| 334 | tools.flatMap((tool) => { |
| 335 | for (const toolName of SHELL_TOOL_NAMES) { |
| 336 | if (tool.startsWith(`${toolName}(`) && tool.endsWith(')')) { |
| 337 | return [normalize(tool.slice(toolName.length + 1, -1))]; |
| 338 | } |
| 339 | } |
| 340 | return []; |
| 341 | }); |
| 342 | |
| 343 | const coreTools = config.getCoreTools() || []; |
| 344 | const excludeTools = config.getExcludeTools() || []; |
| 345 | const commandsToValidate = splitCommands(command).map(normalize); |
| 346 | |
| 347 | // 1. Blocklist Check (Highest Priority) |
| 348 | if (SHELL_TOOL_NAMES.some((name) => excludeTools.includes(name))) { |
| 349 | return { |
| 350 | allAllowed: false, |
| 351 | disallowedCommands: commandsToValidate, |
| 352 | blockReason: 'Shell tool is globally disabled in configuration', |
| 353 | isHardDenial: true, |
| 354 | }; |
| 355 | } |
| 356 | const blockedCommands = extractCommands(excludeTools); |
| 357 | for (const cmd of commandsToValidate) { |
| 358 | if (blockedCommands.some((blocked) => isPrefixedBy(cmd, blocked))) { |
| 359 | return { |
no test coverage detected