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
JavaScript
;
/**
* @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,
};