secure-kit
Version:
Production-grade security + performance toolkit for backend frameworks with OWASP Top 10 compliance
198 lines • 8.89 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExpressAdapter = void 0;
const security_1 = require("../core/security");
const performance_1 = require("../core/performance");
const config_1 = require("../core/config");
class ExpressAdapter {
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);
}
// Main middleware factory
createMiddleware() {
return async (req, res, next) => {
try {
const startTime = Date.now();
// Apply security headers
this.securityManager.applySecurityHeaders(res);
// CORS validation
const origin = req.get('Origin');
if (origin && !this.securityManager.validateCORS(origin, req.method)) {
return res.status(403).json({ error: 'CORS policy violation' });
}
// Rate limiting
const clientIp = this.getClientIP(req);
const rateLimitInfo = this.securityManager.checkRateLimit(clientIp);
if (rateLimitInfo && rateLimitInfo.remaining === 0) {
res.set('Retry-After', rateLimitInfo.retryAfter?.toString() || '60');
return res.status(429).json({
error: 'Rate limit exceeded',
retryAfter: rateLimitInfo.retryAfter,
});
}
// Apply rate limit headers
if (rateLimitInfo) {
res.set('X-RateLimit-Limit', rateLimitInfo.limit.toString());
res.set('X-RateLimit-Remaining', rateLimitInfo.remaining.toString());
res.set('X-RateLimit-Reset', rateLimitInfo.resetTime.toISOString());
}
// JWT validation (if Authorization header is present)
const authHeader = req.get('Authorization');
if (authHeader && authHeader.startsWith('Bearer ')) {
const token = authHeader.substring(7);
const jwtValidation = this.securityManager.validateJWT(token);
if (!jwtValidation.valid) {
return res.status(401).json({ error: jwtValidation.error });
}
// Attach user info to request
req.user = jwtValidation.payload;
}
// Input sanitization
if (req.body) {
const bodyValidation = this.securityManager.sanitizeInput(req.body, 'body');
if (!bodyValidation.isValid) {
return res.status(400).json({
error: 'Invalid input detected',
details: bodyValidation.errors,
});
}
req.body = bodyValidation.sanitizedData;
}
if (req.query) {
const queryValidation = this.securityManager.sanitizeInput(req.query, 'query');
if (!queryValidation.isValid) {
return res.status(400).json({
error: 'Invalid query parameters detected',
details: queryValidation.errors,
});
}
req.query = queryValidation.sanitizedData;
}
if (req.params) {
const paramsValidation = this.securityManager.sanitizeInput(req.params, 'params');
if (!paramsValidation.isValid) {
return res.status(400).json({
error: 'Invalid path parameters detected',
details: paramsValidation.errors,
});
}
req.params = paramsValidation.sanitizedData;
}
// Record metrics on response finish
res.on('finish', () => {
const duration = Date.now() - startTime;
const payloadSize = parseInt(res.get('Content-Length') || '0');
this.performanceManager.recordMetrics(duration, payloadSize);
});
return next();
}
catch (error) {
console.error('Secure backend middleware error:', error);
return res.status(500).json({ error: 'Internal server error' });
}
};
}
// Helper methods
getClientIP(req) {
return (req.ip ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket?.remoteAddress ||
'unknown');
}
// Utility methods for external use
getSecurityManager() {
return this.securityManager;
}
getPerformanceManager() {
return this.performanceManager;
}
getConfig() {
return this.config;
}
// Specialized middleware
createJWTAuthMiddleware() {
return (req, res, next) => {
const authHeader = req.get('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Authorization header required' });
}
const token = authHeader.substring(7);
const validation = this.securityManager.validateJWT(token);
if (!validation.valid) {
return res.status(401).json({ error: validation.error });
}
req.user = validation.payload;
return next();
};
}
createRateLimitMiddleware() {
return (req, res, next) => {
const clientIp = this.getClientIP(req);
const rateLimitInfo = this.securityManager.checkRateLimit(clientIp);
if (rateLimitInfo && rateLimitInfo.remaining === 0) {
res.set('Retry-After', rateLimitInfo.retryAfter?.toString() || '60');
return res.status(429).json({
error: 'Rate limit exceeded',
retryAfter: rateLimitInfo.retryAfter,
});
}
if (rateLimitInfo) {
res.set('X-RateLimit-Limit', rateLimitInfo.limit.toString());
res.set('X-RateLimit-Remaining', rateLimitInfo.remaining.toString());
res.set('X-RateLimit-Reset', rateLimitInfo.resetTime.toISOString());
}
return next();
};
}
// Add security routes to Express app
addSecurityRoutes(app) {
// CSRF token endpoint
if (this.config.security?.csrf?.enabled) {
app.get('/csrf-token', (_req, res) => {
const token = this.securityManager.generateCSRFToken();
const cookieName = this.config.security?.csrf?.cookieName || 'csrf-token';
res.cookie(cookieName, token, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 3600000,
});
res.json({ csrfToken: token });
});
}
// Security events endpoint
if (this.config.logging?.enabled) {
app.get('/security-events', (_req, res) => {
const events = this.securityManager.getSecurityEvents();
res.json({ events });
});
// Performance metrics endpoint
app.get('/performance-metrics', (_req, res) => {
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();
res.json({
metrics: metrics.slice(-100),
summary: {
averageResponseTime: avgResponseTime,
averagePayloadSize: avgPayloadSize,
memoryUsage,
suggestions,
},
});
});
}
// Configuration summary endpoint
app.get('/config-summary', (_req, res) => {
const summary = config_1.ConfigManager.getConfigSummary(this.config);
res.json(JSON.parse(summary));
});
}
}
exports.ExpressAdapter = ExpressAdapter;
//# sourceMappingURL=express.js.map