ValidationPipe: Parameter pollution and DTO bypasses
NestJS uses class-validator to check incoming payloads against Data Transfer Objects (DTOs). A common finding is misconfiguring the global ValidationPipe. If you do not enable the whitelist property, the pipe validates the fields defined in the DTO, but allows other fields in the payload to pass through to your controller:
// VULNERABLE global ValidationPipe configuration
app.useGlobalPipes(new ValidationPipe({
transform: true // Binds types, but does not strip unlisted parameters
})); The Exploit: If the controller passes this DTO directly to TypeORM's save method, an attacker can append columns like role: "admin" to bypass business rules. We audit your pipe configuration to ensure it strips unlisted fields:
// SECURE ValidationPipe configuration
app.useGlobalPipes(new ValidationPipe({
transform: true,
whitelist: true, // Strips any parameter not explicitly defined in the DTO
forbidNonWhitelisted: true // Rejects requests containing unlisted parameters
})); Bypassing NestJS Guards
NestJS uses Guards (annotated with @UseGuards()) to verify authentication. A common implementation mistake is placing guards at the class level but forgetting to apply them to custom REST method overrides, or utilizing custom interceptors that mutate the request context. We audit your guard classes to check for authorization loopholes:
// VULNERABLE custom guard extracting unverified user headers
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
// Bypass: trusts the client-passed header instead of validating JWT
const userId = request.headers['x-user-id'];
return !!userId;
}
} The Fix: All identity context must be derived from cryptographically verified JSON Web Tokens (JWT) verified server-side, never trusted directly from raw client-submitted headers.
Circular dependencies and Denial of Service (DoS)
NestJS uses dependency injection to manage classes. Circular dependencies (e.g., Module A imports Module B, which imports Module A) can cause memory exhaustion during server startup or container restarts. During our source code reviews, we run dependency analysis tools to identify circular references that could be exploited to crash services.
DTO over-posting leading to database value overwrite
An update endpoint accepts "isVerified": true because the global ValidationPipe does not remove or reject unknown fields. If the service passes the body into TypeORM, the caller can change a protected column. Use request DTOs, enable whitelist and forbidNonWhitelisted, and map allowed fields into the update.
Test NestJS across guards, pipes, and services
- Map controllers. Record each route, method, guard, pipe, interceptor, DTO, role, and service call. Include inherited and module-level guards.
- Send unknown fields. Add protected properties to create, update, and nested DTOs. Confirm the global pipe rejects them and nested validation runs.
- Call every transport. Test HTTP, GraphQL resolvers, WebSocket gateways, queues, and microservice handlers. A guard on one transport does not protect another.
- Change identity context. Replace object IDs, tenant IDs, JWT claims, forwarded headers, and GraphQL variables. The service must enforce ownership before database access.
- Test errors and timeouts. Force database and downstream failures. Confirm filters remove stack traces and transactions prevent partial writes.
Keep the route matrix, global application setup, raw request pairs, guard decisions, SQL logs, and before-and-after database values. A retest must cover the controller and the service method that performs the write.
Building scalable backend systems in NestJS? Schedule a security audit.
Book a NestJS PentestFrequently asked questions
Why is class-validator parameter pollution dangerous in NestJS?
If NestJS is configured with `ValidationPipe` but has `whitelist` set to false, the incoming request DTO will accept unexpected client-submitted JSON properties. These un-validated fields pass directly to typeorm/mongoose DB queries.
How do you bypass NestJS guards during a penetration test?
We audit NestJS guards to check if they trust client headers directly (such as `x-user-id` or `x-role`) without cryptographic validation, or if request context variables are mutated across concurrent calls.
What is the security risk of using custom NestJS decorators?
Custom decorators (like `@CurrentUser()`) read values from the request object. If they fail to validate the session state or pull data from un-sanitized fields, they introduce authorization flaws.
Related reading
Blog: Securing Node Express backends · JWT token mistakes · Next.js Server Actions security
Services: API security testing · Secure architecture review