( command: PathCommand, args: string[], cwd: string, toolPermissionContext: ToolPermissionContext, compoundCommandHasCd?: boolean, operationTypeOverride?: FileOperationType, )
| 601 | } |
| 602 | |
| 603 | function validateCommandPaths( |
| 604 | command: PathCommand, |
| 605 | args: string[], |
| 606 | cwd: string, |
| 607 | toolPermissionContext: ToolPermissionContext, |
| 608 | compoundCommandHasCd?: boolean, |
| 609 | operationTypeOverride?: FileOperationType, |
| 610 | ): PermissionResult { |
| 611 | const extractor = PATH_EXTRACTORS[command] |
| 612 | const paths = extractor(args) |
| 613 | const operationType = operationTypeOverride ?? COMMAND_OPERATION_TYPE[command] |
| 614 | |
| 615 | // SECURITY: Check command-specific validators (e.g., to block flags that could bypass path validation) |
| 616 | // Some commands like mv/cp have flags (--target-directory=PATH) that can bypass path extraction, |
| 617 | // so we block ALL flags for these commands to ensure security. |
| 618 | const validator = COMMAND_VALIDATOR[command] |
| 619 | if (validator && !validator(args)) { |
| 620 | return { |
| 621 | behavior: 'ask', |
| 622 | message: `${command} with flags requires manual approval to ensure path safety. For security, Claude Code cannot automatically validate ${command} commands that use flags, as some flags like --target-directory=PATH can bypass path validation.`, |
| 623 | decisionReason: { |
| 624 | type: 'other', |
| 625 | reason: `${command} command with flags requires manual approval`, |
| 626 | }, |
| 627 | } |
| 628 | } |
| 629 | |
| 630 | // SECURITY: Block write operations in compound commands containing 'cd' |
| 631 | // This prevents bypassing path safety checks via directory changes before operations. |
| 632 | // Example attack: cd .claude/ && mv test.txt settings.json |
| 633 | // This would bypass the check for .claude/settings.json because paths are resolved |
| 634 | // relative to the original CWD, not accounting for the cd's effect. |
| 635 | // |
| 636 | // ALTERNATIVE APPROACH: Instead of blocking all writes with cd, we could track the |
| 637 | // effective CWD through the command chain (e.g., after "cd .claude/", subsequent |
| 638 | // commands would be validated with CWD=".claude/"). This would be more permissive |
| 639 | // but requires careful handling of: |
| 640 | // - Relative paths (cd ../foo) |
| 641 | // - Special cd targets (cd ~, cd -, cd with no args) |
| 642 | // - Multiple cd commands in sequence |
| 643 | // - Error cases where cd target cannot be determined |
| 644 | // For now, we take the conservative approach of requiring manual approval. |
| 645 | if (compoundCommandHasCd && operationType !== 'read') { |
| 646 | return { |
| 647 | behavior: 'ask', |
| 648 | message: `Commands that change directories and perform write operations require explicit approval to ensure paths are evaluated correctly. For security, Claude Code cannot automatically determine the final working directory when 'cd' is used in compound commands.`, |
| 649 | decisionReason: { |
| 650 | type: 'other', |
| 651 | reason: |
| 652 | 'Compound command contains cd with write operation - manual approval required to prevent path resolution bypass', |
| 653 | }, |
| 654 | } |
| 655 | } |
| 656 | |
| 657 | for (const path of paths) { |
| 658 | const { allowed, resolvedPath, decisionReason } = validatePath( |
| 659 | path, |
| 660 | cwd, |
no test coverage detected