All 18 checks with why-it-matters prose, severity, and cross-references to related audits.
Unbounded database list queries are a textbook CWE-770 (Allocation of Resources Without Limits) failure: a single unauthenticated request can pull every row in your users or orders table into memory, saturating your database connection pool and spiking both compute and egress costs. On a Hacker News moment, this turns from a slow endpoint into a complete service outage. Any SaaS operating under GDPR or SOC 2 also faces audit risk: returning unbounded customer records exposes more PII than the request requires. At $0.09/GB of Postgres egress, a table with 500K rows of user profiles can cost several dollars per cache miss — and there is no cache for a query with no LIMIT.
Why this severity: Critical because a single viral traffic spike can exhaust the database connection pool and incur unbounded egress charges with no circuit breaker.
ai-slop-cost-bombs.unbounded-operations.db-list-queries-have-limitSee full patternA file upload handler without a size limit is a direct path to disk exhaustion and runaway cloud storage costs — a single malicious actor can POST a 50 GB file to your server, filling disk and triggering overage charges before any alert fires. This maps to CWE-770 and OWASP A05:2021 (Security Misconfiguration): the absence of a limit is a misconfiguration at the application layer, independent of what a CDN or load balancer might reject. Platforms like Vercel enforce a 4.5 MB body limit by default, but self-hosted or Docker deployments carry no such protection. A successful attack disrupts all users on the same host, not just the attacker.
Why this severity: Critical because a single oversized upload can fill disk, crash the process, and block all other users on the same host with no application-level defense.
ai-slop-cost-bombs.unbounded-operations.file-upload-size-limitSee full patternImage processing libraries like Sharp allocate memory proportional to output pixel area — a request for a 999,999 × 999,999 image allocates roughly 3 TB of virtual memory, which OOMs the Node process instantly. This is CWE-770 and CWE-400 in the same request: resource exhaustion via unconstrained user input. Any endpoint that accepts width and height from a query string or POST body without validation is a denial-of-service vector with zero authentication required. OWASP A05:2021 (Security Misconfiguration) covers this class of missing input constraint. Even modest abuse — thousands of 4000×4000 requests — can exhaust CPU and inflate cloud egress for legitimate users.
Why this severity: High because a single crafted request with extreme dimensions can OOM the Node process, taking down the entire application for all users.
ai-slop-cost-bombs.unbounded-operations.image-processing-dimension-limitSee full patternPDF generation is CPU-bound: a renderer like Puppeteer or `@react-pdf/renderer` that loops through a user-supplied `items` array with no length cap is a free compute gift to any attacker. A POST with `items: Array(100000).fill({})` can saturate a server core for minutes, blocking all other requests on that worker. CWE-400 (Uncontrolled Resource Consumption) applies directly: the cost of the operation scales linearly with user input. This is especially acute on serverless platforms where function duration directly maps to billing — one malformed invoice request can burn through your monthly Vercel function budget.
Why this severity: Medium because the attack requires a POST body and scales with input length, making it a targeted DoS rather than an accidental one-request outage.
ai-slop-cost-bombs.unbounded-operations.pdf-page-limitSee full patternSequential `await` calls inside a loop over user-supplied data are simultaneously a latency bomb and a cost bomb: 100 items × 200ms each = 20 seconds of wall time, and each iteration holds an HTTP connection open. When the input array is user-controlled with no length cap — for example `req.body.urls.forEach(url => fetch(url))` — the number of outbound requests is directly attacker-controlled. CWE-770 applies: an unbounded array fed into a loop of external calls gives an attacker leverage over your third-party API rate limits, egress costs, and server-side connection count. Even internal N+1 patterns inflate database query counts and slow down all concurrent users.
Why this severity: High because user-controlled array length maps directly to unbounded external API calls, enabling cost abuse and self-inflicted rate-limit bans with no cap.
ai-slop-cost-bombs.unbounded-operations.n-plus-one-loop-warningSee full patternEmail 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.
Why this severity: High because metered email costs are directly proportional to unvalidated recipient list size, making financial abuse trivial and instantly expensive.
ai-slop-cost-bombs.unrate-limited-spend.email-rate-limited-per-recipientSee full patternSMS is the most expensive metered communication channel in a typical web stack — Twilio charges $0.0079–$0.02 per outbound message, and there is no free tier for abuse. An SMS route without per-IP rate limiting lets an attacker trigger thousands of messages per minute, reaching four-digit charges before any alert fires. CWE-799 (Improper Control of Interaction Frequency) is the direct mapping, alongside OWASP A05:2021. Beyond direct billing, carriers flag high-volume spammy senders for grey-listing or number suspension, destroying your transactional SMS deliverability for legitimate users. This is a cost bomb that also breaks a security-critical channel (OTP delivery).
Why this severity: High because SMS costs are metered per message with no platform-level cap, making a missing rate limit an immediately exploitable financial attack vector.
ai-slop-cost-bombs.unrate-limited-spend.sms-rate-limitedSee full patternSequential `await fetch(` inside a loop over a non-trivial collection is one of the most common AI-generated code patterns that degrades gracefully in testing and catastrophically in production. Each awaited call holds a thread-of-execution and an HTTP connection open until it resolves; a list of 200 items becomes 200 serialized round-trips, inflating p99 latency from 200ms to 40 seconds. The pattern also prevents proper error isolation — a single slow upstream response blocks everything behind it. On metered hosting (Vercel function duration, Lambda invocation time), sequential loops inflate compute costs linearly with list size.
Why this severity: High because sequential awaits in loops inflate response latency and compute billing linearly with input size, with no platform-level protection against runaway iteration.
ai-slop-cost-bombs.unrate-limited-spend.external-api-loops-batchedSee full patternA `fetch` call with no timeout is a connection that can remain open indefinitely if the upstream server is slow, overloaded, or adversarially slow. On serverless platforms, an open connection holds the function invocation alive until the platform's hard execution limit — 10 seconds on Vercel Hobby, 60 seconds on Pro — burning compute the entire time. CWE-400 (Uncontrolled Resource Consumption) applies: the user who triggered the request pays for exactly one slow upstream response, but a platform attack that sends many such requests exhausts the concurrency pool and causes 503s for all users. Explicit timeouts also force proper error handling — without them, timeout errors surface as generic network failures with no useful signal.
Why this severity: Low because a missing timeout primarily affects availability under adversarial upstream conditions, rather than enabling immediate financial or data-loss impact.
ai-slop-cost-bombs.unrate-limited-spend.external-api-call-has-timeoutSee full patternA cron job without an execution time limit can run for the full platform timeout — 5 minutes on Vercel Hobby, 15 minutes on Pro — and if it hangs due to an upstream dependency or a slow database query, it will consume compute on every scheduled tick without making progress. CWE-770 applies: resources are consumed without any application-level limit. For hourly crons, this means up to 24 hung invocations per day, each burning the full platform budget. On multi-tenant serverless infrastructure, a stuck cron competes for concurrency with user-facing requests. A `maxDuration` export is the zero-cost fix — it adds a hard ceiling with no code change to the business logic.
Why this severity: Medium because a hung cron affects compute budget continuously over time rather than in a single request, making it a slow drain rather than an immediate spike.
ai-slop-cost-bombs.job-hygiene.cron-jobs-have-time-limitSee full patternBackground jobs without retry caps are poison-message traps: any job that fails consistently due to a logic bug, bad data, or downstream API change will be retried indefinitely by BullMQ (default: unlimited), Agenda, and similar libraries. CWE-770 describes the outcome — unbounded retries consume Redis storage (the job stays in the failed set), worker CPU (each retry fires job processing logic), and can trigger cascading failures if the job calls external APIs. The retry storm also obscures monitoring: a dead-letter queue that fills silently hides the bug that caused the original failure, delaying the production fix.
Why this severity: High because infinite retries from a single poison message consume continuous Redis and worker resources, masking the underlying bug indefinitely rather than surfacing it for repair.
ai-slop-cost-bombs.job-hygiene.background-jobs-have-retry-capSee full patternWebhook providers (Stripe, Clerk, GitHub, Svix) retry delivery on any 5xx response — which is the correct behavior for eventual consistency. But a webhook handler without idempotency converts that reliability guarantee into a defect multiplier: a transient 500 during `payment.succeeded` processing means the payment is recorded twice, the fulfillment email sends twice, and the inventory decrements twice. For Stripe specifically, this can cause double charges in your records, triggering refund reconciliation issues that require manual intervention. Idempotency is not an optional hardening step — it is the contract that makes retries safe.
Why this severity: Critical because non-idempotent webhook handlers turn provider retry logic into a data corruption vector, causing duplicate charges, duplicate records, and fulfillment errors on every transient 5xx.
ai-slop-cost-bombs.job-hygiene.webhook-handlers-idempotentSee full patternRecursive functions without base cases or depth limits crash on cyclic data structures and blow the call stack on deeply nested inputs. CWE-674 (Uncontrolled Recursion) and CWE-835 (Loop with Unreachable Exit Condition) both apply. In a user-facing API, an attacker who can control a tree or graph structure — JSON, AST, category hierarchy, file system path — can trigger a stack overflow that crashes the Node process for all users. Even without adversarial input, an AI-generated recursive function that processes database-fetched hierarchical data will eventually encounter a self-referencing node in production and take down the server.
Why this severity: Medium because recursion without a guard requires either user-controlled input or corrupt data to trigger, but the crash affects all users on the same process when it fires.
ai-slop-cost-bombs.job-hygiene.infinite-loop-guards-presentSee full patternBackground job queues without a dead-letter path are silent failure sinks: a job that consistently fails disappears from the active queue after its retry cap is exhausted, leaving no record of what went wrong unless you explicitly configure failure handling. For payment-adjacent jobs — subscription renewals, fulfillment triggers, invoice generation — silent failure means lost revenue and customer confusion. Even for non-financial jobs, the absence of a failure path makes debugging a production incident nearly impossible: you know something is wrong because users report it, but the queue gives you no evidence. The observability gap is the core risk, not the job failure itself.
Why this severity: Low because a missing dead-letter path does not cause immediate harm but compounds debugging difficulty and hides systematic failures until user-reported impact escalates.
ai-slop-cost-bombs.job-hygiene.queue-deadletter-configuredSee full patternLLM API calls are the most expensive per-request operations in a modern web stack — GPT-4o costs $2.50/M input tokens and $10/M output tokens, and a single summarization endpoint with no caching pays that cost on every request, even for identical inputs. OWASP LLM10 (Unbounded Consumption) specifically identifies missing caching as a cost-amplification risk for LLM-backed applications. At modest scale, 1,000 daily requests to an uncached summarization endpoint can cost $50–$200/day in token charges. The fix is additive — `unstable_cache` wraps any async function — and reduces cost to near-zero for duplicate inputs without changing the API contract.
Why this severity: Medium because the financial impact scales with traffic rather than being immediately exploitable, but a viral moment on an uncached LLM endpoint can produce four-figure API bills overnight.
ai-slop-cost-bombs.cache-idempotency.expensive-ops-cached-or-flaggedSee full patternAn unverified webhook endpoint is an open forged-event injection point: anyone who discovers the URL can POST a `payment_intent.succeeded` event with a fabricated payment amount and trigger fulfillment, subscription upgrades, or user privilege escalation without paying a cent. CWE-345 (Insufficient Verification of Data Authenticity) is the direct mapping. OWASP A07:2021 (Identification and Authentication Failures) applies: the server cannot distinguish a real Stripe event from a crafted one without signature verification. This is not a theoretical risk — Stripe explicitly documents the attack in their webhook security guide and provides `constructEvent` as the countermeasure.
Why this severity: High because a forged webhook event can trigger financial side effects — fulfillment, subscription activation, refunds — with no authentication required beyond knowing the endpoint URL.
ai-slop-cost-bombs.cache-idempotency.webhook-signature-verifiedSee full patternSDK clients like `new Stripe()`, `new OpenAI()`, and `new PrismaClient()` initialize internal HTTP connection pools, TLS handshake state, and retry logic at construction time. Constructing them inside a request handler means that work — including a TCP connection establishment — happens on every request, adding 20–100ms of latency and preventing connection reuse. For Prisma specifically, constructing a new `PrismaClient` per request is the documented anti-pattern that causes connection pool exhaustion (each instance opens its own pool of connections, and serverless invocations can open hundreds of pools simultaneously, hitting the database's `max_connections` limit).
Why this severity: Low because per-request SDK construction primarily causes latency degradation and connection pool waste rather than immediate data loss or financial exposure.
ai-slop-cost-bombs.cache-idempotency.third-party-sdk-singletonSee full patternGET routes that return the same data on every request — product listings, published posts, public user profiles — without any caching strategy convert every user visit into a full database round-trip. At modest scale this is waste; at traffic spikes it is the mechanism by which a popular moment becomes a database overload event. Even a 60-second `stale-while-revalidate` cache cuts database load by roughly 100x for a route that serves one request per second, while keeping data within a minute of fresh. OWASP LLM10 (Unbounded Consumption) identifies uncached expensive operations as a key cost amplification vector — the same logic applies to database-backed GET routes that serve identical responses to many users.
Why this severity: Info because missing caching is an efficiency gap rather than a security or correctness defect — the application functions correctly, it just costs more to operate than necessary.
ai-slop-cost-bombs.cache-idempotency.stale-while-revalidate-consideredSee full patternRun this audit in your AI coding tool (Claude Code, Cursor, Bolt, etc.) and submit results here for scoring and benchmarks.
Open Hidden Cost Audit