# Input Validation — Derived Rules > Derives from `domains/security/first-principles.md` P4 (Input Validation), P5 (Output Safety). ## The Rule All input is untrusted until validated. Validation happens at the boundary, against a schema, with explicit failure modes. ## Validate at the Boundary (P4 Locality) - The API endpoint, the controller, the message handler — the entry point validates. - Internal code trusts validated input. Unvalidated input never reaches the database. - Defense in depth: the database also has constraints (P3 Defense in Depth). ## Schema Validation - Use a schema library (zod, joi, pydantic, json-schema). Never hand-write validation. - The schema is the contract. The schema is versioned. The schema is tested. - Reject unknown fields (`additionalProperties: false` by default). Be explicit. ## Validation Types ### Type Validation - `id` is a UUID, not a string. `age` is an integer ≥ 0. `email` matches a regex (or better, is parsed). - Never accept `any`. Never accept `string` for a typed value. ### Range Validation - `limit` ≤ 100. `page` ≥ 1. `quantity` ≥ 1 and ≤ stock. - Bounds are explicit. No "unbounded" inputs. ### Format Validation - `email` is parsed (not just regex). `url` is parsed. `date` is parsed. - A regex for email is wrong (RFC 5322 is not a regular language). Use a parser. ### Semantic Validation - `start_date < end_date`. `user_id` exists. `product_id` is in stock. - Semantic validation may require a database lookup. That's fine. ### Presence Validation - Required fields are present. Optional fields are absent or null. - Empty string `""` is not the same as null. Be explicit about which you accept. ## Failure Modes (P8 Fail Securely) - Validation failure → 400 Bad Request with a structured error (`domains/api/error-responses.md`). - Never coerce: `"5" + 3` is not validation. Reject, don't guess. - Never default: a missing required field is an error, not a default value. ## Output Safety (P5 Output Safety) - Validation is for input. Encoding is for output. - Output to HTML: HTML-encode. Output to SQL: parameterize. Output to URL: URL-encode. - Never trust validated input for output. Validate on the way in, encode on the way out. ## What Violates Input Validation | Violation | Principle | |-----------|-----------| | `JSON.parse(req.body)` with no schema | P4 Input Validation | | `parseInt(req.query.id)` with no range check | P4 | | `additionalProperties: true` by default | P1 Contract Fidelity | | Coercing `"5"` to `5` silently | P8 Fail Securely | | SQL string interpolation (even of "validated" input) | P5 Output Safety | | Regex for email validation | P4 (use a parser) |