From claude-initial-setup
Validate and sanitize all user input using schema validation libraries. Activate whenever the user writes form handlers, API endpoints, request parsers, data processing functions, or any code that accepts external input. Also activate when the user asks about validation libraries like Zod, Joi, or Pydantic, or discusses input sanitization.
npx claudepluginhub versoxbt/claude-initial-setup --plugin claude-initial-setupThis skill uses the workspace's default tool permissions.
Validate all external input at system boundaries using schema validation libraries.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Validate all external input at system boundaries using schema validation libraries. Reject invalid data early with clear error messages. Never trust client-side validation as the sole defense.
The standard for TypeScript schema validation. Parse, don't validate.
import { z } from 'zod';
// Define schemas as the source of truth
const CreateUserSchema = z.object({
email: z.string().email('Invalid email format'),
name: z.string().min(1, 'Name required').max(100),
age: z.number().int().min(13, 'Must be 13 or older').max(150),
role: z.enum(['user', 'admin', 'moderator']).default('user'),
website: z.string().url().optional(),
});
// Infer TypeScript types from schemas
type CreateUserInput = z.infer<typeof CreateUserSchema>;
// Parse at the boundary, use typed data internally
app.post('/api/users', async (req, res) => {
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: 'Validation failed',
issues: result.error.issues.map(i => ({
field: i.path.join('.'),
message: i.message,
})),
});
}
// result.data is fully typed as CreateUserInput
const user = await createUser(result.data);
res.json(user);
});
Advanced Zod patterns for complex validation:
// Transform and refine
const SearchParamsSchema = z.object({
page: z.coerce.number().int().min(1).default(1),
limit: z.coerce.number().int().min(1).max(100).default(20),
sort: z.enum(['name', 'date', 'relevance']).default('relevance'),
q: z.string().trim().min(1).max(200).optional(),
});
// Discriminated unions for type-safe variants
const PaymentSchema = z.discriminatedUnion('method', [
z.object({
method: z.literal('card'),
cardNumber: z.string().regex(/^\d{16}$/),
expiry: z.string().regex(/^\d{2}\/\d{2}$/),
cvv: z.string().regex(/^\d{3,4}$/),
}),
z.object({
method: z.literal('bank_transfer'),
accountNumber: z.string().min(8).max(20),
routingNumber: z.string().length(9),
}),
]);
// Custom refinements with cross-field validation
const DateRangeSchema = z.object({
startDate: z.coerce.date(),
endDate: z.coerce.date(),
}).refine(
data => data.endDate > data.startDate,
{ message: 'End date must be after start date', path: ['endDate'] }
);
The standard for Python data validation using type annotations.
from pydantic import BaseModel, Field, field_validator, EmailStr
from datetime import date
from enum import Enum
class UserRole(str, Enum):
USER = "user"
ADMIN = "admin"
MODERATOR = "moderator"
class CreateUserRequest(BaseModel):
email: EmailStr
name: str = Field(min_length=1, max_length=100)
age: int = Field(ge=13, le=150)
role: UserRole = UserRole.USER
website: str | None = None
@field_validator("name")
@classmethod
def name_must_not_be_empty(cls, v: str) -> str:
stripped = v.strip()
if not stripped:
raise ValueError("Name cannot be blank")
return stripped
# FastAPI integration - validation is automatic
@app.post("/api/users")
async def create_user(data: CreateUserRequest):
# data is already validated and typed
return await save_user(data)
Always prefer allowlists. Denylists are inherently incomplete.
// WRONG: Denylist approach - always has gaps
function sanitizeFilename(name: string): string {
return name.replace(/[<>:"/\\|?*]/g, ''); // What about null bytes? Unicode tricks?
}
// CORRECT: Allowlist approach - only permit known-good characters
function sanitizeFilename(name: string): string {
const sanitized = name.replace(/[^a-zA-Z0-9._-]/g, '');
if (!sanitized || sanitized.startsWith('.')) {
throw new Error('Invalid filename');
}
return sanitized;
}
// CORRECT: Allowlist for content types
const ALLOWED_TYPES = new Set(['image/jpeg', 'image/png', 'image/webp', 'application/pdf']);
function validateContentType(type: string): boolean {
return ALLOWED_TYPES.has(type);
}
Prevent attacks that exploit JavaScript's loose type coercion.
// WRONG: Vulnerable to type coercion
app.get('/api/users', (req, res) => {
// req.query.admin could be "true" (string), true (if parsed), or ["true"] (array)
if (req.query.admin == true) { // Loose equality - dangerous
return getAdminData();
}
});
// CORRECT: Strict parsing with Zod
const QuerySchema = z.object({
admin: z.enum(['true', 'false']).transform(v => v === 'true').optional(),
id: z.coerce.number().int().positive(), // Explicit coercion
});
app.get('/api/users', (req, res) => {
const query = QuerySchema.parse(req.query);
// query.admin is boolean | undefined, query.id is number
});
// WRONG: JSON.parse without validation
const config = JSON.parse(userInput); // Could be any type
// CORRECT: Parse then validate
const rawData = JSON.parse(userInput);
const config = ConfigSchema.parse(rawData);
Apply context-appropriate sanitization after validation.
import DOMPurify from 'dompurify';
import sqlstring from 'sqlstring';
// HTML content: strip dangerous tags
function sanitizeHtml(input: string): string {
return DOMPurify.sanitize(input, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'br', 'ul', 'li'],
ALLOWED_ATTR: [],
});
}
// Path traversal prevention
function safePath(basedir: string, userPath: string): string {
const resolved = path.resolve(basedir, userPath);
if (!resolved.startsWith(basedir)) {
throw new Error('Path traversal detected');
}
return resolved;
}
==) instead of strict equality (===) in JavaScriptContent-Type headers without verifying actual content| Language | Library | Key Pattern |
|---|---|---|
| TypeScript | Zod | schema.safeParse(input) returns { success, data, error } |
| TypeScript | Joi | schema.validate(input) returns { value, error } |
| Python | Pydantic | Class-based models with type annotations |
| Go | validator | Struct tags: validate:"required,email" |
| Principle | Rule |
|---|---|
| Validate early | At system boundaries, not deep in business logic |
| Allowlist | Permit known-good, reject everything else |
| Parse, don't validate | Transform raw input into typed domain objects |
| Fail loudly | Return specific field-level error messages |
| Never trust client | Server-side validation is mandatory |