express-limiter-pro
Version:
A TypeScript library for safe display and sanitization to prevent XSS attacks.
231 lines (220 loc) • 8.51 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
configManager: () => configManager,
configureRateLimits: () => configureRateLimits
});
module.exports = __toCommonJS(index_exports);
// src/limits/limiters.ts
var import_express_rate_limit2 = require("express-rate-limit");
// src/utils/ipKeyHelpers.ts
var import_express_rate_limit = require("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 (0, import_express_rate_limit.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
var import_events = require("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 import_events.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) => (0, import_express_rate_limit2.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");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
configManager,
configureRateLimits
});
//# sourceMappingURL=index.js.map