Input validation on all endpoints
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:
// 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()orsafeParse(), Yupvalidate(), Joivalidate(), class-validatorvalidate(), 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 usereq.body.fieldNameorparams.iddirectly 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, orreq.paramsdirectly 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 callschema.safeParse(req.body)and return a 400 if parsing fails. Keep schemas in a dedicatedlib/validations/orschemas/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 — Improper Input Validation
- owasp:2021 · A03 — Injection
- owasp:2021 · A08 — Software and Data Integrity Failures
Taxons
History
- 2026-04-18·v1.0.0·Initial import from saas-api-design·automated