Use when accepting user input. Use when handling request data. Use when trusting external data without validation.
Never trust input. Validate everything at system boundaries.
All external data is potentially malicious or malformed. Validate at the point of entry, fail fast on invalid input.
NEVER use external input without explicit validation.
No exceptions:
If you're using input directly, STOP:
// ❌ VIOLATION: Trusting input
app.post('/users', async (req, res) => {
const { email, age } = req.body; // Direct destructure - no validation
await db.users.create({ email, age });
res.json({ success: true });
});
Problems:
email could be empty, null, or not a stringage could be negative, a string, or missing// ✅ CORRECT: Validate with schema
import { z } from 'zod';
const createUserSchema = z.object({
email: z.string().email('Invalid email format'),
age: z.number().int().min(13).max(120),
name: z.string().min(1).max(100),
});
app.post('/users', async (req, res) => {
// Validate
const result = createUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: 'Validation failed',
details: result.error.flatten().fieldErrors
});
}
// Now data is typed and validated
const { email, age, name } = result.data;
await db.users.create({ email, age, name });
res.status(201).json({ success: true });
});
// Zod (recommended for TypeScript)
const schema = z.object({
id: z.string().uuid(),
email: z.string().email(),
age: z.number().min(0).max(150),
role: z.enum(['user', 'admin']),
tags: z.array(z.string()).max(10),
});
// Yup
const schema = yup.object({
email: yup.string().email().required(),
age: yup.number().positive().integer(),
});
// class-validator
class CreateUserDto {
@IsEmail()
email: string;
@IsInt()
@Min(13)
age: number;
}
Pressure: "The form already validates this data"
Response: Frontend validation is for UX. Backend validation is for security. Attackers bypass frontends.
Action: Validate on backend regardless of frontend validation.
Pressure: "Only our services call this endpoint"
Response: Internal services have bugs too. Defense in depth requires validation everywhere.
Action: Validate internal API inputs too.
Pressure: "This data comes from our partner's API"
Response: Their API can have bugs, be compromised, or change format. Trust no one.
Action: Validate external API responses before using.
Pressure: "It's just a string, what could go wrong?"
Response: Strings can be empty, too long, contain scripts, SQL, or null bytes.
Action: Define what "valid" means and enforce it.
const { x } = req.body without validationany type for inputAll of these mean: Add validation.
| Input Type | Validate | |------------|----------| | Email | Format, length, required | | Password | Length, complexity | | ID | Format (uuid/int), exists | | Number | Type, range, integer? | | String | Length, pattern, sanitize | | Array | Length, item validation | | Object | Schema validation |
| Excuse | Reality | |--------|---------| | "Frontend validates" | Attackers bypass frontends. | | "Internal API" | Internal bugs exist too. | | "We trust the source" | Sources can be compromised. | | "Simple field" | Simple fields cause complex bugs. | | "Validation is slow" | Validation is faster than breaches. | | "TypeScript types are enough" | Types disappear at runtime. |
Validate all input. Trust nothing. Fail fast on invalid data.
Every external boundary should have explicit validation. Use schema validation libraries. Return clear error messages for invalid input. Never let unvalidated data into your system.
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer