Files
agent-skills/skills/nestjs-best-practices/rules/security-validate-all-input.md
Jason Woltje 861b28b965 feat: Expand fleet to 23 skills across all domains
New skills (14):
- nestjs-best-practices: 40 priority-ranked rules (kadajett)
- fastapi: Pydantic v2, async SQLAlchemy, JWT auth (jezweb)
- architecture-patterns: Clean Architecture, Hexagonal, DDD (wshobson)
- python-performance-optimization: Profiling and optimization (wshobson)
- ai-sdk: Vercel AI SDK streaming and agent patterns (vercel)
- create-agent: Modular agent architecture with OpenRouter (openrouterteam)
- proactive-agent: WAL Protocol, compaction recovery, self-improvement (halthelobster)
- brand-guidelines: Brand identity enforcement (anthropics)
- ui-animation: Motion design with accessibility (mblode)
- marketing-ideas: 139 ideas across 14 categories (coreyhaines31)
- pricing-strategy: SaaS pricing and tier design (coreyhaines31)
- programmatic-seo: SEO at scale with playbooks (coreyhaines31)
- competitor-alternatives: Comparison page architecture (coreyhaines31)
- referral-program: Referral and affiliate programs (coreyhaines31)

README reorganized by domain: Code Quality, Frontend, Backend,
Auth, AI/Agent Building, Marketing, Design, Meta.

Mosaic Stack is not limited to coding — the Orchestrator serves
coding, business, design, marketing, writing, logistics, and analysis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:22:53 -06:00

151 lines
3.3 KiB
Markdown

---
title: Validate All Input with DTOs and Pipes
impact: HIGH
impactDescription: First line of defense against attacks
tags: security, validation, dto, pipes
---
## Validate All Input with DTOs and Pipes
Always validate incoming data using class-validator decorators on DTOs and the global ValidationPipe. Never trust user input. Validate all request bodies, query parameters, and route parameters before processing.
**Incorrect (trust raw input without validation):**
```typescript
// Trust raw input without validation
@Controller('users')
export class UsersController {
@Post()
create(@Body() body: any) {
// body could contain anything - SQL injection, XSS, etc.
return this.usersService.create(body);
}
@Get()
findAll(@Query() query: any) {
// query.limit could be "'; DROP TABLE users; --"
return this.usersService.findAll(query.limit);
}
}
// DTOs without validation decorators
export class CreateUserDto {
name: string; // No validation
email: string; // Could be "not-an-email"
age: number; // Could be "abc" or -999
}
```
**Correct (validated DTOs with global ValidationPipe):**
```typescript
// Enable ValidationPipe globally in main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // Strip unknown properties
forbidNonWhitelisted: true, // Throw on unknown properties
transform: true, // Auto-transform to DTO types
transformOptions: {
enableImplicitConversion: true,
},
}),
);
await app.listen(3000);
}
// Create well-validated DTOs
import {
IsString,
IsEmail,
IsInt,
Min,
Max,
IsOptional,
MinLength,
MaxLength,
Matches,
IsNotEmpty,
} from 'class-validator';
import { Transform, Type } from 'class-transformer';
export class CreateUserDto {
@IsString()
@IsNotEmpty()
@MinLength(2)
@MaxLength(100)
@Transform(({ value }) => value?.trim())
name: string;
@IsEmail()
@Transform(({ value }) => value?.toLowerCase().trim())
email: string;
@IsInt()
@Min(0)
@Max(150)
age: number;
@IsString()
@MinLength(8)
@MaxLength(100)
@Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, {
message: 'Password must contain uppercase, lowercase, and number',
})
password: string;
}
// Query DTO with defaults and transformation
export class FindUsersQueryDto {
@IsOptional()
@IsString()
@MaxLength(100)
search?: string;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
@Max(100)
limit: number = 20;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(0)
offset: number = 0;
}
// Param validation
export class UserIdParamDto {
@IsUUID('4')
id: string;
}
@Controller('users')
export class UsersController {
@Post()
create(@Body() dto: CreateUserDto): Promise<User> {
// dto is guaranteed to be valid
return this.usersService.create(dto);
}
@Get()
findAll(@Query() query: FindUsersQueryDto): Promise<User[]> {
// query.limit is a number, query.search is sanitized
return this.usersService.findAll(query);
}
@Get(':id')
findOne(@Param() params: UserIdParamDto): Promise<User> {
// params.id is a valid UUID
return this.usersService.findById(params.id);
}
}
```
Reference: [NestJS Validation](https://docs.nestjs.com/techniques/validation)