godcrm/backend/middleware/rateLimiter.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

154 lines
4 KiB
JavaScript

// backend/middleware/rateLimiter.js
// SEC-020: Rate Limiting - ADR-015, ADR-064
// Created: 2026-01-08
import rateLimit from 'express-rate-limit';
const isProduction = process.env.NODE_ENV === 'production';
// Trusted IPs — bypass global & auth rate limiting (admin, server-to-server)
const TRUSTED_IPS = new Set([
'<ADMIN_IP>', // Admin
'<LEGACY_IP>', // PROD server
'<DEV_IP>', // DEV server
'127.0.0.1', // localhost
'::1', // localhost IPv6
'::ffff:127.0.0.1', // localhost IPv4-mapped
]);
function isTrustedIP(req) {
return TRUSTED_IPS.has(req.ip);
}
// ADR-064: Environment-aware rate limit configuration
export const RATE_LIMIT_CONFIG = {
global: { windowMs: 15 * 60 * 1000, max: isProduction ? 10000 : 15000 },
auth: { windowMs: 15 * 60 * 1000, max: isProduction ? 50 : 100 }
};
/**
* Global API rate limiter
* Production: 1000 requests per 15 minutes per IP
* Development: 3000 requests per 15 minutes per IP
*/
export const globalLimiter = rateLimit({
windowMs: RATE_LIMIT_CONFIG.global.windowMs,
max: RATE_LIMIT_CONFIG.global.max,
standardHeaders: true,
legacyHeaders: false,
skip: isTrustedIP,
message: {
success: false,
error: {
code: 'RATE_LIMITED',
message: 'Too many requests, please try again later'
},
timestamp: new Date().toISOString()
},
// Using default IP-based key generator (handles IPv6 correctly)
validate: { xForwardedForHeader: false }
});
/**
* Auth endpoints rate limiter
* Production: 10 attempts per 15 minutes per IP
* Development: 100 attempts per 15 minutes per IP
*/
export const authLimiter = rateLimit({
windowMs: RATE_LIMIT_CONFIG.auth.windowMs,
max: RATE_LIMIT_CONFIG.auth.max,
standardHeaders: true,
legacyHeaders: false,
skip: isTrustedIP,
message: {
success: false,
error: {
code: 'AUTH_RATE_LIMITED',
message: 'Too many login attempts, please try again later'
},
timestamp: new Date().toISOString()
},
skipSuccessfulRequests: true // Don't count successful logins
});
/**
* API Key rate limiter factory
* 100 requests per minute per API key
* @returns {import('express').RequestHandler}
*/
export function createApiKeyLimiter() {
return rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 100,
standardHeaders: true,
legacyHeaders: false,
message: {
success: false,
error: {
code: 'API_KEY_RATE_LIMITED',
message: 'API key rate limit exceeded, please slow down'
},
timestamp: new Date().toISOString()
},
// Using default key generator, API key validation happens in middleware
validate: { xForwardedForHeader: false }
});
}
/**
* Strict limiter for sensitive operations (password reset, etc)
* 3 attempts per hour per IP
*/
export const strictLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 3,
standardHeaders: true,
legacyHeaders: false,
message: {
success: false,
error: {
code: 'STRICT_RATE_LIMITED',
message: 'Too many attempts, please try again in an hour'
},
timestamp: new Date().toISOString()
}
});
/**
* File upload limiter
* 20 uploads per 10 minutes per IP
*/
export const uploadLimiter = rateLimit({
windowMs: 10 * 60 * 1000, // 10 minutes
max: 20,
standardHeaders: true,
legacyHeaders: false,
message: {
success: false,
error: {
code: 'UPLOAD_RATE_LIMITED',
message: 'Too many uploads, please try again later'
},
timestamp: new Date().toISOString()
}
});
/**
* Code execution rate limiter (ADR-032)
* 30 executions per minute per IP
* Prevents abuse of code execution resources
*/
export const codeExecutionLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 30,
standardHeaders: true,
legacyHeaders: false,
message: {
success: false,
error: {
code: 'CODE_EXECUTION_RATE_LIMITED',
message: 'Too many code executions, please wait before trying again'
},
timestamp: new Date().toISOString()
}
});