Summary
The in-memory rate limiter in src/lib/rate-limit.ts has two compounding failures that make it non-functional in any real deployment: (1) it uses a module-level Map that is reset on every serverless function cold start — meaning Next.js API routes on Vercel, AWS Lambda, or any serverless platform get a fresh empty rate limit store per invocation; (2) it trusts the X-Forwarded-For header completely without validation, meaning any client can set an arbitrary IP and bypass per-IP limits with zero friction. Even where the rate limiter is wired up (only /api/signals), it provides no real protection.
Evidence
File: src/lib/rate-limit.ts:
// Module-level Map — reset on every serverless cold start
const rateLimitStore = new Map<string, RateLimitEntry>();
function getClientIdentifier(request: Request): string {
const forwarded = request.headers.get('x-forwarded-for');
const ip = forwarded?.split(',')[0]?.trim() || // ← trusts attacker-controlled header
request.headers.get('x-real-ip') ||
'unknown';
return `${ip}:${userAgent.slice(0, 50)}`;
}
Rate limiter only called in one route out of ~30:
// src/app/api/signals/route.ts — only this route calls applyRateLimit
const rateLimitResponse = applyRateLimit(request);
if (rateLimitResponse) { return rateLimitResponse; }
Routes with NO rate limiting whatsoever:
POST /api/notify (Telegram broadcast)
POST /api/alerts (Telegram send + SSRF)
POST /api/telegram (webhook)
GET /api/brief (AI brief generation)
GET /api/military-aircraft
GET /api/infrastructure (nuclear data)
- All other ~25 endpoints
Bypass in 1 request:
curl https://feed.xdc.network/api/signals \
-H 'X-Forwarded-For: 1.2.3.4'
# Fresh rate limit bucket for IP 1.2.3.4
Why this matters
- An attacker can flood any unprotected endpoint (particularly
/api/notify and /api/alerts) with unlimited requests, causing Telegram API rate limits to be hit for the bot token, potentially suspending the bot.
- The trusted
X-Forwarded-For header bypass means even the one protected endpoint (/api/signals) can be hammered without limit.
- In serverless deployments, the
Map is empty at every cold start, so the rate limiter is literally non-functional.
- Absence of rate limiting on
/api/brief means the expensive brief-generation logic can be called continuously.
Root cause
The rate limiter was designed for a long-running Node.js server process (PM2), not for serverless. It was applied to only one endpoint and never validated the X-Forwarded-For header. The in-memory state was never replaced with a persistent store.
Recommended fix
- Replace the in-memory
Map with a proper persistent rate limit backend:
- Redis with
ioredis (if self-hosted).
- Upstash Redis (serverless-compatible).
- Vercel KV / edge middleware rate limiting if deploying to Vercel.
- Validate
X-Forwarded-For — only trust it when the request comes from a known proxy IP, or use x-real-ip set by your own nginx config.
- Apply rate limiting to ALL API routes, especially
POST /api/notify, POST /api/alerts, and GET /api/infrastructure.
Acceptance criteria
Suggested labels
security, bug
Priority
P0
Severity
High — rate limiter non-functional in serverless, bypassable by header manipulation, and missing entirely from the most abusable endpoints.
Confidence
Confirmed — Map used for storage and X-Forwarded-For trusted unconditionally in current HEAD.
This issue was filed as part of the AI Slop Intelligence Dashboard Awareness Program.
Repo under review: https://github.com/OpenScanAI/GlobeNewsLive
Programme: https://labs.jamessawyer.co.uk/ai-slop-intelligence-dashboards/
Summary
The in-memory rate limiter in
src/lib/rate-limit.tshas two compounding failures that make it non-functional in any real deployment: (1) it uses a module-levelMapthat is reset on every serverless function cold start — meaning Next.js API routes on Vercel, AWS Lambda, or any serverless platform get a fresh empty rate limit store per invocation; (2) it trusts theX-Forwarded-Forheader completely without validation, meaning any client can set an arbitrary IP and bypass per-IP limits with zero friction. Even where the rate limiter is wired up (only/api/signals), it provides no real protection.Evidence
File:
src/lib/rate-limit.ts:Rate limiter only called in one route out of ~30:
Routes with NO rate limiting whatsoever:
POST /api/notify(Telegram broadcast)POST /api/alerts(Telegram send + SSRF)POST /api/telegram(webhook)GET /api/brief(AI brief generation)GET /api/military-aircraftGET /api/infrastructure(nuclear data)Bypass in 1 request:
Why this matters
/api/notifyand/api/alerts) with unlimited requests, causing Telegram API rate limits to be hit for the bot token, potentially suspending the bot.X-Forwarded-Forheader bypass means even the one protected endpoint (/api/signals) can be hammered without limit.Mapis empty at every cold start, so the rate limiter is literally non-functional./api/briefmeans the expensive brief-generation logic can be called continuously.Root cause
The rate limiter was designed for a long-running Node.js server process (PM2), not for serverless. It was applied to only one endpoint and never validated the
X-Forwarded-Forheader. The in-memory state was never replaced with a persistent store.Recommended fix
Mapwith a proper persistent rate limit backend:ioredis(if self-hosted).X-Forwarded-For— only trust it when the request comes from a known proxy IP, or usex-real-ipset by your own nginx config.POST /api/notify,POST /api/alerts, andGET /api/infrastructure.Acceptance criteria
X-Forwarded-Foris not trusted from arbitrary clients./api/signals.Suggested labels
security, bug
Priority
P0
Severity
High — rate limiter non-functional in serverless, bypassable by header manipulation, and missing entirely from the most abusable endpoints.
Confidence
Confirmed —
Mapused for storage andX-Forwarded-Fortrusted unconditionally in current HEAD.