Without batch rate limiting, a single IP can submit hundreds of listings per minute, flooding the moderation queue and making it impossible for human reviewers to process legitimate submissions. CWE-770 (Allocation of Resources Without Limits) and OWASP A05 (Security Misconfiguration) both apply. The cost is not just operational — spam that slips through moderation poisons the directory's data quality for every downstream user and search engine that indexes it.
High because uncapped submission rates enable queue flooding that incapacitates moderation, drives up infrastructure costs, and degrades directory data quality at scale.
Implement server-side rate limiting keyed by IP (and user ID when authenticated). Use an atomic increment in Redis so the counter survives across serverless cold starts:
// app/api/listings/submit/route.ts
import { Redis } from '@upstash/redis'
const redis = new Redis({ url: process.env.UPSTASH_REDIS_URL!, token: process.env.UPSTASH_REDIS_TOKEN! })
const ip = req.headers.get('x-forwarded-for') ?? 'unknown'
const key = `sub_rate:${ip}`
const count = await redis.incr(key)
if (count === 1) await redis.expire(key, 60) // 60-second window
if (count > 5) {
await logSecurityEvent({ type: 'submission-rate-exceeded', ip, count })
return Response.json({ error: 'Too many submissions — try again in a minute.' }, { status: 429 })
}
Send an admin alert (email or Slack webhook) when a single IP exceeds the threshold so patterns can be investigated and IPs permanently blocked if warranted.
ID: directory-submissions-moderation.spam-prevention.rate-limiting-batch
Severity: high
What to look for: Examine the submission API logic. Check for rate limiting that tracks submissions per IP or user. Look for logic that: (1) counts submissions in a time window (e.g., 60 seconds), (2) blocks further submissions if a threshold is exceeded (e.g., >5 per 60s), (3) logs or alerts admins about the abuse.
Pass criteria: Enumerate all relevant code paths. Rate limiting is enforced server-side. More than 5 submissions per 60 seconds from the same IP or user triggers a temporary block (e.g., 10-minute cooldown) and logs the event for admin review. with at least 1 verified instance. On pass, report the count of validated fields vs. total fields.
Fail criteria: No rate limiting, or rate limiting only on client-side, or no alert mechanism.
Skip (N/A) when: The project doesn't allow rapid submissions (e.g., requires manual review for each submission).
Detail on fail: "A user can submit 100 listings in 1 second. No rate limiting. Server gets hammered with spam." or "Rate limit exists but is not enforced — server returns 429 but still processes the request."
Remediation: Implement server-side rate limiting:
// Using a library like express-rate-limit or custom logic
import { Redis } from '@upstash/redis'
const redis = new Redis({
url: process.env.REDIS_URL,
token: process.env.REDIS_TOKEN
})
const RATE_LIMIT_WINDOW = 60 // seconds
const RATE_LIMIT_MAX = 5 // max submissions
export async function POST(req: Request) {
const clientIp = req.headers.get('x-forwarded-for') || 'unknown'
const key = `submission:${clientIp}`
const count = await redis.incr(key)
if (count === 1) {
await redis.expire(key, RATE_LIMIT_WINDOW)
}
if (count > RATE_LIMIT_MAX) {
// Alert admin
await logSecurityEvent({
type: 'rate-limit-exceeded',
ip: clientIp,
count,
timestamp: new Date()
})
return Response.json(
{ error: 'Too many submissions. Please try again later.' },
{ status: 429 }
)
}
// Proceed with submission
}