# File upload handlers enforce a maximum file size

- **Pattern:** `ab-000238` (`ai-slop-cost-bombs.unbounded-operations.file-upload-size-limit`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000238
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000238)

## Why it matters

A 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.

## Severity rationale

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.

## Remediation

Set `maxFileSize` at library initialization, not as a post-read check. Validating size after reading defeats the purpose — the damage is done once the bytes hit memory.

```ts
import formidable from 'formidable'

export async function POST(req: Request) {
  const form = formidable({
    maxFileSize: 10 * 1024 * 1024,  // 10 MB hard cap
    maxFiles: 1,
  })
  // formidable will throw a 413 automatically if the limit is exceeded
  const [, files] = await form.parse(req as any)
  // ...
}
```

For Next.js Route Handlers using `request.formData()`, check `file.size` immediately: `if (file.size > 10 * 1024 * 1024) return Response.json({ error: 'File too large' }, { status: 413 })`.

## Detection

- **ID:** `file-upload-size-limit`
- **Severity:** `critical`
- **What to look for:** Walk all API handlers and count all handlers that process file uploads. A file upload handler is one that uses `formidable`, `multer`, `busboy`, `@vercel/blob/client`, `@aws-sdk/lib-storage`, OR calls `request.formData()` / `req.body` and reads file content. For each file upload handler, before evaluating, extract and quote the relevant configuration line — quoting the exact configuration is required for accurate evaluation. Verify the handler includes one of these size-limit configurations: `maxFileSize`, `limits: { fileSize: ... }`, `maxBodyLength`, `bodyParser.json({ limit: ... })`, `multer({ limits: { fileSize: ... } })`, OR a manual size check on the file before processing. EXCLUDE size limits set at the platform level (Vercel max body 4.5MB, Cloudflare max upload 100MB) — those are out of scope for this check.
- **Pass criteria:** 100% of file upload handlers configure a max file size at the application level via `maxFileSize`, `limits.fileSize`, `bodyParser.json({ limit })`, OR an explicit manual size check before processing the file content.
- **Report even on pass:** Always report the count and the smallest size limit found. Example: "3 file upload handlers inspected, all 3 enforce max size (smallest: 5 MB)."
- **Fail criteria:** At least 1 file upload handler has no application-level size limit.
- **Do NOT pass when:** Size limit is set to 1 GB or higher — that's effectively no limit.
- **Skip (N/A) when:** Project has 0 API handlers that process file uploads (no upload-library imports AND no `formData()`/`req.body` file handling found).
- **Detail on fail:** `"1 unbounded file upload: app/api/upload/route.ts uses formidable() with no limits — accepts files of any size, will fill disk and run up egress costs"`
- **Remediation:** A file upload endpoint with no size limit lets one user fill your entire disk with a single request. Always set a max file size:

  ```ts
  // Bad: no limit
  import formidable from 'formidable'
  const form = formidable()  // any size accepted

  // Good: 10 MB cap
  import formidable from 'formidable'
  const form = formidable({
    maxFileSize: 10 * 1024 * 1024,  // 10 MB
    maxFiles: 1,
  })
  ```

  For Next.js Route Handlers using `request.formData()`, validate `file.size` manually before reading: `if (file.size > 10 * 1024 * 1024) return Response.json({ error: 'Too large' }, { status: 413 })`.

## External references

- cwe CWE-400
- cwe CWE-770
- owasp A05

Taxons: cost-efficiency, injection-and-input-trust

HTML version: https://auditbuffet.com/patterns/ab-000238
