UNPKG

@hivetechs/hive-ai

Version:

Real-time streaming AI consensus platform with HTTP+SSE MCP integration for Claude Code, VS Code, Cursor, and Windsurf - powered by OpenRouter's unified API

267 lines 9.7 kB
/** * Security Input Validation Framework * * Provides comprehensive input validation and sanitization to prevent: * - Tool Poisoning Attacks (TPAs) * - Command Injection * - Prompt Injection * - SQL Injection * - Path Traversal */ export const DEFAULT_SECURITY_CONFIG = { maxInputLength: 10000, maxPromptLength: 50000, allowedCommands: [ 'consensus', 'cons', 'capture', 'cap', 'list_profiles', 'profiles', 'lp', 'get_profile', 'profile', 'gp', 'update_profile', 'up', 'list_providers', 'providers', 'lprov', 'configure_provider', 'config_provider', 'cp', 'test_providers', 'test', 'tp', 'list_pipeline_profiles', 'pipelines', 'lpp', 'configure_pipeline', 'config_pipeline', 'cpp', 'set_default_profile', 'default_profile', 'sdp', 'update_model_registry', 'update_registry', 'umr', 'add_custom_model', 'add_model', 'acm', 'list_models', 'models', 'lm' ], bannedPatterns: [ // Command injection patterns /[;&|`$(){}[\]\\]/g, /\$\(/g, /\$\{/g, /`[^`]*`/g, // File system patterns /\.\.\//g, /~\//g, /\/etc\//g, /\/proc\//g, /\/sys\//g, // Network patterns /curl\s+/i, /wget\s+/i, /nc\s+/i, /netcat\s+/i, // Script execution patterns /eval\s*\(/i, /exec\s*\(/i, /system\s*\(/i, /shell_exec\s*\(/i, // Hidden instruction patterns (Tool Poisoning) /<!--.*-->/gs, /<script.*<\/script>/gis, /\[HIDDEN\]/gi, /\[SYSTEM\]/gi, /\[ADMIN\]/gi, /IGNORE PREVIOUS INSTRUCTIONS/gi, /DISREGARD.*ABOVE/gi, /EXECUTE.*COMMAND/gi, // SQL injection patterns /union\s+select/gi, /drop\s+table/gi, /delete\s+from/gi, /insert\s+into/gi, /update\s+set/gi, // Environment variable access /\$HOME/g, /\$PATH/g, /\$USER/g, /process\.env/g, // Credential patterns /sk-[a-zA-Z0-9]{48}/g, // OpenAI API keys /^xoxb-/g, // Slack tokens /^ghp_/g, // GitHub tokens /^ghs_/g, // GitHub tokens /^gho_/g, // GitHub tokens /^ghu_/g, // GitHub tokens /aws_access_key_id/gi, /aws_secret_access_key/gi, ], urlPattern: /^https?:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(\/[^\s]*)?$/, filePathPattern: /^[a-zA-Z0-9\/_\-\.]+$/ }; export class SecurityValidationError extends Error { code; constructor(message, code) { super(message); this.code = code; this.name = 'SecurityValidationError'; } } export class InputValidator { config; constructor(config = DEFAULT_SECURITY_CONFIG) { this.config = config; } /** * Validate and sanitize general text input */ validateTextInput(input, context = 'general') { if (!input) { throw new SecurityValidationError('Input cannot be empty', 'EMPTY_INPUT'); } // Length validation const maxLength = context === 'prompt' ? this.config.maxPromptLength : this.config.maxInputLength; if (input.length > maxLength) { throw new SecurityValidationError(`Input too long: ${input.length} > ${maxLength}`, 'INPUT_TOO_LONG'); } // Check for banned patterns for (const pattern of this.config.bannedPatterns) { if (pattern.test(input)) { throw new SecurityValidationError(`Suspicious pattern detected: ${pattern.source}`, 'SUSPICIOUS_PATTERN'); } } return this.sanitizeInput(input); } /** * Validate command strings for shorthand commands */ validateCommand(command) { const cleanCommand = command.trim().toLowerCase(); if (!this.config.allowedCommands.includes(cleanCommand)) { throw new SecurityValidationError(`Command not allowed: ${command}`, 'INVALID_COMMAND'); } return cleanCommand; } /** * Validate and sanitize URLs */ validateUrl(url) { if (!url) { throw new SecurityValidationError('URL cannot be empty', 'EMPTY_URL'); } // Basic format validation if (!this.config.urlPattern.test(url)) { throw new SecurityValidationError(`Invalid URL format: ${url}`, 'INVALID_URL_FORMAT'); } // Check for dangerous protocols const lowercaseUrl = url.toLowerCase(); if (lowercaseUrl.startsWith('file://') || lowercaseUrl.startsWith('ftp://') || lowercaseUrl.startsWith('javascript:') || lowercaseUrl.startsWith('data:')) { throw new SecurityValidationError(`Dangerous URL protocol: ${url}`, 'DANGEROUS_PROTOCOL'); } // Validate domain allowlist for API endpoints const allowedDomains = [ 'api.openai.com', 'api.anthropic.com', 'generativelanguage.googleapis.com', 'api.x.ai', 'hive-api.hivetechs.io' ]; try { const urlObj = new URL(url); const domain = urlObj.hostname.toLowerCase(); const isAllowed = allowedDomains.some(allowedDomain => domain === allowedDomain || domain.endsWith('.' + allowedDomain)); if (!isAllowed) { throw new SecurityValidationError(`Domain not allowed: ${domain}`, 'DOMAIN_NOT_ALLOWED'); } } catch (error) { if (error instanceof SecurityValidationError) { throw error; } throw new SecurityValidationError(`Invalid URL: ${url}`, 'INVALID_URL'); } return url; } /** * Validate file paths to prevent path traversal */ validateFilePath(path) { if (!path) { throw new SecurityValidationError('Path cannot be empty', 'EMPTY_PATH'); } // Check for path traversal patterns if (path.includes('..') || path.includes('~') || path.startsWith('/')) { throw new SecurityValidationError(`Dangerous path pattern: ${path}`, 'DANGEROUS_PATH'); } // Validate path format if (!this.config.filePathPattern.test(path)) { throw new SecurityValidationError(`Invalid path format: ${path}`, 'INVALID_PATH_FORMAT'); } return path; } /** * Validate conversation IDs (UUIDs) */ validateConversationId(id) { const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; if (!uuidPattern.test(id)) { throw new SecurityValidationError(`Invalid conversation ID format: ${id}`, 'INVALID_CONVERSATION_ID'); } return id; } /** * Validate API keys to prevent exposure */ validateApiKey(key) { if (!key) { throw new SecurityValidationError('API key cannot be empty', 'EMPTY_API_KEY'); } // Length validation (most API keys are between 20-200 characters) if (key.length < 10 || key.length > 300) { throw new SecurityValidationError(`Invalid API key length: ${key.length}`, 'INVALID_API_KEY_LENGTH'); } // Check for common API key patterns (more flexible for real world usage) const keyPatterns = [ /^sk-[a-zA-Z0-9\-_]{20,}$/, // OpenAI (flexible length) /^sk-ant-[a-zA-Z0-9\-_]{50,}$/, // Anthropic (sk-ant- prefix, flexible) /^AIzaS[a-zA-Z0-9\-_]{20,}$/, // Google (AIzaS prefix, flexible) /^xai-[a-zA-Z0-9\-_]{20,}$/, // xAI (flexible length) /^gsk_[a-zA-Z0-9\-_]{20,}$/, // Google alternative format /^[a-zA-Z0-9\-_\.]{20,300}$/ // Generic pattern (very flexible for any provider) ]; const isValidPattern = keyPatterns.some(pattern => pattern.test(key)); if (!isValidPattern) { throw new SecurityValidationError('Invalid API key format', 'INVALID_API_KEY_FORMAT'); } return key; } /** * Sanitize input by removing/escaping dangerous characters */ sanitizeInput(input) { return input // Remove null bytes .replace(/\0/g, '') // Remove or escape HTML/XML tags .replace(/<[^>]*>/g, '') // Remove invisible characters .replace(/[\u200B-\u200D\uFEFF]/g, '') // Normalize whitespace .replace(/\s+/g, ' ') .trim(); } /** * Validate prompt specifically for AI models to prevent injection */ validatePrompt(prompt) { // Use text validation first const validatedPrompt = this.validateTextInput(prompt, 'prompt'); // Additional prompt-specific validations const promptInjectionPatterns = [ /ignore\s+previous\s+instructions/gi, /disregard\s+.+above/gi, /forget\s+everything/gi, /new\s+instructions/gi, /system\s*:\s*/gi, /assistant\s*:\s*/gi, /\[\/INST\]/gi, /\<\|system\|\>/gi, /\<\|user\|\>/gi, /\<\|assistant\|\>/gi, ]; for (const pattern of promptInjectionPatterns) { if (pattern.test(validatedPrompt)) { throw new SecurityValidationError('Prompt injection pattern detected', 'PROMPT_INJECTION'); } } return validatedPrompt; } } // Export default instance export const defaultValidator = new InputValidator(); //# sourceMappingURL=input-validator.js.map