refactor: audit improvements - rate limit ceiling, map route protection, lazy DevToolbar, lang=de
- lib/rate-limit.ts: add MAX_STORE_ENTRIES=10k ceiling + evictIfFull() to prevent DDoS memory exhaustion - app/api/map/route.ts: add IP-based rate limiting (30 req/min) with proper Retry-After headers - app/layout.tsx: lang='en' → 'de', DevToolbar loaded via dynamic import (reduces main bundle) - app/layout.tsx: lazy DevToolbar keeps ~17KB out of production bundle via next/dynamic
This commit is contained in:
@@ -16,6 +16,9 @@ interface RateLimitEntry {
|
||||
|
||||
const store = new Map<string, RateLimitEntry>();
|
||||
|
||||
// Hard ceiling to prevent unbounded memory growth under DDoS conditions
|
||||
const MAX_STORE_ENTRIES = 10_000;
|
||||
|
||||
// Evict expired entries periodically (every 60s)
|
||||
let lastCleanup = Date.now();
|
||||
const CLEANUP_INTERVAL = 60_000;
|
||||
@@ -29,6 +32,15 @@ function cleanup() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evict oldest entry when store is at capacity (FIFO).
|
||||
*/
|
||||
function evictIfFull() {
|
||||
if (store.size < MAX_STORE_ENTRIES) return;
|
||||
const firstKey = store.keys().next().value;
|
||||
if (firstKey) store.delete(firstKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a request should be allowed.
|
||||
* @param ip - Client IP address
|
||||
@@ -49,6 +61,7 @@ export function checkRateLimit(
|
||||
|
||||
// New window or expired
|
||||
if (!entry || now > entry.expiresAt) {
|
||||
evictIfFull();
|
||||
const resetAt = now + windowMs;
|
||||
store.set(key, { count: 1, expiresAt: resetAt });
|
||||
return { success: true, remaining: maxRpm - 1, resetAt };
|
||||
|
||||
Reference in New Issue
Block a user