Claude hooks auto-backup: manual (backup_20250720_091250)

This commit is contained in:
Ryan Malloy 2025-07-20 03:12:51 -06:00
parent 9445e09c48
commit 392833187e
135 changed files with 16151 additions and 3439 deletions

View file

@ -263,52 +263,62 @@ Hooks are designed to fail safely:
Always validate hook input:
```python
def validate_input(input_data):
if not isinstance(input_data, dict):
raise ValueError("Input must be JSON object")
```javascript
function validateInput(inputData) {
if (typeof inputData !== 'object' || inputData === null || Array.isArray(inputData)) {
throw new Error('Input must be JSON object');
}
tool = input_data.get("tool", "")
if not isinstance(tool, str):
raise ValueError("Tool must be string")
const tool = inputData.tool || '';
if (typeof tool !== 'string') {
throw new Error('Tool must be string');
}
# Validate other fields...
// Validate other fields...
}
```
### Output Sanitization
Ensure hook output is safe:
```python
def safe_message(text):
# Remove potential injection characters
return text.replace('\x00', '').replace('\r', '').replace('\n', '\\n')
response = {
"allow": True,
"message": safe_message(user_input)
```javascript
function safeMessage(text) {
// Remove potential injection characters
return text.replace(/\x00/g, '').replace(/\r/g, '').replace(/\n/g, '\\n');
}
const response = {
allow: true,
message: safeMessage(userInput)
};
```
### File Path Validation
For hooks that access files:
```python
def validate_file_path(path):
# Convert to absolute path
abs_path = os.path.abspath(path)
```javascript
const path = require('path');
function validateFilePath(filePath) {
// Convert to absolute path
const absPath = path.resolve(filePath);
# Check if within project boundaries
project_root = os.path.abspath(".")
if not abs_path.startswith(project_root):
raise ValueError("Path outside project directory")
// Check if within project boundaries
const projectRoot = path.resolve('.');
if (!absPath.startsWith(projectRoot)) {
throw new Error('Path outside project directory');
}
# Check for system files
system_paths = ['/etc', '/usr', '/var', '/sys', '/proc']
for sys_path in system_paths:
if abs_path.startswith(sys_path):
raise ValueError("System file access denied")
// Check for system files
const systemPaths = ['/etc', '/usr', '/var', '/sys', '/proc'];
for (const sysPath of systemPaths) {
if (absPath.startsWith(sysPath)) {
throw new Error('System file access denied');
}
}
}
```
---
@ -319,27 +329,32 @@ def validate_file_path(path):
Test hooks with sample inputs:
```python
def test_command_validator():
import subprocess
import json
```javascript
const { spawn } = require('child_process');
function testCommandValidator() {
// Test dangerous command
const inputData = {
tool: 'Bash',
parameters: { command: 'rm -rf /' }
};
# Test dangerous command
input_data = {
"tool": "Bash",
"parameters": {"command": "rm -rf /"}
}
const process = spawn('node', ['hooks/command-validator.js'], {
stdio: 'pipe'
});
process = subprocess.run(
["python3", "hooks/command_validator.py"],
input=json.dumps(input_data),
capture_output=True,
text=True
)
process.stdin.write(JSON.stringify(inputData));
process.stdin.end();
assert process.returncode == 1 # Should block
response = json.loads(process.stdout)
assert response["allow"] == False
process.on('exit', (code) => {
console.assert(code === 1, 'Should block'); // Should block
});
process.stdout.on('data', (data) => {
const response = JSON.parse(data.toString());
console.assert(response.allow === false);
});
}
```
### Integration Testing
@ -348,7 +363,7 @@ Test with Claude Code directly:
```bash
# Test in development environment
echo '{"tool": "Bash", "parameters": {"command": "ls"}}' | python3 hooks/command_validator.py
echo '{"tool": "Bash", "parameters": {"command": "ls"}}' | node hooks/command-validator.js
# Test hook registration
claude-hooks status
@ -358,25 +373,33 @@ claude-hooks status
Measure hook execution time:
```python
import time
import subprocess
import json
```javascript
const { spawn } = require('child_process');
def benchmark_hook(hook_script, input_data, iterations=100):
times = []
function benchmarkHook(hookScript, inputData, iterations = 100) {
const times = [];
let completed = 0;
for _ in range(iterations):
start = time.time()
subprocess.run(
["python3", hook_script],
input=json.dumps(input_data),
capture_output=True
)
times.append(time.time() - start)
avg_time = sum(times) / len(times)
max_time = max(times)
print(f"Average: {avg_time*1000:.1f}ms, Max: {max_time*1000:.1f}ms")
for (let i = 0; i < iterations; i++) {
const start = Date.now();
const process = spawn('node', [hookScript], {
stdio: 'pipe'
});
process.stdin.write(JSON.stringify(inputData));
process.stdin.end();
process.on('exit', () => {
times.push(Date.now() - start);
completed++;
if (completed === iterations) {
const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
const maxTime = Math.max(...times);
console.log(`Average: ${avgTime.toFixed(1)}ms, Max: ${maxTime.toFixed(1)}ms`);
}
});
}
}
```