UNPKG

mcp-sanitizer

Version:

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

310 lines (267 loc) 9.57 kB
/** * MCP Sanitizer Pattern Detection Modules * * Exports all pattern detection modules for the MCP Sanitizer. * This provides a centralized access point for all security pattern * detection functionality. * * Each module includes: * - Detection functions that return detailed results * - Simple boolean check functions * - Individual pattern checkers for granular control * - Exportable regex patterns for reuse * - Severity level constants * * Based on security best practices from HTML/XML sanitizers like DOMPurify, * OWASP guidelines, and comprehensive security research. */ const commandInjection = require('./command-injection') const sqlInjection = require('./sql-injection') const prototypePollution = require('./prototype-pollution') const templateInjection = require('./template-injection') /** * Combined severity levels from all modules */ const SEVERITY_LEVELS = { CRITICAL: 'critical', HIGH: 'high', MEDIUM: 'medium', LOW: 'low' } /** * Pattern type constants for easier identification */ const PATTERN_TYPES = { COMMAND_INJECTION: 'command_injection', SQL_INJECTION: 'sql_injection', PROTOTYPE_POLLUTION: 'prototype_pollution', TEMPLATE_INJECTION: 'template_injection' } /** * Comprehensive security pattern detection * Runs all pattern detection modules and combines results * * @param {string|Object} input - The input to analyze * @param {Object} options - Detection options * @returns {Object} Combined detection result */ function detectAllPatterns (input, options = {}) { const results = { detected: false, severity: null, patterns: [], detectionResults: {}, summary: { totalPatterns: 0, patternsByType: {}, patternsBySeverity: {} } } // Run all detection modules const detectionModules = [ { name: PATTERN_TYPES.COMMAND_INJECTION, module: commandInjection }, { name: PATTERN_TYPES.SQL_INJECTION, module: sqlInjection }, { name: PATTERN_TYPES.PROTOTYPE_POLLUTION, module: prototypePollution }, { name: PATTERN_TYPES.TEMPLATE_INJECTION, module: templateInjection } ] for (const { name, module } of detectionModules) { let detectResult // Call the appropriate detection function based on module if (name === PATTERN_TYPES.COMMAND_INJECTION) { detectResult = module.detectCommandInjection(input, options) } else if (name === PATTERN_TYPES.SQL_INJECTION) { detectResult = module.detectSQLInjection(input, options) } else if (name === PATTERN_TYPES.PROTOTYPE_POLLUTION) { detectResult = module.detectPrototypePollution(input, options) } else if (name === PATTERN_TYPES.TEMPLATE_INJECTION) { detectResult = module.detectTemplateInjection(input, options) } // Store individual results results.detectionResults[name] = detectResult // Combine patterns if (detectResult.detected) { results.detected = true results.patterns.push(...detectResult.patterns.map(p => `${name}:${p}`)) // Update severity to highest found results.severity = getHigherSeverity(results.severity, detectResult.severity) // Update summary results.summary.patternsByType[name] = detectResult.patterns.length if (detectResult.severity) { results.summary.patternsBySeverity[detectResult.severity] = (results.summary.patternsBySeverity[detectResult.severity] || 0) + detectResult.patterns.length } } else { results.summary.patternsByType[name] = 0 } } results.summary.totalPatterns = results.patterns.length // Generate combined message if (results.detected) { const typeCount = Object.values(results.summary.patternsByType) .filter(count => count > 0).length results.message = `${results.summary.totalPatterns} security patterns detected across ${typeCount} categories (severity: ${results.severity})` } return results } /** * Quick boolean check for any security patterns * @param {string|Object} input - The input to check * @returns {boolean} True if any security patterns are detected */ function hasSecurityPatterns (input) { return detectAllPatterns(input).detected } /** * Get detailed security analysis with recommendations * @param {string|Object} input - The input to analyze * @param {Object} options - Analysis options * @returns {Object} Detailed security analysis */ function analyzeSecurityPatterns (input, options = {}) { const detection = detectAllPatterns(input, options) const analysis = { ...detection, recommendations: [], riskLevel: getRiskLevel(detection.severity), shouldBlock: detection.severity === SEVERITY_LEVELS.CRITICAL } // Generate recommendations based on detected patterns if (detection.detectionResults[PATTERN_TYPES.COMMAND_INJECTION]?.detected) { analysis.recommendations.push('Input contains command injection patterns. Sanitize shell metacharacters and validate against allowed commands.') } if (detection.detectionResults[PATTERN_TYPES.SQL_INJECTION]?.detected) { analysis.recommendations.push('Input contains SQL injection patterns. Use parameterized queries and validate SQL keywords.') } if (detection.detectionResults[PATTERN_TYPES.PROTOTYPE_POLLUTION]?.detected) { analysis.recommendations.push('Input contains prototype pollution patterns. Validate object keys and use Object.create(null) for safe objects.') } if (detection.detectionResults[PATTERN_TYPES.TEMPLATE_INJECTION]?.detected) { analysis.recommendations.push('Input contains template injection patterns. Sanitize template syntax and use safe template engines.') } return analysis } /** * Get risk level based on severity */ function getRiskLevel (severity) { switch (severity) { case SEVERITY_LEVELS.CRITICAL: return 'CRITICAL' case SEVERITY_LEVELS.HIGH: return 'HIGH' case SEVERITY_LEVELS.MEDIUM: return 'MEDIUM' case SEVERITY_LEVELS.LOW: return 'LOW' default: return 'NONE' } } /** * 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 } /** * Create a pattern detector configured for specific use cases * @param {Object} config - Configuration options * @returns {Object} Configured detector functions */ function createPatternDetector (config = {}) { const { enableCommandInjection = true, enableSQLInjection = true, enablePrototypePollution = true, enableTemplateInjection = true, strictMode = false, customPatterns = [] } = config return { detect: (input, options = {}) => { const mergedOptions = { ...options, strictMode, customPatterns } if (!enableCommandInjection && !enableSQLInjection && !enablePrototypePollution && !enableTemplateInjection) { return { detected: false, severity: null, patterns: [] } } // Run only enabled detectors const results = { detected: false, severity: null, patterns: [], detectionResults: {} } if (enableCommandInjection) { const result = commandInjection.detectCommandInjection(input, mergedOptions) if (result.detected) { results.detected = true results.patterns.push(...result.patterns) results.severity = getHigherSeverity(results.severity, result.severity) } results.detectionResults.commandInjection = result } if (enableSQLInjection) { const result = sqlInjection.detectSQLInjection(input, mergedOptions) if (result.detected) { results.detected = true results.patterns.push(...result.patterns) results.severity = getHigherSeverity(results.severity, result.severity) } results.detectionResults.sqlInjection = result } if (enablePrototypePollution) { const result = prototypePollution.detectPrototypePollution(input, mergedOptions) if (result.detected) { results.detected = true results.patterns.push(...result.patterns) results.severity = getHigherSeverity(results.severity, result.severity) } results.detectionResults.prototypePollution = result } if (enableTemplateInjection) { const result = templateInjection.detectTemplateInjection(input, mergedOptions) if (result.detected) { results.detected = true results.patterns.push(...result.patterns) results.severity = getHigherSeverity(results.severity, result.severity) } results.detectionResults.templateInjection = result } return results }, isSecure: (input, options = {}) => { return !this.detect(input, options).detected } } } // Export individual modules module.exports = { // Individual detection modules commandInjection, sqlInjection, prototypePollution, templateInjection, // Combined detection functions detectAllPatterns, hasSecurityPatterns, analyzeSecurityPatterns, createPatternDetector, // Utility functions getHigherSeverity, getRiskLevel, // Constants SEVERITY_LEVELS, PATTERN_TYPES }