UNPKG

secure-kit

Version:

Production-grade security + performance toolkit for backend frameworks with OWASP Top 10 compliance

214 lines 9.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.KoaAdapter = void 0; const security_1 = require("../core/security"); const performance_1 = require("../core/performance"); const config_1 = require("../core/config"); class KoaAdapter { constructor(config) { this.config = config_1.ConfigManager.createConfig(config); this.securityManager = new security_1.SecurityManager(this.config); this.performanceManager = new performance_1.PerformanceManager(this.config); } createMiddleware() { return async (ctx, next) => { try { const startTime = Date.now(); // Apply security headers this.securityManager.applySecurityHeaders(ctx.res); // CORS validation const origin = ctx.get('Origin'); if (origin && !this.securityManager.validateCORS(origin, ctx.method)) { ctx.status = 403; ctx.body = { error: 'CORS policy violation' }; return; } // Rate limiting const clientIp = this.getClientIP(ctx); const rateLimitInfo = this.securityManager.checkRateLimit(clientIp); if (rateLimitInfo && rateLimitInfo.remaining === 0) { ctx.set('Retry-After', rateLimitInfo.retryAfter?.toString() || '60'); ctx.status = 429; ctx.body = { error: 'Rate limit exceeded', retryAfter: rateLimitInfo.retryAfter, }; return; } // Apply rate limit headers if (rateLimitInfo) { ctx.set('X-RateLimit-Limit', rateLimitInfo.limit.toString()); ctx.set('X-RateLimit-Remaining', rateLimitInfo.remaining.toString()); ctx.set('X-RateLimit-Reset', rateLimitInfo.resetTime.toISOString()); } // JWT validation (if Authorization header is present) const authHeader = ctx.get('Authorization'); if (authHeader && authHeader.startsWith('Bearer ')) { const token = authHeader.substring(7); const jwtValidation = this.securityManager.validateJWT(token); if (!jwtValidation.valid) { ctx.status = 401; ctx.body = { error: jwtValidation.error }; return; } // Attach user info to context ctx.user = jwtValidation.payload; } // Input sanitization if (ctx.request.body) { const bodyValidation = this.securityManager.sanitizeInput(ctx.request.body, 'body'); if (!bodyValidation.isValid) { ctx.status = 400; ctx.body = { error: 'Invalid input detected', details: bodyValidation.errors, }; return; } ctx.request.body = bodyValidation.sanitizedData; } if (ctx.query) { const queryValidation = this.securityManager.sanitizeInput(ctx.query, 'query'); if (!queryValidation.isValid) { ctx.status = 400; ctx.body = { error: 'Invalid query parameters detected', details: queryValidation.errors, }; return; } ctx.query = queryValidation.sanitizedData; } await next(); // Performance monitoring const duration = Date.now() - startTime; const payloadSize = ctx.length || 0; this.performanceManager.recordMetrics(duration, payloadSize); // Optimize response if (ctx.body) { const optimized = this.performanceManager.optimizeResponse(ctx.body); this.performanceManager.applyCachingHeaders(ctx.res, optimized.data); ctx.body = optimized.data; } } catch (error) { console.error('Secure backend middleware error:', error); ctx.status = 500; ctx.body = { error: 'Internal server error' }; } }; } // Helper methods getClientIP(ctx) { return (ctx.ip || ctx.request.ip || ctx.req.connection.remoteAddress || 'unknown'); } // Utility methods for external use getSecurityManager() { return this.securityManager; } getPerformanceManager() { return this.performanceManager; } getConfig() { return this.config; } // Create specialized middleware createJWTAuthMiddleware() { return async (ctx, next) => { const authHeader = ctx.get('Authorization'); if (!authHeader || !authHeader.startsWith('Bearer ')) { ctx.status = 401; ctx.body = { error: 'Authorization header required' }; return; } const token = authHeader.substring(7); const validation = this.securityManager.validateJWT(token); if (!validation.valid) { ctx.status = 401; ctx.body = { error: validation.error }; return; } ctx.user = validation.payload; await next(); }; } createRateLimitMiddleware() { return async (ctx, next) => { const clientIp = this.getClientIP(ctx); const rateLimitInfo = this.securityManager.checkRateLimit(clientIp); if (rateLimitInfo && rateLimitInfo.remaining === 0) { ctx.set('Retry-After', rateLimitInfo.retryAfter?.toString() || '60'); ctx.status = 429; ctx.body = { error: 'Rate limit exceeded', retryAfter: rateLimitInfo.retryAfter, }; return; } if (rateLimitInfo) { ctx.set('X-RateLimit-Limit', rateLimitInfo.limit.toString()); ctx.set('X-RateLimit-Remaining', rateLimitInfo.remaining.toString()); ctx.set('X-RateLimit-Reset', rateLimitInfo.resetTime.toISOString()); } await next(); }; } // Add security routes to Koa app addSecurityRoutes(app) { // CSRF token endpoint if (this.config.security?.csrf?.enabled) { app.use(async (ctx, next) => { if (ctx.path === '/csrf-token' && ctx.method === 'GET') { const token = this.securityManager.generateCSRFToken(); const cookieName = this.config.security?.csrf?.cookieName || 'csrf-token'; ctx.cookies.set(cookieName, token, { httpOnly: true, secure: true, sameSite: 'strict', maxAge: 3600000, }); ctx.body = { csrfToken: token }; return; } await next(); }); } // Security events endpoint if (this.config.logging?.enabled) { app.use(async (ctx, next) => { if (ctx.path === '/security-events' && ctx.method === 'GET') { const events = this.securityManager.getSecurityEvents(); ctx.body = { events }; return; } await next(); }); // Performance metrics endpoint app.use(async (ctx, next) => { if (ctx.path === '/performance-metrics' && ctx.method === 'GET') { const metrics = this.performanceManager.getMetrics(); const avgResponseTime = this.performanceManager.getAverageResponseTime(); const avgPayloadSize = this.performanceManager.getAveragePayloadSize(); const memoryUsage = this.performanceManager.getMemoryUsage(); const suggestions = this.performanceManager.getOptimizationSuggestions(); ctx.body = { metrics: metrics.slice(-100), summary: { averageResponseTime: avgResponseTime, averagePayloadSize: avgPayloadSize, memoryUsage, suggestions, }, }; return; } await next(); }); } } } exports.KoaAdapter = KoaAdapter; //# sourceMappingURL=koa.js.map