Email send routes have per-recipient rate limiting
Why it matters
Email providers charge per send — Resend, SendGrid, and SES all bill on volume, and a single API route that accepts a recipients array with no length cap is a direct financial attack surface. An attacker who finds /api/notify can POST {recipients: Array(10000).fill('victim@example.com')} and run up thousands of dollars in charges within minutes, while simultaneously triggering your domain's abuse threshold and risking deliverability bans. CWE-770 describes exactly this pattern. OWASP A05:2021 covers the missing rate control. The reputational damage from being a spam source can far exceed the direct billing cost.
Severity rationale
High because metered email costs are directly proportional to unvalidated recipient list size, making financial abuse trivial and instantly expensive.
Remediation
Cap the recipient list with a Zod .max() and add per-IP rate limiting with @upstash/ratelimit. Both controls are necessary: schema validation caps burst size, rate limiting prevents repeated small sends.
import { z } from 'zod'
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'
import pMap from 'p-map'
const limiter = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.fixedWindow(10, '1 h'),
})
const NotifySchema = z.object({
recipients: z.array(z.string().email()).max(100),
})
export async function POST(req: Request) {
const ip = req.headers.get('x-forwarded-for') ?? 'anon'
const { success } = await limiter.limit(ip)
if (!success) return Response.json({ error: 'Rate limited' }, { status: 429 })
const { recipients } = NotifySchema.parse(await req.json())
await pMap(recipients, (email) => resend.emails.send({ to: email, ... }), { concurrency: 5 })
return Response.json({ sent: recipients.length })
}
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Hidden Cost Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe · CWE-770 — Allocation of Resources Without Limits or Throttling
- cwe · CWE-799 — Improper Control of Interaction Frequency
- owasp:2021 · A05
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ai-slop-cost-bombs·automated