UNPKG

express-limiter-pro

Version:

A TypeScript library for safe display and sanitization to prevent XSS attacks.

205 lines (195 loc) 7.37 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // src/limits/limiters.ts import { rateLimit } from "express-rate-limit"; // src/utils/ipKeyHelpers.ts import { ipKeyGenerator } from "express-rate-limit"; function getIpFromRequest(req) { const xff = req.headers["x-forwarded-for"] ?? ""; const forwarded = xff.split(",")[0]?.trim(); const stripPort = /* @__PURE__ */ __name((ip) => ip.replace(/:\d+$/, ""), "stripPort"); if (req.ip) return stripPort(req.ip); if (forwarded) return stripPort(forwarded); return "127.0.0.1"; } __name(getIpFromRequest, "getIpFromRequest"); function ipKeyGeneratorFromReq(req, ipv6Subnet = 64) { const ip = getIpFromRequest(req); return ipKeyGenerator(ip, ipv6Subnet); } __name(ipKeyGeneratorFromReq, "ipKeyGeneratorFromReq"); // src/configs/rateLimitConfigs.ts var rateLimitConfigs = { development: { api: { windowMs: 15 * 60 * 1e3, max: 1e3, standardHeaders: true, legacyHeaders: false }, auth: { windowMs: 15 * 60 * 1e3, max: 50, standardHeaders: true, legacyHeaders: false }, sensitive: { windowMs: 15 * 60 * 1e3, max: 20, standardHeaders: true, legacyHeaders: false } }, production: { api: { windowMs: 15 * 60 * 1e3, max: 300, standardHeaders: true, legacyHeaders: false, keyGenerator: /* @__PURE__ */ __name((req) => ipKeyGeneratorFromReq(req), "keyGenerator") }, auth: { windowMs: 15 * 60 * 1e3, max: 20, standardHeaders: true, legacyHeaders: false, keyGenerator: /* @__PURE__ */ __name((req) => ipKeyGeneratorFromReq(req), "keyGenerator") }, sensitive: { windowMs: 60 * 60 * 1e3, max: 10, standardHeaders: true, legacyHeaders: false, keyGenerator: /* @__PURE__ */ __name((req) => ipKeyGeneratorFromReq(req), "keyGenerator") } } }; // src/logger/logger.ts var logger = { info: /* @__PURE__ */ __name((...args) => console.info("[info]", ...args), "info"), warn: /* @__PURE__ */ __name((...args) => console.warn("[warn]", ...args), "warn"), error: /* @__PURE__ */ __name((...args) => console.error("[error]", ...args), "error"), debug: /* @__PURE__ */ __name((...args) => console.debug("[debug]", ...args), "debug") }; // src/configs/configManager.ts import { EventEmitter } from "events"; // src/errors/error.ts var _ConfigNotFoundException = class _ConfigNotFoundException extends Error { constructor(message) { super(message ?? "Configuration not found"); this.name = "ConfigNotFoundException"; } }; __name(_ConfigNotFoundException, "ConfigNotFoundException"); var ConfigNotFoundException = _ConfigNotFoundException; // src/configs/configManager.ts var _ConfigManager = class _ConfigManager extends EventEmitter { constructor() { super(); } setConfigSource(source) { this.configSource = source; } setConfig(cfg) { this.config = { ...cfg }; this.emit("configAvailable", this.config); } getConfig() { return this.config; } async loadConfig() { if (!this.configSource) { throw new ConfigNotFoundException("Config source not set"); } try { const config = await this.configSource(); this.setConfig(config); return config; } catch (error) { throw new ConfigNotFoundException("Error loading configuration"); } } }; __name(_ConfigManager, "ConfigManager"); var ConfigManager = _ConfigManager; var configManager = new ConfigManager(); // src/limits/limiters.ts var apiLimiter = null; var authLimiter = null; var sensitiveLimiter = null; var createLimiter = /* @__PURE__ */ __name((options) => rateLimit({ ...options, skipSuccessfulRequests: false, handler: /* @__PURE__ */ __name((req, res) => { logger.warn(`Rate limit exceeded for ${req.ip} on ${req.originalUrl}`); return res.status(429).json({ success: false, error: options.errorName || "Rate Limit Exceeded", message: options.message || "Too many requests, please try again later." }); }, "handler") }), "createLimiter"); var initializeRateLimiters = /* @__PURE__ */ __name(async () => { try { const config = await configManager.getConfig(); const rateLimitConfig = rateLimitConfigs[config?.isDevelopment ? "development" : "production"]; apiLimiter = createLimiter({ ...rateLimitConfig.api, errorName: "API Rate Limit Exceeded", message: "Too many requests, please try again later." }); authLimiter = createLimiter({ ...rateLimitConfig.auth, errorName: "Authentication Rate Limit Exceeded", message: "Too many authentication attempts, please try again later." }); sensitiveLimiter = createLimiter({ ...rateLimitConfig.sensitive, errorName: "Operation Rate Limit Exceeded", message: "Too many sensitive operations attempted, please try again later." }); logger.info("Rate limiters initialized successfully"); } catch (error) { logger.error("Failed to initialize rate limiters:", error); throw error; } }, "initializeRateLimiters"); var getApiLimiter = /* @__PURE__ */ __name(() => { if (!apiLimiter) throw new Error("Rate limiters not initialized. Call initializeRateLimiters() first."); return apiLimiter; }, "getApiLimiter"); var getAuthLimiter = /* @__PURE__ */ __name(() => { if (!authLimiter) throw new Error("Rate limiters not initialized. Call initializeRateLimiters() first."); return authLimiter; }, "getAuthLimiter"); var getSensitiveLimiter = /* @__PURE__ */ __name(() => { if (!sensitiveLimiter) throw new Error("Rate limiters not initialized. Call initializeRateLimiters() first."); return sensitiveLimiter; }, "getSensitiveLimiter"); // src/configs/apiEndpoints.ts var API_ENDPOINT_HEADER = "X-API-Endpoint"; // src/setup/rateLimitSetup.ts async function configureRateLimits(app) { try { await initializeRateLimiters(); const apiLimiter2 = getApiLimiter(); const authLimiter2 = getAuthLimiter(); const sensitiveLimiter2 = getSensitiveLimiter(); const rateLimitRules = [ { paths: ["/api", "/api/v1"], limiter: apiLimiter2 }, { paths: ["/api/auth", "/api/v1/auth"], limiter: authLimiter2, condition: /* @__PURE__ */ __name((req) => ["/login", "/register", "/authenticate"].some((p) => req.path.includes(p)), "condition") }, { paths: ["/api"], limiter: sensitiveLimiter2, condition: /* @__PURE__ */ __name((req) => req.path.includes("/2fa") || ["auth:2fa-setup", "auth:2fa-enable", "auth:2fa-disable", "auth:2fa-verify"].includes(req.headers[API_ENDPOINT_HEADER] || req.headers[API_ENDPOINT_HEADER.toLowerCase()]), "condition") } ]; rateLimitRules.forEach((rule) => { if (rule.condition) { const middleware = /* @__PURE__ */ __name((req, res, next) => rule.condition(req) ? rule.limiter(req, res, next) : next(), "middleware"); app.use(rule.paths, middleware); } else { app.use(rule.paths, rule.limiter); } }); logger.info("Rate limiting configured successfully"); } catch (error) { logger.error("Failed to configure rate limiting:", error); throw error; } } __name(configureRateLimits, "configureRateLimits"); export { configManager, configureRateLimits }; //# sourceMappingURL=index.mjs.map