# Image processing has output dimension limits

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

## Why it matters

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

## Severity rationale

High because a single crafted request with extreme dimensions can OOM the Node process, taking down the entire application for all users.

## Remediation

Validate width and height against a Zod schema with a hard `.max()` before any Sharp call. Reject the request before the buffer is even allocated.

```ts
import { z } from 'zod'
import sharp from 'sharp'

const ResizeSchema = z.object({
  width: z.number().int().min(1).max(2000),
  height: z.number().int().min(1).max(2000),
})

export async function POST(req: Request) {
  const { width, height } = ResizeSchema.parse(await req.json())
  const buffer = await req.arrayBuffer()
  const output = await sharp(Buffer.from(buffer))
    .resize(width, height)
    .webp({ quality: 80 })
    .toBuffer()
  return new Response(output, { headers: { 'Content-Type': 'image/webp' } })
}
```

Choose a sensible max for your product — 2000px is generous for thumbnails, 4000px for user-uploaded cover images.

## Detection

- **ID:** `image-processing-dimension-limit`
- **Severity:** `high`
- **What to look for:** When an image processing library is in `package.json` dependencies (`sharp`, `jimp`, `image-size`, `@squoosh/lib`, `gm`, `imagemagick`), count all image processing call sites: `.resize(`, `.extract(`, `.composite(`, `.png(`, `.jpeg(`, `.webp(`, `.toFormat(`. For each call site, before evaluating, extract and quote the size argument or the surrounding validation. Verify the operation occurs inside a function whose width/height parameters are validated against a maximum (look for `Math.min(width, MAX_WIDTH)`, `if (width > X)`, `z.number().max(X)`, schema-based validation, or a hardcoded literal width/height).
- **Pass criteria:** 100% of image processing call sites have dimension validation OR use a hardcoded constant size. Report: "X image processing call sites inspected, Y validated, 0 unvalidated."
- **Fail criteria:** At least 1 image processing call uses width/height from user input without validation.
- **Skip (N/A) when:** No image processing library in dependencies.
- **Detail on fail:** `"1 unvalidated image resize: sharp(buffer).resize(req.body.width, req.body.height) in app/api/thumbnail/route.ts — user can request 999999x999999 image, exhausting memory"`
- **Remediation:** Sharp can allocate gigabytes of memory for a single oversized image — one user with `width=999999&height=999999` can OOM your server. Validate dimensions:

  ```ts
  import { z } from 'zod'
  import sharp from 'sharp'

  const ResizeSchema = z.object({
    width: z.number().int().min(1).max(2000),
    height: z.number().int().min(1).max(2000),
  })

  export async function POST(req: Request) {
    const { width, height } = ResizeSchema.parse(await req.json())
    const buffer = await req.arrayBuffer()
    const result = await sharp(Buffer.from(buffer)).resize(width, height).png().toBuffer()
    return new Response(result, { headers: { 'Content-Type': 'image/png' } })
  }
  ```

## External references

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

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

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