feat: Add intelligent auto-router and enhanced integrations
- Add intelligent-router.sh hook for automatic agent routing - Add AUTO-TRIGGER-SUMMARY.md documentation - Add FINAL-INTEGRATION-SUMMARY.md documentation - Complete Prometheus integration (6 commands + 4 tools) - Complete Dexto integration (12 commands + 5 tools) - Enhanced Ralph with access to all agents - Fix /clawd command (removed disable-model-invocation) - Update hooks.json to v5 with intelligent routing - 291 total skills now available - All 21 commands with automatic routing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
472
dexto/packages/tools-process/src/command-validator.ts
Normal file
472
dexto/packages/tools-process/src/command-validator.ts
Normal file
@@ -0,0 +1,472 @@
|
||||
/**
|
||||
* Command Validator
|
||||
*
|
||||
* Security-focused command validation for process execution
|
||||
*/
|
||||
|
||||
import { ProcessConfig, CommandValidation } from './types.js';
|
||||
import type { IDextoLogger } from '@dexto/core';
|
||||
|
||||
const MAX_COMMAND_LENGTH = 10000; // 10K characters
|
||||
|
||||
// Dangerous command patterns that should be blocked
|
||||
// Validated against common security vulnerabilities and dangerous command patterns
|
||||
const DANGEROUS_PATTERNS = [
|
||||
// File system destruction
|
||||
/rm\s+-rf\s+\//, // rm -rf /
|
||||
/rm\s+-rf\s+\/\s*$/, // rm -rf / (end of line)
|
||||
/rm\s+-rf\s+\/\s*2/, // rm -rf / 2>/dev/null (with error suppression)
|
||||
|
||||
// Fork bomb variations
|
||||
/:\(\)\{\s*:\|:&\s*\};:/, // Classic fork bomb
|
||||
/:\(\)\{\s*:\|:&\s*\};/, // Fork bomb without final colon
|
||||
/:\(\)\{\s*:\|:&\s*\}/, // Fork bomb without semicolon
|
||||
|
||||
// Disk operations
|
||||
/dd\s+if=.*of=\/dev\//, // dd to disk devices
|
||||
/dd\s+if=\/dev\/zero.*of=\/dev\//, // dd zero to disk
|
||||
/dd\s+if=\/dev\/urandom.*of=\/dev\//, // dd random to disk
|
||||
/>\s*\/dev\/sd[a-z]/, // Write to disk devices
|
||||
/>>\s*\/dev\/sd[a-z]/, // Append to disk devices
|
||||
|
||||
// Filesystem operations
|
||||
/mkfs\./, // Format filesystem
|
||||
/mkfs\s+/, // Format filesystem with space
|
||||
/fdisk\s+\/dev\/sd[a-z]/, // Partition disk
|
||||
/parted\s+\/dev\/sd[a-z]/, // Partition disk with parted
|
||||
|
||||
// Download and execute patterns
|
||||
/wget.*\|\s*sh/, // wget | sh
|
||||
/wget.*\|\s*bash/, // wget | bash
|
||||
/curl.*\|\s*sh/, // curl | sh
|
||||
/curl.*\|\s*bash/, // curl | bash
|
||||
/wget.*\|\s*python/, // wget | python
|
||||
/curl.*\|\s*python/, // curl | python
|
||||
|
||||
// Shell execution
|
||||
/\|\s*bash/, // Pipe to bash
|
||||
/\|\s*sh/, // Pipe to sh
|
||||
/\|\s*zsh/, // Pipe to zsh
|
||||
/\|\s*fish/, // Pipe to fish
|
||||
|
||||
// Command evaluation
|
||||
/eval\s+\$\(/, // eval $()
|
||||
/eval\s+`/, // eval backticks
|
||||
/eval\s+"/, // eval double quotes
|
||||
/eval\s+'/, // eval single quotes
|
||||
|
||||
// Permission changes
|
||||
/chmod\s+777\s+\//, // chmod 777 /
|
||||
/chmod\s+777\s+\/\s*$/, // chmod 777 / (end of line)
|
||||
/chmod\s+-R\s+777\s+\//, // chmod -R 777 /
|
||||
/chown\s+-R\s+root\s+\//, // chown -R root /
|
||||
|
||||
// Network operations
|
||||
/nc\s+-l\s+-p\s+\d+/, // netcat listener
|
||||
/ncat\s+-l\s+-p\s+\d+/, // ncat listener
|
||||
/socat\s+.*LISTEN/, // socat listener
|
||||
|
||||
// Process manipulation
|
||||
/killall\s+-9/, // killall -9
|
||||
/pkill\s+-9/, // pkill -9
|
||||
/kill\s+-9\s+-1/, // kill -9 -1 (kill all processes)
|
||||
|
||||
// System shutdown/reboot
|
||||
/shutdown\s+now/, // shutdown now
|
||||
/reboot/, // reboot
|
||||
/halt/, // halt
|
||||
/poweroff/, // poweroff
|
||||
|
||||
// Memory operations
|
||||
/echo\s+3\s*>\s*\/proc\/sys\/vm\/drop_caches/, // Clear page cache
|
||||
/sync\s*;\s*echo\s+3\s*>\s*\/proc\/sys\/vm\/drop_caches/, // Sync and clear cache
|
||||
|
||||
// Network interface manipulation
|
||||
/ifconfig\s+.*down/, // Bring interface down
|
||||
/ip\s+link\s+set\s+.*down/, // Bring interface down with ip
|
||||
|
||||
// Package manager operations
|
||||
/apt\s+remove\s+--purge\s+.*/, // Remove packages
|
||||
/yum\s+remove\s+.*/, // Remove packages
|
||||
/dnf\s+remove\s+.*/, // Remove packages
|
||||
/pacman\s+-R\s+.*/, // Remove packages
|
||||
];
|
||||
|
||||
// Command injection patterns
|
||||
// Note: We don't block compound commands with && here, as they're handled by
|
||||
// the compound command detection logic in determineApprovalRequirement()
|
||||
const INJECTION_PATTERNS = [
|
||||
// Command chaining with dangerous commands using semicolon (more suspicious)
|
||||
/;\s*rm\s+-rf/, // ; rm -rf
|
||||
/;\s*chmod\s+777/, // ; chmod 777
|
||||
/;\s*chown\s+root/, // ; chown root
|
||||
|
||||
// Command substitution with dangerous commands
|
||||
/`.*rm.*`/, // backticks with rm
|
||||
/\$\(.*rm.*\)/, // $() with rm
|
||||
/`.*chmod.*`/, // backticks with chmod
|
||||
/\$\(.*chmod.*\)/, // $() with chmod
|
||||
/`.*chown.*`/, // backticks with chown
|
||||
/\$\(.*chown.*\)/, // $() with chown
|
||||
|
||||
// Multiple command separators
|
||||
/;\s*;\s*/, // Multiple semicolons
|
||||
/&&\s*&&\s*/, // Multiple && operators
|
||||
/\|\|\s*\|\|\s*/, // Multiple || operators
|
||||
|
||||
// Redirection with dangerous commands
|
||||
/rm\s+.*>\s*\/dev\/null/, // rm with output redirection
|
||||
/chmod\s+.*>\s*\/dev\/null/, // chmod with output redirection
|
||||
/chown\s+.*>\s*\/dev\/null/, // chown with output redirection
|
||||
|
||||
// Environment variable manipulation
|
||||
/\$[A-Z_]+\s*=\s*.*rm/, // Environment variable with rm
|
||||
/\$[A-Z_]+\s*=\s*.*chmod/, // Environment variable with chmod
|
||||
/\$[A-Z_]+\s*=\s*.*chown/, // Environment variable with chown
|
||||
];
|
||||
|
||||
// Commands that require approval
|
||||
const REQUIRES_APPROVAL_PATTERNS = [
|
||||
// File operations
|
||||
/^rm\s+/, // rm (removal)
|
||||
/^mv\s+/, // move files
|
||||
/^cp\s+/, // copy files
|
||||
/^chmod\s+/, // chmod
|
||||
/^chown\s+/, // chown
|
||||
/^chgrp\s+/, // chgrp
|
||||
/^ln\s+/, // create links
|
||||
/^unlink\s+/, // unlink files
|
||||
|
||||
// Git operations
|
||||
/^git\s+push/, // git push
|
||||
/^git\s+commit/, // git commit
|
||||
/^git\s+reset/, // git reset
|
||||
/^git\s+rebase/, // git rebase
|
||||
/^git\s+merge/, // git merge
|
||||
/^git\s+checkout/, // git checkout
|
||||
/^git\s+branch/, // git branch
|
||||
/^git\s+tag/, // git tag
|
||||
|
||||
// Package management
|
||||
/^npm\s+publish/, // npm publish
|
||||
/^npm\s+uninstall/, // npm uninstall
|
||||
/^yarn\s+publish/, // yarn publish
|
||||
/^yarn\s+remove/, // yarn remove
|
||||
/^pip\s+install/, // pip install
|
||||
/^pip\s+uninstall/, // pip uninstall
|
||||
/^apt\s+install/, // apt install
|
||||
/^apt\s+remove/, // apt remove
|
||||
/^yum\s+install/, // yum install
|
||||
/^yum\s+remove/, // yum remove
|
||||
/^dnf\s+install/, // dnf install
|
||||
/^dnf\s+remove/, // dnf remove
|
||||
/^pacman\s+-S/, // pacman install
|
||||
/^pacman\s+-R/, // pacman remove
|
||||
|
||||
// Container operations
|
||||
/^docker\s+/, // docker commands
|
||||
/^podman\s+/, // podman commands
|
||||
/^kubectl\s+/, // kubectl commands
|
||||
|
||||
// System operations
|
||||
/^sudo\s+/, // sudo commands
|
||||
/^su\s+/, // su commands
|
||||
/^systemctl\s+/, // systemctl commands
|
||||
/^service\s+/, // service commands
|
||||
/^mount\s+/, // mount commands
|
||||
/^umount\s+/, // umount commands
|
||||
/^fdisk\s+/, // fdisk commands
|
||||
/^parted\s+/, // parted commands
|
||||
/^mkfs\s+/, // mkfs commands
|
||||
/^fsck\s+/, // fsck commands
|
||||
|
||||
// Network operations
|
||||
/^iptables\s+/, // iptables commands
|
||||
/^ufw\s+/, // ufw commands
|
||||
/^firewall-cmd\s+/, // firewall-cmd commands
|
||||
/^sshd\s+/, // sshd commands
|
||||
/^ssh\s+/, // ssh commands
|
||||
/^scp\s+/, // scp commands
|
||||
/^rsync\s+/, // rsync commands
|
||||
|
||||
// Process management
|
||||
/^kill\s+/, // kill commands
|
||||
/^killall\s+/, // killall commands
|
||||
/^pkill\s+/, // pkill commands
|
||||
/^nohup\s+/, // nohup commands
|
||||
/^screen\s+/, // screen commands
|
||||
/^tmux\s+/, // tmux commands
|
||||
|
||||
// Database operations
|
||||
/^mysql\s+/, // mysql commands
|
||||
/^psql\s+/, // psql commands
|
||||
/^sqlite3\s+/, // sqlite3 commands
|
||||
/^mongodb\s+/, // mongodb commands
|
||||
/^redis-cli\s+/, // redis-cli commands
|
||||
];
|
||||
|
||||
// Safe command patterns for strict mode
|
||||
const SAFE_PATTERNS = [
|
||||
// Directory navigation with commands
|
||||
/^cd\s+.*&&\s+\w+/, // cd && command
|
||||
/^cd\s+.*;\s+\w+/, // cd ; command
|
||||
|
||||
// Safe pipe operations
|
||||
/\|\s*grep/, // | grep
|
||||
/\|\s*head/, // | head
|
||||
/\|\s*tail/, // | tail
|
||||
/\|\s*sort/, // | sort
|
||||
/\|\s*uniq/, // | uniq
|
||||
/\|\s*wc/, // | wc
|
||||
/\|\s*cat/, // | cat
|
||||
/\|\s*less/, // | less
|
||||
/\|\s*more/, // | more
|
||||
/\|\s*awk/, // | awk
|
||||
/\|\s*sed/, // | sed
|
||||
/\|\s*cut/, // | cut
|
||||
/\|\s*tr/, // | tr
|
||||
/\|\s*xargs/, // | xargs
|
||||
|
||||
// Safe redirection
|
||||
/^ls\s+.*>/, // ls with output redirection
|
||||
/^find\s+.*>/, // find with output redirection
|
||||
/^grep\s+.*>/, // grep with output redirection
|
||||
/^cat\s+.*>/, // cat with output redirection
|
||||
];
|
||||
|
||||
// Write operation patterns for moderate mode
|
||||
const WRITE_PATTERNS = [
|
||||
// Output redirection
|
||||
/>/, // output redirection
|
||||
/>>/, // append redirection
|
||||
/2>/, // error redirection
|
||||
/2>>/, // error append redirection
|
||||
/&>/, // both output and error redirection
|
||||
/&>>/, // both output and error append redirection
|
||||
|
||||
// File operations
|
||||
/tee\s+/, // tee command
|
||||
/touch\s+/, // touch command
|
||||
/mkdir\s+/, // mkdir command
|
||||
/rmdir\s+/, // rmdir command
|
||||
|
||||
// Text editors
|
||||
/vim\s+/, // vim command
|
||||
/nano\s+/, // nano command
|
||||
/emacs\s+/, // emacs command
|
||||
/code\s+/, // code command (VS Code)
|
||||
|
||||
// File copying and moving
|
||||
/cp\s+/, // cp command
|
||||
/mv\s+/, // mv command
|
||||
/scp\s+/, // scp command
|
||||
/rsync\s+/, // rsync command
|
||||
];
|
||||
|
||||
/**
|
||||
* CommandValidator - Validates commands for security and policy compliance
|
||||
*
|
||||
* Security checks:
|
||||
* 1. Command length limits
|
||||
* 2. Dangerous command patterns
|
||||
* 3. Command injection detection
|
||||
* 4. Allowed/blocked command lists
|
||||
* 5. Shell metacharacter analysis
|
||||
* TODO: Add tests for this class
|
||||
*/
|
||||
export class CommandValidator {
|
||||
private config: ProcessConfig;
|
||||
private logger: IDextoLogger;
|
||||
|
||||
constructor(config: ProcessConfig, logger: IDextoLogger) {
|
||||
this.config = config;
|
||||
this.logger = logger;
|
||||
this.logger.debug(
|
||||
`CommandValidator initialized with security level: ${config.securityLevel}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a command for security and policy compliance
|
||||
*/
|
||||
validateCommand(command: string): CommandValidation {
|
||||
// 1. Check for empty command
|
||||
if (!command || command.trim() === '') {
|
||||
return {
|
||||
isValid: false,
|
||||
error: 'Command cannot be empty',
|
||||
};
|
||||
}
|
||||
|
||||
const trimmedCommand = command.trim();
|
||||
|
||||
// 2. Check for shell backgrounding (trailing &)
|
||||
// This bypasses timeout and creates orphaned processes that can't be controlled
|
||||
if (/&\s*$/.test(trimmedCommand)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: 'Commands ending with & (shell backgrounding) are not allowed. Use run_in_background parameter instead for proper process management.',
|
||||
};
|
||||
}
|
||||
|
||||
// 3. Check command length
|
||||
if (trimmedCommand.length > MAX_COMMAND_LENGTH) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: `Command too long: ${trimmedCommand.length} characters. Maximum: ${MAX_COMMAND_LENGTH}`,
|
||||
};
|
||||
}
|
||||
|
||||
// 4. Check against dangerous patterns (strict and moderate)
|
||||
if (this.config.securityLevel !== 'permissive') {
|
||||
for (const pattern of DANGEROUS_PATTERNS) {
|
||||
if (pattern.test(trimmedCommand)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: `Command matches dangerous pattern: ${pattern.source}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Check for command injection attempts (all security levels)
|
||||
const injectionResult = this.detectInjection(trimmedCommand);
|
||||
if (!injectionResult.isValid) {
|
||||
return injectionResult;
|
||||
}
|
||||
|
||||
// 6. Check against blocked commands list
|
||||
for (const blockedPattern of this.config.blockedCommands) {
|
||||
if (trimmedCommand.includes(blockedPattern)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: `Command is blocked: matches "${blockedPattern}"`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Check against allowed commands list (if not empty)
|
||||
if (this.config.allowedCommands.length > 0) {
|
||||
const isAllowed = this.config.allowedCommands.some((allowedCmd) =>
|
||||
trimmedCommand.startsWith(allowedCmd)
|
||||
);
|
||||
|
||||
if (!isAllowed) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: `Command not in allowed list. Allowed: ${this.config.allowedCommands.join(', ')}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Determine if approval is required based on security level
|
||||
const requiresApproval = this.determineApprovalRequirement(trimmedCommand);
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
normalizedCommand: trimmedCommand,
|
||||
requiresApproval,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect command injection attempts
|
||||
*/
|
||||
private detectInjection(command: string): CommandValidation {
|
||||
// Check for obvious injection patterns
|
||||
for (const pattern of INJECTION_PATTERNS) {
|
||||
if (pattern.test(command)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: `Potential command injection detected: ${pattern.source}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// In strict mode, be more aggressive
|
||||
if (this.config.securityLevel === 'strict') {
|
||||
// Check for multiple commands chained together (except safe ones)
|
||||
const hasMultipleCommands = /;|\|{1,2}|&&/.test(command);
|
||||
if (hasMultipleCommands) {
|
||||
// Allow safe patterns like "cd dir && ls" or "command | grep pattern"
|
||||
const isSafe = SAFE_PATTERNS.some((pattern) => pattern.test(command));
|
||||
if (!isSafe) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: 'Multiple commands detected in strict mode. Use moderate or permissive mode if this is intentional.',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a command requires approval
|
||||
* Handles compound commands (with &&, ||, ;) by checking each sub-command
|
||||
*/
|
||||
private determineApprovalRequirement(command: string): boolean {
|
||||
// Split compound commands by &&, ||, or ; to check each part independently
|
||||
// This ensures dangerous operations in the middle of compound commands are detected
|
||||
const subCommands = command.split(/\s*(?:&&|\|\||;)\s*/).map((cmd) => cmd.trim());
|
||||
|
||||
// Check if ANY sub-command requires approval
|
||||
for (const subCmd of subCommands) {
|
||||
if (!subCmd) continue; // Skip empty parts
|
||||
|
||||
// Strip leading shell keywords and braces to get the actual command
|
||||
// This prevents bypassing approval checks via control-flow wrapping
|
||||
const normalizedSubCmd = subCmd
|
||||
.replace(/^(?:then|do|else)\b\s*/, '')
|
||||
.replace(/^\{\s*/, '')
|
||||
.trim();
|
||||
if (!normalizedSubCmd) continue;
|
||||
|
||||
// Commands that modify system state always require approval
|
||||
for (const pattern of REQUIRES_APPROVAL_PATTERNS) {
|
||||
if (pattern.test(normalizedSubCmd)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// In strict mode, all commands require approval
|
||||
if (this.config.securityLevel === 'strict') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// In moderate mode, write operations require approval
|
||||
if (this.config.securityLevel === 'moderate') {
|
||||
if (WRITE_PATTERNS.some((pattern) => pattern.test(normalizedSubCmd))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Permissive mode - no additional approval required
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of blocked commands
|
||||
*/
|
||||
getBlockedCommands(): string[] {
|
||||
return [...this.config.blockedCommands];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of allowed commands
|
||||
*/
|
||||
getAllowedCommands(): string[] {
|
||||
return [...this.config.allowedCommands];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get security level
|
||||
*/
|
||||
getSecurityLevel(): string {
|
||||
return this.config.securityLevel;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user