UNPKG

mcp-sanitizer

Version:

Comprehensive security sanitization library for Model Context Protocol (MCP) servers with trusted security libraries

309 lines (266 loc) 7.93 kB
/** * Command Injection Pattern Detection Module * * Detects patterns commonly used in command injection attacks, including * shell metacharacters, dangerous commands, and command chaining attempts. * * Based on security best practices from DOMPurify, OWASP guidelines, * and common command injection vectors. */ const SEVERITY_LEVELS = { CRITICAL: 'critical', HIGH: 'high', MEDIUM: 'medium', LOW: 'low' } /** * Shell metacharacters that can be used for command injection */ const SHELL_METACHARACTERS = [ /[;&|`$(){}[\]]/, // Basic shell metacharacters /\|\s*\w+|&&|\|\||;|`/, // Command chaining patterns /\$\(.*?\)|\$\{.*?\}/, // Command substitution />\s*\/dev\/|<\s*\/dev\//, // Device redirection /<<\s*EOF|<<\s*\w+/, // Here documents /\*|\?|~|\^/ // Wildcards and expansion ] /** * Dangerous system commands that should be blocked */ const DANGEROUS_COMMANDS = [ // File system manipulation /\b(rm|del|format|mkfs|dd|shred)\s+/i, /\b(chmod|chown|chgrp)\s+/i, /\b(mount|umount|fdisk)\s+/i, // Network tools /\b(nc|netcat|telnet|ssh|scp|ftp)\s+/i, /\b(wget|curl|lynx)\s+/i, /\b(nmap|ping|traceroute)\s+/i, // System information /\b(ps|kill|killall|pkill)\s+/i, /\b(sudo|su|passwd)\s+/i, /\b(crontab|at|batch)\s+/i, // Code execution /\b(eval|exec|system|shell_exec)\s*\(/i, /\b(python|perl|ruby|node|bash|sh|zsh)\s+/i, // Data exfiltration /\b(mail|sendmail|base64|xxd|hexdump)\s+/i ] /** * Command injection patterns specific to different shells */ const SHELL_SPECIFIC_PATTERNS = { bash: [ /\$\(IFS=.*?\)/, // IFS manipulation /\$\{IFS\}/, // IFS variable usage /\|\s*tee\s+/, // Output redirection /\|\s*xargs\s+/ // Command execution via xargs ], powershell: [ /Invoke-Expression|iex\s+/i, // PowerShell code execution /Start-Process|saps\s+/i, // Process execution /Get-Content|gc\s+/i, // File reading /Set-Content|sc\s+/i // File writing ], cmd: [ /&\s*echo\s+/i, // Windows command chaining /\|\s*findstr\s+/i, // Windows text processing />\s*con\s*/i, // Console redirection /for\s+\/[lrf]\s+/i // Windows for loops ] } /** * Patterns for encoded command injection attempts */ const ENCODED_PATTERNS = [ /\\x[0-9a-f]{2}/i, // Hex encoding /\\[0-7]{3}/, // Octal encoding /\\u[0-9a-f]{4}/i, // Unicode encoding /%[0-9a-f]{2}/i, // URL encoding /\+/ // URL encoding spaces ] /** * Time-based command injection patterns */ const TIME_BASED_PATTERNS = [ /sleep\s+\d+/i, /ping\s+-[nc]\s+\d+/i, /timeout\s+\d+/i, /usleep\s+\d+/i ] /** * Main detection function for command injection patterns * @param {string} input - The input string to analyze * @param {Object} options - Detection options * @returns {Object} Detection result with severity and details */ function detectCommandInjection (input, options = {}) { if (typeof input !== 'string') { return { detected: false, severity: null, patterns: [] } } const detectedPatterns = [] let maxSeverity = null // Check shell metacharacters const shellMetaResult = checkShellMetacharacters(input) if (shellMetaResult.detected) { detectedPatterns.push(...shellMetaResult.patterns) maxSeverity = getHigherSeverity(maxSeverity, shellMetaResult.severity) } // Check dangerous commands const dangerousCommandResult = checkDangerousCommands(input) if (dangerousCommandResult.detected) { detectedPatterns.push(...dangerousCommandResult.patterns) maxSeverity = getHigherSeverity(maxSeverity, dangerousCommandResult.severity) } // Check shell-specific patterns const shellSpecificResult = checkShellSpecificPatterns(input) if (shellSpecificResult.detected) { detectedPatterns.push(...shellSpecificResult.patterns) maxSeverity = getHigherSeverity(maxSeverity, shellSpecificResult.severity) } // Check encoded patterns const encodedResult = checkEncodedPatterns(input) if (encodedResult.detected) { detectedPatterns.push(...encodedResult.patterns) maxSeverity = getHigherSeverity(maxSeverity, encodedResult.severity) } // Check time-based patterns const timeBasedResult = checkTimeBasedPatterns(input) if (timeBasedResult.detected) { detectedPatterns.push(...timeBasedResult.patterns) maxSeverity = getHigherSeverity(maxSeverity, timeBasedResult.severity) } return { detected: detectedPatterns.length > 0, severity: maxSeverity, patterns: detectedPatterns, message: detectedPatterns.length > 0 ? `Command injection patterns detected: ${detectedPatterns.join(', ')}` : null } } /** * Check for shell metacharacters */ function checkShellMetacharacters (input) { const detected = [] for (const pattern of SHELL_METACHARACTERS) { if (pattern.test(input)) { detected.push(`shell_metacharacter:${pattern.source}`) } } return { detected: detected.length > 0, severity: detected.length > 0 ? SEVERITY_LEVELS.HIGH : null, patterns: detected } } /** * Check for dangerous system commands */ function checkDangerousCommands (input) { const detected = [] for (const pattern of DANGEROUS_COMMANDS) { if (pattern.test(input)) { detected.push(`dangerous_command:${pattern.source}`) } } return { detected: detected.length > 0, severity: detected.length > 0 ? SEVERITY_LEVELS.CRITICAL : null, patterns: detected } } /** * Check for shell-specific injection patterns */ function checkShellSpecificPatterns (input) { const detected = [] for (const [shell, patterns] of Object.entries(SHELL_SPECIFIC_PATTERNS)) { for (const pattern of patterns) { if (pattern.test(input)) { detected.push(`${shell}_specific:${pattern.source}`) } } } return { detected: detected.length > 0, severity: detected.length > 0 ? SEVERITY_LEVELS.HIGH : null, patterns: detected } } /** * Check for encoded command injection attempts */ function checkEncodedPatterns (input) { const detected = [] for (const pattern of ENCODED_PATTERNS) { if (pattern.test(input)) { detected.push(`encoded_pattern:${pattern.source}`) } } return { detected: detected.length > 0, severity: detected.length > 0 ? SEVERITY_LEVELS.MEDIUM : null, patterns: detected } } /** * Check for time-based command injection patterns */ function checkTimeBasedPatterns (input) { const detected = [] for (const pattern of TIME_BASED_PATTERNS) { if (pattern.test(input)) { detected.push(`time_based:${pattern.source}`) } } return { detected: detected.length > 0, severity: detected.length > 0 ? SEVERITY_LEVELS.MEDIUM : null, patterns: detected } } /** * Get the higher severity between two severity levels */ function getHigherSeverity (current, newSeverity) { if (!current) return newSeverity if (!newSeverity) return current const severityOrder = [ SEVERITY_LEVELS.LOW, SEVERITY_LEVELS.MEDIUM, SEVERITY_LEVELS.HIGH, SEVERITY_LEVELS.CRITICAL ] const currentIndex = severityOrder.indexOf(current) const newIndex = severityOrder.indexOf(newSeverity) return newIndex > currentIndex ? newSeverity : current } /** * Simple boolean check for command injection * @param {string} input - The input string to check * @returns {boolean} True if command injection patterns are detected */ function isCommandInjection (input) { return detectCommandInjection(input).detected } module.exports = { // Main detection functions detectCommandInjection, isCommandInjection, // Individual checkers checkShellMetacharacters, checkDangerousCommands, checkShellSpecificPatterns, checkEncodedPatterns, checkTimeBasedPatterns, // Pattern exports for reuse SHELL_METACHARACTERS, DANGEROUS_COMMANDS, SHELL_SPECIFIC_PATTERNS, ENCODED_PATTERNS, TIME_BASED_PATTERNS, // Constants SEVERITY_LEVELS }