UNPKG

mcp-quiz-server

Version:

🧠 AI-Powered Quiz Management via Model Context Protocol (MCP) - Create, manage, and take quizzes directly from VS Code, Claude, and other AI agents.

180 lines (179 loc) • 6.34 kB
"use strict"; /** * @moduleName: Input Sanitization Middleware * @version: 1.0.0 * @since: 2025-01-23 * @lastUpdated: 2025-01-23 * @projectSummary: Input sanitization and validation middleware for security * @techStack: Express.js, DOMPurify, TypeScript * @dependency: express, dompurify, jsdom * @interModuleDependency: None * @requirementsTraceability: * {@link Requirements.REQ_SEC_001} (Input Validation Security) * {@link Requirements.REQ_SEC_002} (XSS Prevention) * {@link Requirements.REQ_SEC_003} (Injection Attack Prevention) * @briefDescription: Middleware to sanitize user input and prevent XSS/injection attacks * @methods: sanitizeInput, validateInputSchema, preventInjection * @contributors: GitHub Copilot * @examples: app.use(sanitizeMiddleware) * @vulnerabilitiesAssessment: Prevents XSS, SQL injection, script injection attacks */ Object.defineProperty(exports, "__esModule", { value: true }); exports.sanitizeInput = sanitizeInput; exports.validateInputLength = validateInputLength; const audit_logger_1 = require("./audit-logger"); // Simple HTML tag removal for basic XSS prevention function stripHtmlTags(str) { if (typeof str !== 'string') return str; return str.replace(/<[^>]*>/g, ''); } // Basic SQL injection pattern detection function containsSqlInjection(str) { if (typeof str !== 'string') return false; const sqlPatterns = [ /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION|SCRIPT)\b)/i, /(;|\s)(--|#)/i, /('\s*(OR|AND)\s*')/i, /('\s*=\s*')/i, /(EXEC\s*\()/i, /(\bOR\b\s+\d+\s*=\s*\d+)/i, /(\bUNION\b.*\bSELECT\b)/i, ]; return sqlPatterns.some(pattern => pattern.test(str)); } // Basic XSS pattern detection function containsXssPayload(str) { if (typeof str !== 'string') return false; const xssPatterns = [ /<script[^>]*>.*?<\/script>/gi, /javascript:/gi, /on\w+\s*=/gi, /<iframe[^>]*>/gi, /<object[^>]*>/gi, /<embed[^>]*>/gi, /<link[^>]*>/gi, /<meta[^>]*>/gi, /vbscript:/gi, /data:text\/html/gi, ]; return xssPatterns.some(pattern => pattern.test(str)); } // Recursively sanitize object properties function sanitizeObject(obj) { if (obj === null || obj === undefined) return obj; if (typeof obj === 'string') { // Check for malicious patterns if (containsSqlInjection(obj)) { throw new Error('Potential SQL injection detected in input'); } if (containsXssPayload(obj)) { throw new Error('Potential XSS payload detected in input'); } // Basic sanitization - strip HTML tags return stripHtmlTags(obj.trim()); } if (typeof obj === 'number' || typeof obj === 'boolean') { return obj; } if (Array.isArray(obj)) { return obj.map(sanitizeObject); } if (typeof obj === 'object') { const sanitized = {}; for (const [key, value] of Object.entries(obj)) { // Sanitize both key and value const sanitizedKey = typeof key === 'string' ? stripHtmlTags(key) : key; sanitized[sanitizedKey] = sanitizeObject(value); } return sanitized; } return obj; } // Input sanitization middleware function sanitizeInput(req, res, next) { try { // Sanitize request body if (req.body && typeof req.body === 'object') { req.body = sanitizeObject(req.body); } // Sanitize query parameters if (req.query && typeof req.query === 'object') { req.query = sanitizeObject(req.query); } // Sanitize URL parameters if (req.params && typeof req.params === 'object') { req.params = sanitizeObject(req.params); } next(); } catch (error) { // Log security event for blocked malicious input const auditLogger = (0, audit_logger_1.getAuditLogger)(); auditLogger.logSecurityEvent('MALICIOUS_INPUT_BLOCKED', { ipAddress: req.ip || req.connection.remoteAddress, userAgent: req.get('User-Agent'), threat_type: 'INPUT_VALIDATION', severity: 'HIGH', blocked: true, metadata: { url: req.url, method: req.method, errorMessage: error instanceof Error ? error.message : 'Unknown error', bodySize: JSON.stringify(req.body || {}).length, }, }); // Return 400 for malicious input res.status(400).json({ error: { code: 'MALICIOUS_INPUT', message: error instanceof Error ? error.message : 'Malicious input detected', details: 'Request contains potentially harmful content', }, }); } } // Length validation middleware function validateInputLength(maxLength = 10000) { return (req, res, next) => { const bodySize = JSON.stringify(req.body || {}).length; if (bodySize > maxLength) { // Log security event for oversized input const auditLogger = (0, audit_logger_1.getAuditLogger)(); auditLogger.logSecurityEvent('OVERSIZED_INPUT_BLOCKED', { ipAddress: req.ip || req.connection.remoteAddress, userAgent: req.get('User-Agent'), threat_type: 'INPUT_SIZE_LIMIT', severity: 'MEDIUM', blocked: true, metadata: { url: req.url, method: req.method, maxLength, actualSize: bodySize, exceedsBy: bodySize - maxLength, }, }); res.status(400).json({ error: { code: 'INPUT_TOO_LARGE', message: `Request body exceeds maximum size of ${maxLength} characters`, details: `Current size: ${bodySize} characters`, }, }); return; } next(); }; } // Export middleware functions exports.default = { sanitizeInput, validateInputLength, stripHtmlTags, containsSqlInjection, containsXssPayload, };