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
JavaScript
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);
}