* Validates a single command for path constraints and shell safety. * * This function: * 1. Parses the command arguments * 2. Checks if it's a path command (cd, ls, find) * 3. Validates for shell injection patterns * 4. Validates all paths are within allowed directories * * @param cmd - The
( cmd: string, cwd: string, toolPermissionContext: ToolPermissionContext, compoundCommandHasCd?: boolean, )
| 832 | * @returns PermissionResult - 'passthrough' if not a path command, otherwise validation result |
| 833 | */ |
| 834 | function validateSinglePathCommand( |
| 835 | cmd: string, |
| 836 | cwd: string, |
| 837 | toolPermissionContext: ToolPermissionContext, |
| 838 | compoundCommandHasCd?: boolean, |
| 839 | ): PermissionResult { |
| 840 | // SECURITY: Strip wrapper commands (timeout, nice, nohup, time) before extracting |
| 841 | // the base command. Without this, dangerous commands wrapped with these utilities |
| 842 | // would bypass path validation since the wrapper command (e.g., 'timeout') would |
| 843 | // be checked instead of the actual command (e.g., 'rm'). |
| 844 | // Example: 'timeout 10 rm -rf /' would otherwise see 'timeout' as the base command. |
| 845 | const strippedCmd = stripSafeWrappers(cmd) |
| 846 | |
| 847 | // Parse command into arguments, handling quotes and globs |
| 848 | const extractedArgs = parseCommandArguments(strippedCmd) |
| 849 | if (extractedArgs.length === 0) { |
| 850 | return { |
| 851 | behavior: 'passthrough', |
| 852 | message: 'Empty command - no paths to validate', |
| 853 | } |
| 854 | } |
| 855 | |
| 856 | // Check if this is a path command we need to validate |
| 857 | const [baseCmd, ...args] = extractedArgs |
| 858 | if (!baseCmd || !SUPPORTED_PATH_COMMANDS.includes(baseCmd as PathCommand)) { |
| 859 | return { |
| 860 | behavior: 'passthrough', |
| 861 | message: `Command '${baseCmd}' is not a path-restricted command`, |
| 862 | } |
| 863 | } |
| 864 | |
| 865 | // For read-only sed commands (e.g., sed -n '1,10p' file.txt), |
| 866 | // validate file paths as read operations instead of write operations. |
| 867 | // sed is normally classified as 'write' for path validation, but when the |
| 868 | // command is purely reading (line printing with -n), file args are read-only. |
| 869 | const operationTypeOverride = |
| 870 | baseCmd === 'sed' && sedCommandIsAllowedByAllowlist(strippedCmd) |
| 871 | ? ('read' as FileOperationType) |
| 872 | : undefined |
| 873 | |
| 874 | // Validate all paths are within allowed directories |
| 875 | const pathChecker = createPathChecker( |
| 876 | baseCmd as PathCommand, |
| 877 | operationTypeOverride, |
| 878 | ) |
| 879 | return pathChecker(args, cwd, toolPermissionContext, compoundCommandHasCd) |
| 880 | } |
| 881 | |
| 882 | /** |
| 883 | * Like validateSinglePathCommand but operates on AST-derived argv directly |
no test coverage detected