PreToolUse Hooks: Prevent Dangerous Command String Replacements
Bug Description
⏺ String Replacement Risks in Hook Command Modification
Summary
When PreToolUse hooks gain the ability to modify commands (per issue #2814), naive string replacement could cause catastrophic unintended modifications.
Documentation should warn users and provide guidance on safe command parsing.
Context
Once hooks can modify tool_input (currently broken), users will likely implement command corrections like python→python3. However, simple string replacement is
dangerous.
The Problem
A naive implementation like this:
command = input_data.get('tool_input', {}).get('command', '')
if 'python' in command:
command = command.replace('python', 'python3')
Would cause these disasters:
- Search Command Corruption
grep "python" requirements.txt → grep "python3" requirements.txt ❌
rg "import python" → rg "import python3" ❌
Result: Searches for wrong strings, missing actual matches
- File Operation Failures
mv python-utils.py backup/ → mv python3-utils.py backup/ ❌
rm old-python-scripts/ → rm old-python3-scripts/ ❌
cat python_config.json → cat python3_config.json ❌
Result: Operations on non-existent files, data loss
- String Content Corruption
echo "Use python3 not python" → echo "Use python33 not python3" ❌
sed 's/python/python3/g' file → sed 's/python3/python33/g' file ❌
Result: Corrupted documentation and code
- Path Corruption
cd /usr/local/python-3.12/ → cd /usr/local/python3-3.12/ ❌
./scripts/python-installer.sh → ./scripts/python3-installer.sh ❌
Result: Navigation to wrong directories, script execution failures
Safe Implementation Requirements
Proper command modification requires:
- Command Parsing - Identify the actual executable vs arguments
- Context Awareness - Only modify when string is the command itself
- Boundary Detection - Use word boundaries to avoid partial matches
- Argument Preservation - Never modify command arguments
Example safer approach:
import shlex
parts = shlex.split(command)
if parts and parts[0] == 'python':
parts[0] = 'python3'
command = shlex.join(parts)
Recommendations
- Documentation Updates
- Add prominent warnings about string replacement dangers
- Provide safe command parsing examples
- List common pitfalls with examples
- Built-in Safeguards
- Consider providing utility functions for safe command modification
- Add hook validation that warns about naive string operations
- Example Templates
- Provide tested examples for common use cases (python→python3, etc.)
- Show proper command parsing techniques
Impact
Without proper guidance, users will create hooks that:
- Break their workflows in subtle ways
- Cause data loss through wrong file operations
- Create hard-to-debug issues
- Generate user frustration and distrust
Related Issues
- #2814 (Hooks cannot modify commands - the underlying issue)
Priority
High - This guidance should be available when #2814 is fixed to prevent widespread user issues.
Environment Info
- Platform: darwin
- Terminal: Apple_Terminal
- Version: 1.0.38
- Feedback ID: 38cf8cc4-2acc-4f1c-a7a2-5cd1b29d9fa2
Errors
[{"error":"Error: Command failed: security find-generic-password -a $USER -w -s \"Claude Code\"\nsecurity: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.\n\n at genericNodeError (node:internal/errors:983:15)\n at wrappedFn (node:internal/errors:537:14)\n at checkExecSyncError (node:child_process:892:11)\n at execSync (node:child_process:964:15)\n at WI (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:701:3921)\n at file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:623:9038\n at Q (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:526:13327)\n at SX (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:623:8184)\n at K_ (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:623:7265)\n at O9 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:623:11432)","timestamp":"2025-07-01T17:50:58.218Z"}]This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗