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.
High because a single crafted request with extreme dimensions can OOM the Node process, taking down the entire application for all users.
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.
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.
ID: ai-slop-cost-bombs.unbounded-operations.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:
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' } })
}