Claude hooks auto-backup: manual (backup_20250720_091250)
This commit is contained in:
parent
9445e09c48
commit
392833187e
135 changed files with 16151 additions and 3439 deletions
222
hooks/command-validator.js
Executable file
222
hooks/command-validator.js
Executable file
|
|
@ -0,0 +1,222 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Command Validator Hook - PreToolUse[Bash] hook
|
||||
* Validates bash commands using shadow learner insights
|
||||
*/
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
// Add lib directory to require path
|
||||
const libPath = path.join(__dirname, '..', 'lib');
|
||||
const { ShadowLearner } = require(path.join(libPath, 'shadow-learner'));
|
||||
|
||||
class CommandValidator {
|
||||
constructor() {
|
||||
this.shadowLearner = new ShadowLearner();
|
||||
|
||||
// Dangerous command patterns
|
||||
this.dangerousPatterns = [
|
||||
/rm\s+-rf\s+\//, // Delete root
|
||||
/mkfs\./, // Format filesystem
|
||||
/dd\s+if=.*of=\/dev\//, // Overwrite devices
|
||||
/:\(\){ :\|:& };:/, // Fork bomb
|
||||
/curl.*\|\s*bash/, // Pipe to shell
|
||||
/wget.*\|\s*sh/, // Pipe to shell
|
||||
/;.*rm\s+-rf/, // Command chaining with rm
|
||||
/&&.*rm\s+-rf/, // Command chaining with rm
|
||||
];
|
||||
|
||||
this.suspiciousPatterns = [
|
||||
/sudo\s+rm/, // Sudo with rm
|
||||
/chmod\s+777/, // Overly permissive
|
||||
/\/etc\/passwd/, // System files
|
||||
/\/etc\/shadow/, // System files
|
||||
/nc.*-l.*-p/, // Netcat listener
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Comprehensive command safety validation
|
||||
*/
|
||||
validateCommandSafety(command) {
|
||||
// Normalize command for analysis
|
||||
const normalized = command.toLowerCase().trim();
|
||||
|
||||
// Check for dangerous patterns
|
||||
for (const pattern of this.dangerousPatterns) {
|
||||
if (pattern.test(normalized)) {
|
||||
return {
|
||||
allowed: false,
|
||||
reason: 'Dangerous command pattern detected',
|
||||
severity: 'critical'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Check for suspicious patterns
|
||||
for (const pattern of this.suspiciousPatterns) {
|
||||
if (pattern.test(normalized)) {
|
||||
return {
|
||||
allowed: true, // Allow but warn
|
||||
reason: 'Suspicious command pattern detected',
|
||||
severity: 'warning'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { allowed: true, reason: 'Command appears safe' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Use shadow learner to predict command success
|
||||
*/
|
||||
validateWithShadowLearner(command) {
|
||||
try {
|
||||
const prediction = this.shadowLearner.predictCommandOutcome(command);
|
||||
|
||||
if (!prediction.likelySuccess && prediction.confidence > 0.8) {
|
||||
const suggestions = prediction.suggestions || [];
|
||||
const suggestionText = suggestions.length > 0 ? ` Try: ${suggestions[0]}` : '';
|
||||
|
||||
return {
|
||||
allowed: false,
|
||||
reason: `Command likely to fail (confidence: ${Math.round(prediction.confidence * 100)}%)${suggestionText}`,
|
||||
severity: 'medium',
|
||||
suggestions
|
||||
};
|
||||
} else if (prediction.warnings && prediction.warnings.length > 0) {
|
||||
return {
|
||||
allowed: true,
|
||||
reason: prediction.warnings[0],
|
||||
severity: 'warning',
|
||||
suggestions: prediction.suggestions || []
|
||||
};
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// If shadow learner fails, don't block
|
||||
}
|
||||
|
||||
return { allowed: true, reason: 'No issues detected' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Main validation entry point
|
||||
*/
|
||||
validateCommand(command) {
|
||||
// Safety validation (blocking)
|
||||
const safetyResult = this.validateCommandSafety(command);
|
||||
if (!safetyResult.allowed) {
|
||||
return safetyResult;
|
||||
}
|
||||
|
||||
// Shadow learner validation (predictive)
|
||||
const predictionResult = this.validateWithShadowLearner(command);
|
||||
|
||||
// Return most significant result
|
||||
if (['high', 'critical'].includes(predictionResult.severity)) {
|
||||
return predictionResult;
|
||||
} else if (['warning', 'medium'].includes(safetyResult.severity)) {
|
||||
return safetyResult;
|
||||
} else {
|
||||
return predictionResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
let inputData = '';
|
||||
|
||||
// Handle stdin input
|
||||
if (process.stdin.isTTY) {
|
||||
// If called directly for testing
|
||||
inputData = JSON.stringify({
|
||||
tool: 'Bash',
|
||||
parameters: { command: 'pip install requests' }
|
||||
});
|
||||
} else {
|
||||
// Read from stdin
|
||||
process.stdin.setEncoding('utf8');
|
||||
|
||||
for await (const chunk of process.stdin) {
|
||||
inputData += chunk;
|
||||
}
|
||||
}
|
||||
|
||||
const input = JSON.parse(inputData);
|
||||
|
||||
// Extract command from parameters
|
||||
const tool = input.tool || '';
|
||||
const parameters = input.parameters || {};
|
||||
const command = parameters.command || '';
|
||||
|
||||
if (tool !== 'Bash' || !command) {
|
||||
// Not a bash command, allow it
|
||||
const response = { allow: true, message: 'Not a bash command' };
|
||||
console.log(JSON.stringify(response));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Validate command
|
||||
const validator = new CommandValidator();
|
||||
const result = validator.validateCommand(command);
|
||||
|
||||
if (!result.allowed) {
|
||||
// Block the command
|
||||
const response = {
|
||||
allow: false,
|
||||
message: `⛔ Command blocked: ${result.reason}`
|
||||
};
|
||||
console.log(JSON.stringify(response));
|
||||
process.exit(1); // Exit code 1 = block operation
|
||||
}
|
||||
|
||||
else if (['warning', 'medium'].includes(result.severity)) {
|
||||
// Allow with warning
|
||||
const warningEmoji = result.severity === 'warning' ? '⚠️' : '🚨';
|
||||
let message = `${warningEmoji} ${result.reason}`;
|
||||
|
||||
if (result.suggestions && result.suggestions.length > 0) {
|
||||
message += `\n💡 Suggestion: ${result.suggestions[0]}`;
|
||||
}
|
||||
|
||||
const response = {
|
||||
allow: true,
|
||||
message
|
||||
};
|
||||
console.log(JSON.stringify(response));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
else {
|
||||
// Allow without warning
|
||||
const response = { allow: true, message: 'Command validated' };
|
||||
console.log(JSON.stringify(response));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// Never block on validation errors - always allow operation
|
||||
const response = {
|
||||
allow: true,
|
||||
message: `Validation error: ${error.message}`
|
||||
};
|
||||
console.log(JSON.stringify(response));
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle unhandled promise rejections
|
||||
process.on('unhandledRejection', (error) => {
|
||||
const response = {
|
||||
allow: true,
|
||||
message: `Validation error: ${error.message}`
|
||||
};
|
||||
console.log(JSON.stringify(response));
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
main();
|
||||
Loading…
Add table
Add a link
Reference in a new issue