refactor(security): unify hook rate-limit and hook module loading

This commit is contained in:
Peter Steinberger
2026-02-22 08:56:24 +01:00
parent 7cf280805c
commit 9f97555b5e
6 changed files with 152 additions and 82 deletions

View File

@@ -31,11 +31,14 @@ export interface RateLimitConfig {
lockoutMs?: number;
/** Exempt loopback (localhost) addresses from rate limiting. @default true */
exemptLoopback?: boolean;
/** Background prune interval in milliseconds; set <= 0 to disable auto-prune. @default 60_000 */
pruneIntervalMs?: number;
}
export const AUTH_RATE_LIMIT_SCOPE_DEFAULT = "default";
export const AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET = "shared-secret";
export const AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN = "device-token";
export const AUTH_RATE_LIMIT_SCOPE_HOOK_AUTH = "hook-auth";
export interface RateLimitEntry {
/** Timestamps (epoch ms) of recent failed attempts inside the window. */
@@ -94,13 +97,14 @@ export function createAuthRateLimiter(config?: RateLimitConfig): AuthRateLimiter
const windowMs = config?.windowMs ?? DEFAULT_WINDOW_MS;
const lockoutMs = config?.lockoutMs ?? DEFAULT_LOCKOUT_MS;
const exemptLoopback = config?.exemptLoopback ?? true;
const pruneIntervalMs = config?.pruneIntervalMs ?? PRUNE_INTERVAL_MS;
const entries = new Map<string, RateLimitEntry>();
// Periodic cleanup to avoid unbounded map growth.
const pruneTimer = setInterval(() => prune(), PRUNE_INTERVAL_MS);
const pruneTimer = pruneIntervalMs > 0 ? setInterval(() => prune(), pruneIntervalMs) : null;
// Allow the Node.js process to exit even if the timer is still active.
if (pruneTimer.unref) {
if (pruneTimer?.unref) {
pruneTimer.unref();
}
@@ -218,7 +222,9 @@ export function createAuthRateLimiter(config?: RateLimitConfig): AuthRateLimiter
}
function dispose(): void {
clearInterval(pruneTimer);
if (pruneTimer) {
clearInterval(pruneTimer);
}
entries.clear();
}