UNPKG

mcp-sanitizer

Version:

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

393 lines (335 loc) 10.5 kB
/** * Default Configuration for MCP Sanitizer * * This module provides the default configuration settings for the MCP Sanitizer. * These settings represent secure defaults that can be overridden by users while * maintaining a secure baseline. * * Based on security best practices from OWASP guidelines, DOMPurify approach, * and comprehensive security research. */ /** * Default configuration options for MCP Sanitizer * @type {Object} */ const DEFAULT_CONFIG = { // Network Security Settings allowedProtocols: [ 'http', // Standard HTTP protocol 'https', // Secure HTTP protocol 'mcp' // MCP protocol for Model Context Protocol ], // Content Length and Depth Limits maxStringLength: 10000, // Maximum length for string values (10KB) maxDepth: 10, // Maximum object nesting depth maxArrayLength: 1000, // Maximum array length maxObjectKeys: 100, // Maximum number of object keys // File System Security allowedFileExtensions: [ '.txt', // Plain text files '.json', // JSON data files '.md', // Markdown documentation '.csv', // Comma-separated values '.yaml', // YAML configuration files '.yml', // YAML configuration files (alternate extension) '.log' // Log files ], // Blocked Content Patterns blockedPatterns: [ // Template injection patterns (Server-Side Template Injection) /\$\{.*?\}|\{\{.*?\}\}|<%.*?%>/, // Prototype pollution patterns /__proto__|constructor\.prototype|prototype\.constructor/i, // Code execution patterns /require\s*\(|import\s*\(|eval\s*\(|Function\s*\(/i, // Script injection patterns (XSS) /<!--[\s\S]*?-->|<script[\s\S]*?<\/script>|<[^>]*on\w+\s*=|javascript:/i, // Command chaining patterns /\|\s*\w+|&&|\|\||;|`/, // Path traversal patterns /\.\.\//, // SQL comment patterns /--[\s\S]*$|\/\*[\s\S]*?\*\// ], // SQL Injection Protection sqlKeywords: [ // Data Definition Language (DDL) 'DROP', 'CREATE', 'ALTER', 'TRUNCATE', // Data Manipulation Language (DML) 'DELETE', 'INSERT', 'UPDATE', // Query operations 'UNION', 'SELECT', 'FROM', 'WHERE', // Administrative functions 'EXEC', 'EXECUTE', 'xp_', 'sp_', // Common injection patterns 'OR 1=1', 'OR 1 = 1', 'AND 1=1', 'AND 1 = 1', // Comment patterns '--', '/*', '*/', // Information schema access 'INFORMATION_SCHEMA', 'SYSOBJECTS', 'SYSCOLUMNS' ], // Command Injection Protection blockedCommands: [ // File system operations 'rm', 'del', 'delete', 'format', 'mkfs', // Network operations 'nc', 'netcat', 'curl', 'wget', 'ping', // System information 'ps', 'top', 'whoami', 'id', 'uname', // Process control 'kill', 'killall', 'pkill', // Archive operations 'tar', 'zip', 'unzip', 'gzip' ], // Shell Metacharacters (for command injection detection) shellMetacharacters: [ ';', '&', '|', '`', '$', '(', ')', '{', '}', '[', ']', '<', '>', '"', "'", '\\', '\n', '\r' ], // Security Policy Settings strictMode: false, // Enable strict validation mode logSecurityEvents: true, // Log security violations blockOnSeverity: 'critical', // Block requests at this severity level or higher // Pattern Detection Configuration patternDetection: { enableCommandInjection: true, enableSQLInjection: true, enablePrototypePollution: true, enableTemplateInjection: true, enableXSSDetection: true, enablePathTraversal: true }, // Context-Specific Settings contextSettings: { // File path validation filePath: { allowAbsolutePaths: false, allowedDirectories: [], blockedDirectories: [ '/etc', '/proc', '/sys', '/dev', '/root', 'C:\\Windows', 'C:\\System32', 'C:\\Program Files' ] }, // URL validation url: { allowPrivateIPs: false, allowLocalhostWithoutPort: false, maxURLLength: 2048, blockedDomains: [], allowedDomains: [] }, // Command validation command: { allowedCommands: [], blockedCommands: [], maxCommandLength: 1000 } }, // Output Configuration outputOptions: { includeWarnings: true, includeMetadata: false, sanitizeOutput: true, htmlEncode: true }, // Performance Settings performance: { timeoutMs: 5000, // Maximum processing time per request maxConcurrentRequests: 100, // Maximum concurrent sanitization requests enableCaching: false // Enable result caching (disabled by default for security) }, // Custom Pattern Support customPatterns: { enabled: false, patterns: [] } } /** * Configuration schema for validation * Each key maps to a validation function */ const CONFIG_SCHEMA = { allowedProtocols: (value) => { if (!Array.isArray(value)) { throw new Error('allowedProtocols must be an array') } if (!value.every(protocol => typeof protocol === 'string')) { throw new Error('All protocols must be strings') } }, maxStringLength: (value) => { if (typeof value !== 'number' || value < 0) { throw new Error('maxStringLength must be a non-negative number') } }, maxDepth: (value) => { if (typeof value !== 'number' || value < 0) { throw new Error('maxDepth must be a non-negative number') } }, maxArrayLength: (value) => { if (typeof value !== 'number' || value < 0) { throw new Error('maxArrayLength must be a non-negative number') } }, maxObjectKeys: (value) => { if (typeof value !== 'number' || value < 0) { throw new Error('maxObjectKeys must be a non-negative number') } }, allowedFileExtensions: (value) => { if (!Array.isArray(value)) { throw new Error('allowedFileExtensions must be an array') } if (!value.every(ext => typeof ext === 'string' && ext.startsWith('.'))) { throw new Error('All file extensions must be strings starting with a dot') } }, blockedPatterns: (value) => { if (!Array.isArray(value)) { throw new Error('blockedPatterns must be an array') } if (!value.every(pattern => pattern instanceof RegExp)) { throw new Error('All blocked patterns must be RegExp objects') } }, sqlKeywords: (value) => { if (!Array.isArray(value)) { throw new Error('sqlKeywords must be an array') } if (!value.every(keyword => typeof keyword === 'string')) { throw new Error('All SQL keywords must be strings') } }, strictMode: (value) => { if (typeof value !== 'boolean') { throw new Error('strictMode must be a boolean') } }, logSecurityEvents: (value) => { if (typeof value !== 'boolean') { throw new Error('logSecurityEvents must be a boolean') } }, blockOnSeverity: (value) => { const validSeverities = ['low', 'medium', 'high', 'critical'] if (!validSeverities.includes(value)) { throw new Error(`blockOnSeverity must be one of: ${validSeverities.join(', ')}`) } } } /** * Deep merge function that handles RegExp objects * @param {Object} target - Target object (will be extended) * @param {Object} source - Source object (will override target) * @returns {Object} Merged configuration */ function deepMerge (target, source) { if (typeof target !== 'object' || target === null) { target = {} } if (typeof source !== 'object' || source === null) { return target } const result = { ...target } for (const key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key]) && !(source[key] instanceof RegExp)) { // Deep merge objects result[key] = deepMerge(target[key] || {}, source[key]) } else if (source[key] instanceof RegExp) { // Clone RegExp objects result[key] = new RegExp(source[key].source, source[key].flags) } else if (Array.isArray(source[key])) { // For arrays, completely replace with source array (cloning RegExp objects) result[key] = source[key].map(item => item instanceof RegExp ? new RegExp(item.source, item.flags) : item ) } else { // For primitive values, use source value result[key] = source[key] } } } return result } /** * Merge user configuration with default configuration * @param {Object} target - Target configuration (defaults to DEFAULT_CONFIG) * @param {Object} source - Source configuration to merge in * @returns {Object} Merged configuration */ function mergeConfig (target = DEFAULT_CONFIG, source = {}) { // If called with one parameter, assume it's merging with DEFAULT_CONFIG if (arguments.length === 1 && typeof target === 'object') { source = target target = DEFAULT_CONFIG } return deepMerge(target, source) } /** * Validate configuration against schema * @param {Object} config - Configuration to validate * @throws {Error} If configuration is invalid */ function validateConfig (config) { for (const [key, validator] of Object.entries(CONFIG_SCHEMA)) { if (key in config) { try { validator(config[key]) } catch (error) { throw new Error(`Invalid configuration for '${key}': ${error.message}`) } } } } /** * Create a validated configuration object * @param {Object} userConfig - User-provided configuration * @returns {Object} Validated and merged configuration */ function createConfig (userConfig = {}) { const mergedConfig = mergeConfig(userConfig) validateConfig(mergedConfig) return mergedConfig } /** * Get default configuration (read-only) * @returns {Object} Deep copy of default configuration */ function getDefaultConfig () { // Deep clone function that preserves RegExp objects function deepClone (obj) { if (obj === null || typeof obj !== 'object') { return obj } if (obj instanceof RegExp) { return new RegExp(obj.source, obj.flags) } if (Array.isArray(obj)) { return obj.map(item => deepClone(item)) } const cloned = {} for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { cloned[key] = deepClone(obj[key]) } } return cloned } return deepClone(DEFAULT_CONFIG) } module.exports = { DEFAULT_CONFIG, CONFIG_SCHEMA, deepMerge, mergeConfig, validateConfig, createConfig, getDefaultConfig }