# Input validation on all endpoints

- **Pattern:** `ab-002203` (`saas-api-design.api-security.input-validation-all`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002203
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002203)

## Why it matters

A single unvalidated endpoint — one handler that reads `req.body.email` directly into a database query without a validation step — is all an attacker needs to trigger injection (CWE-20, OWASP A03:2021), corrupt data with unexpected types, or crash the application with oversized inputs. OWASP A08:2021 (software and data integrity failures) also applies when schema-less input reaches business logic unguarded. TypeScript types do not enforce runtime shapes: JSON.parse gives you `any`, and type assertions are compile-time fiction.

## Severity rationale

Critical because even a single unvalidated endpoint is an injection and data-corruption vector — the entire API's trust boundary is defined by its weakest input handler.

## Remediation

Add runtime validation via Zod to every route handler that accepts input. Define schemas in `src/lib/validations/` and import them into route files:

```ts
// src/lib/validations/user.ts
export const createUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1).max(100),
})

// app/api/users/route.ts
const result = createUserSchema.safeParse(await req.json())
if (!result.success) {
  return apiError('VALIDATION_ERROR', result.error.message, 400)
}
```

Also validate path parameters — confirm that ID segments are valid UUIDs (`z.string().uuid()`) before using them in database queries. A validation schema is not optional polish; it is your first defense against injection and your contract with every caller.

## Detection

- **ID:** `input-validation-all`
- **Severity:** `critical`
- **What to look for:** Enumerate all POST/PUT/PATCH route handlers. Count the ratio of validated routes to total. Check each for input validation before the data reaches business logic or database queries. Look for: Zod `parse()` or `safeParse()`, Yup `validate()`, Joi `validate()`, class-validator `validate()`, custom validation functions, or TypeScript type assertions used in place of runtime validation. Also check: are query parameters validated (not just body)? Are path parameters validated (e.g., is an ID checked to be a valid UUID before use)? Look specifically for handlers that use `req.body.fieldName` or `params.id` directly without any validation step.
- **Pass criteria:** At least 100% of routes that accept user input (request body, query params, path params) runs that input through a validation library or explicit validation logic before using it. Validation errors return 400 responses with clear error messages.
- **Fail criteria:** Any route handler that uses `req.body`, `req.query`, or `req.params` directly in database queries or business logic without validation. Even a single unvalidated endpoint constitutes a failure. Must not pass when validation exists on most routes but even 1 endpoint accepts raw input.
- **Skip (N/A) when:** The project has no routes that accept any user input (read-only GET endpoints only with no parameters). Signal: all routes are GET with no query parameters and no path parameters.
- **Detail on fail:** Name the specific unvalidated routes (e.g., "POST /api/users uses req.body.email directly in DB query without Zod or other validation; PATCH /api/posts/:id uses req.params.id without UUID validation"). Max 500 chars.
- **Remediation:** Add runtime validation in `app/api/` route files to every endpoint that accepts input. The recommended approach for TypeScript projects is Zod: define a schema for each request shape, then call `schema.safeParse(req.body)` and return a 400 if parsing fails. Keep schemas in a dedicated `lib/validations/` or `schemas/` directory and import them into route handlers. Also validate path parameters — check that IDs are valid UUIDs before querying the database. Input validation is your first defense against injection attacks, data corruption, and application errors from unexpected input.
- **Cross-reference:** For a deeper look at injection and injection-adjacent vulnerabilities, the SaaS Security Hardening Audit covers SQL injection, XSS, and command injection in detail.

## External references

- cwe CWE-20
- owasp A03
- owasp A08

Taxons: injection-and-input-trust

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