UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

228 lines (227 loc) 10.6 kB
import { UnifiedSecurityEngine, createDefaultSecurityConfig, createSessionId } from '../core/unified-security-engine.js'; import logger from '../../../logger.js'; export class SecurityMiddleware { static instance = null; securityEngine; config; constructor(config) { this.config = { enablePathValidation: true, enableInputSanitization: true, enableConcurrentAccess: true, performanceThresholdMs: 50, strictMode: true, logViolations: true, blockOnCriticalViolations: true, ...config }; const securityConfig = createDefaultSecurityConfig(); this.securityEngine = UnifiedSecurityEngine.getInstance(securityConfig); logger.info({ config: this.config }, 'Security Middleware initialized with UnifiedSecurityEngine'); } static getInstance(config) { if (!SecurityMiddleware.instance) { SecurityMiddleware.instance = new SecurityMiddleware(config); } return SecurityMiddleware.instance; } mapSeverity(severity) { switch (severity) { case 'info': return 'low'; case 'low': return 'low'; case 'medium': return 'medium'; case 'high': return 'high'; case 'critical': return 'critical'; default: return 'medium'; } } async validateCommandSecurity(data, context, operationContext) { const startTime = Date.now(); const violations = []; let sanitizedData = data; let pathValidation; let sanitizationResult; let lockResult; try { if (this.config.enableInputSanitization && data) { const sanitizationStart = Date.now(); const sanitizationResponse = await this.securityEngine.sanitizeData(data); const sanitizationTime = Date.now() - sanitizationStart; if (!sanitizationResponse.success) { violations.push({ type: 'sanitization', severity: 'high', description: 'Input sanitization failed', context: { error: sanitizationResponse.error?.message } }); if (this.config.blockOnCriticalViolations) { return this.createValidationResult(false, violations, { totalTime: Date.now() - startTime, sanitizationTime }); } } else { sanitizationResult = sanitizationResponse.data; sanitizedData = sanitizationResult?.sanitizedData || data; if (sanitizationResult?.violations) { sanitizationResult.violations.forEach(violation => { violations.push({ type: 'sanitization', severity: this.mapSeverity(violation.severity), description: violation.message, field: violation.field, originalValue: violation.originalValue, sanitizedValue: violation.sanitizedValue }); }); } } } if (this.config.enablePathValidation && operationContext.filePaths) { const pathValidationStart = Date.now(); for (const filePath of operationContext.filePaths) { const pathValidationResponse = await this.securityEngine.validatePath(filePath, 'write'); if (!pathValidationResponse.success) { violations.push({ type: 'path', severity: 'critical', description: pathValidationResponse.error?.message || 'Path validation failed', field: 'filePath', originalValue: filePath, context: { error: pathValidationResponse.error?.message } }); if (this.config.blockOnCriticalViolations) { return this.createValidationResult(false, violations, { totalTime: Date.now() - startTime, pathValidationTime: Date.now() - pathValidationStart }); } } else { pathValidation = pathValidationResponse.data; if (!pathValidation?.isValid) { violations.push({ type: 'path', severity: pathValidation?.violationType ? 'critical' : 'medium', description: pathValidation?.error || 'Path validation failed', field: 'filePath', originalValue: filePath, context: { violationType: pathValidation?.violationType } }); if (this.config.blockOnCriticalViolations && pathValidation?.violationType) { return this.createValidationResult(false, violations, { totalTime: Date.now() - startTime, pathValidationTime: Date.now() - pathValidationStart }); } } } } } if (this.config.enableConcurrentAccess && operationContext.requiresLock) { const lockStart = Date.now(); const lockResponse = await this.securityEngine.acquireLock(operationContext.resource || operationContext.operation, 'write', createSessionId(context.sessionId), operationContext.lockTimeout); const lockTime = Date.now() - lockStart; if (!lockResponse.success) { violations.push({ type: 'access', severity: 'medium', description: lockResponse.error?.message || 'Failed to acquire resource lock', context: { resource: operationContext.resource, error: lockResponse.error?.message } }); if (this.config.strictMode) { return this.createValidationResult(false, violations, { totalTime: Date.now() - startTime, lockAcquisitionTime: lockTime }); } } else { lockResult = lockResponse.data; } } const totalTime = Date.now() - startTime; if (totalTime > this.config.performanceThresholdMs) { violations.push({ type: 'performance', severity: 'medium', description: `Security validation exceeded performance threshold: ${totalTime}ms > ${this.config.performanceThresholdMs}ms`, context: { actualTime: totalTime, threshold: this.config.performanceThresholdMs } }); } if (this.config.logViolations && violations.length > 0) { logger.warn({ violations: violations.length, operation: operationContext.operation, sessionId: context.sessionId, totalTime }, 'Security violations detected'); } return this.createValidationResult(true, violations, { totalTime, pathValidationTime: pathValidation?.auditInfo.validationTime, sanitizationTime: sanitizationResult?.sanitizationTime, lockAcquisitionTime: lockResult?.waitTime }, sanitizedData, pathValidation, sanitizationResult, lockResult); } catch (error) { logger.error({ err: error, operation: operationContext.operation }, 'Security validation error'); violations.push({ type: 'access', severity: 'critical', description: `Security validation error: ${error instanceof Error ? error.message : String(error)}`, context: { error: error instanceof Error ? error.message : String(error) } }); return this.createValidationResult(false, violations, { totalTime: Date.now() - startTime }); } } async releaseLocks(lockIds) { if (!this.config.enableConcurrentAccess) { return; } for (const lockId of lockIds) { try { const typedLockId = typeof lockId === 'string' ? lockId : lockId; await this.securityEngine.releaseLock(typedLockId); } catch (error) { logger.error({ err: error, lockId }, 'Failed to release lock'); } } } createValidationResult(valid, violations, performanceMetrics, sanitizedData, pathValidation, sanitizationResult, lockResult) { return { valid, sanitizedData, pathValidation, sanitizationResult, lockResult, violations, performanceMetrics }; } getConfig() { return { ...this.config }; } updateConfig(config) { this.config = { ...this.config, ...config }; logger.info({ config: this.config }, 'Security Middleware configuration updated'); } shutdown() { logger.info('Security Middleware shutdown'); } } export async function validateCommandSecurity(data, context, operationContext) { const middleware = SecurityMiddleware.getInstance(); return middleware.validateCommandSecurity(data, context, operationContext); } export async function releaseLocks(lockIds) { const middleware = SecurityMiddleware.getInstance(); return middleware.releaseLocks(lockIds); }