Jason Statham, de la saga de películas de acción "Fast & Furious", le da un giro inesperado a la trama cuando el contenido de un blog se convierte en una investigación de seguridad. En el mundo de la ciberseguridad, donde las vulnerabilidades son como los errores de un guión mal escrito, cada línea de código y cada configuración pueden ser el punto de entrada para un atacante. Hoy, desmantelaremos una API REST construida con NestJS, no para alabarla, sino para diseccionar sus defensas y descubrir dónde podría fallar bajo presión. Si tu código es un castillo, este análisis es tu equipo de asalto, listos para encontrar las debilidades.

As developers, we’re often tasked with building APIs. It’s the backbone of modern applications, the silent handshake between services. But how do we build them not just functional, but *resilient*? How do we ensure the structure we erect can withstand the inevitable siege of malformed requests and malicious intent? NestJS, a framework rapidly gaining traction in the Node.js ecosystem, promises scalability and maintainability. But promises are cheap. What matters is the implementation, the hardened configurations, the end-to-end validation. Today, we’re not just learning NestJS; we’re stress-testing it. We’ll construct a CRUD REST API, not with the naive optimism of a beginner, but with the forensic eye of an attacker looking for exploitable patterns.
The objective: to build a bookmarks API from the ground up. The tools: NestJS, Docker for environment isolation, PostgreSQL for robust data storage, Passport.js and JWT for authentication, Prisma as our ORM, PactumJS for end-to-end testing, and dotenv to manage our sensitive configurations. This isn't a gentle introduction; it's a tactical deployment.
## The Threat Landscape of Modern Backend Development
The digital frontier is a volatile place. Every new framework, every library, introduces a potential attack surface. NestJS, with its modular architecture and strong typing, aims to mitigate common pitfalls. But a framework is only as strong as the developer wielding it. Common vulnerabilities lurk in the shadows of inadequate input validation, weak authentication mechanisms, and insecure configurations.
Consider the simple act of creating a user. Without proper sanitization and validation, an attacker could inject malicious scripts or exploit type coercion. Authentication, often handled by libraries like Passport.js, requires meticulous configuration to prevent sessions from being hijacked or tokens from being forged. And data persistence, even with an ORM like Prisma, can be vulnerable to injection attacks if not handled correctly. Our goal is to build with these threats in mind, anticipating the adversary’s moves at every stage.
The NestJS Arsenal: Building Blocks for a Resilient API
NestJS provides a structured approach, but understanding its core components is crucial for building secure applications.
- **Modules**: These are organizational units that group related components. Think of them as self-contained security zones. A well-defined module can limit the blast radius of a compromise.
- **Controllers**: They handle incoming requests and outgoing responses. This is the front line. Proper validation and sanitization *must* be enforced here.
- **Services**: Business logic resides here. This is where the core operations are performed, and where data integrity checks are paramount.
- **Dependency Injection**: NestJS’s DI system allows for decoupling components, making the codebase more testable and maintainable. This is invaluable for isolating and testing individual security controls.
- **Pipes**: These are powerful tools for validation and transformation of request payloads. They are your first line of defense against malformed inputs.
- **Guards**: These are responsible for authorization. They determine if a user is allowed to access a specific route or resource. This is your access control list, enforced programmatically.
- **Decorators**: Custom decorators can encapsulate complex logic, such as extracting user information from JWT tokens, thereby simplifying route handlers.
Tactical Deployment: Setting Up the Environment
Before we write a single line of application code, we need a secure and isolated development environment. Docker is our chosen tool for this.
### Setting Up PostgreSQL in Docker
First, we need our database. PostgreSQL is a robust relational database, but like any system, it needs careful configuration.
docker run --name postgres-bookmarks -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 -d postgres
This command spins up a PostgreSQL instance, assigning it a name, setting a password, exposing the default port, and running it in detached mode. We'll later refine security by creating dedicated database users and restricting network access.
### Setting Up Prisma
Prisma simplifies database interactions. Its schema-first approach enforces data consistency.
First, install Prisma CLI:
npm install prisma --save-dev
Then, initialize Prisma in your NestJS project:
npx prisma init --datasource-provider postgresql
This creates a `prisma` directory with `schema.prisma`.
### Defining Data Models
The `schema.prisma` file is where we define our database structure. This is critical for data integrity and preventing injection vulnerabilities.
// schema.prisma
datasource db {
url = env("DATABASE_URL")
provider = "postgresql"
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(uuid())
email String @unique
password String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
bookmarks Bookmark[]
}
model Bookmark {
id String @id @default(uuid())
title String
url String
description String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
We define two models: `User` and `Bookmark`. Notice the `userId` in the `Bookmark` model with `onDelete: Cascade`, ensuring that when a user is deleted, their associated bookmarks are also removed, preventing orphaned data.
### Running Prisma Migrations
To apply these models to our database, we use migrations.
npx prisma migrate dev --name init
This command will create the necessary tables in our PostgreSQL database. For automated restarts and migrations in development, consider integrating `dotenv-cli` with Prisma.
# Example for package.json scripts
"dev": "dotenv -e .env -- nest start --watch"
"prisma:migrate": "dotenv -e .env -- prisma migrate dev"
Authentication: The Gatekeeper's Protocol
Secure authentication is non-negotiable. A weak auth mechanism is an open back door. We'll use Passport.js with JWT (JSON Web Tokens) for stateless authentication.
### Using Auth DTOs
Data Transfer Objects (DTOs) ensure that incoming data conforms to expected shapes and types. This is where input validation starts.
// src/auth/dto/signup.dto.ts
import { IsEmail, IsNotEmpty, IsString, MinLength } from 'class-validator';
export class SignupDto {
@IsEmail()
email: string;
@IsNotEmpty()
@MinLength(8, { message: 'Password must be at least 8 characters long' })
password: string;
}
// src/auth/dto/signin.dto.ts
import { IsEmail, IsNotEmpty } from 'class-validator';
import { SignupDto } from './signup.dto'; // Re-using email validation
export class SigninDto extends SignupDto {}
These DTOs, when used with NestJS Pipes, will automatically validate incoming request bodies, rejecting malformed data before it even hits our business logic.
### NestJS Pipes for Validation
Pipes are the first layer of defense in NestJS for request data.
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true }));
await app.listen(3000);
}
bootstrap();
By setting `whitelist: true`, unknown properties are stripped from the payload. `forbidNonWhitelisted: true` will throw an error if unknown properties are present, making it harder for attackers to smuggle in unexpected data.
### Hashing Passwords
Never store passwords in plaintext. Use a strong, salted hashing algorithm like Argon2.
// src/auth/auth.service.ts
import * as argon2 from 'argon2';
async hashPassword(password: string): Promise<string> {
return argon2.hash(password);
}
async verifyPassword(storedPasswordHash: string, providedPassword: string): Promise<boolean> {
return argon2.verify(storedPasswordHash, providedPassword);
}
Integrate this into your signup and signin logic.
### Implementing Sign Up and Sign In Logic
The core of authentication involves securely hashing passwords on signup and verifying them on signin.
// src/auth/auth.service.ts (simplified)
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { SignupDto } from './dto/signup.dto';
import { SigninDto } from './dto/signin.dto';
import * as argon2 from 'argon2';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(
private prisma: PrismaService,
private jwtService: JwtService,
) {}
async signup(signupDto: SignupDto): Promise<any> {
const hashedPassword = await argon2.hash(signupDto.password);
const user = await this.prisma.user.create({
data: {
email: signupDto.email,
password: hashedPassword,
},
});
const token = this.jwtService.sign({ email: user.email, sub: user.id });
return { access_token: token };
}
async signin(signinDto: SigninDto): Promise<any> {
const user = await this.prisma.user.findUnique({
where: { email: signinDto.email },
});
if (!user) {
throw new UnauthorizedException('Invalid credentials');
}
const passwordMatches = await argon2.verify(user.password, signinDto.password);
if (!passwordMatches) {
throw new UnauthorizedException('Invalid credentials');
}
const token = this.jwtService.sign({ email: user.email, sub: user.id });
return { access_token: token };
}
}
This service handles user creation with hashed passwords and token generation upon successful sign-in.
### NestJS Config Module
Sensitive information like JWT secrets and database URLs must be managed securely. The NestJS Config Module is ideal for this.
npm install @nestjs/config
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AuthModule } from './auth/auth.module';
import { PrismaModule } from './prisma/prisma.module';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // Makes ConfigService available globally
envFilePath: ['.env'], // Load environment variables from .env file
}),
AuthModule,
PrismaModule,
],
})
export class AppModule {}
Ensure your `.env` file contains your JWT secret and database URL:
DATABASE_URL="postgresql://user:password@host:port/database?schema=public"
JWT_SECRET="your_super_secret_key_here"
### Using Passport.js and JWT Module
Integrate Passport.js and the JWT strategy for protecting routes.
npm install passport passport-jwt @nestjs/jwt
// src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { JwtStrategy } from './jwt.strategy';
import { PrismaModule } from '../prisma/prisma.module';
@Module({
imports: [
PrismaModule,
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.register({
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '1h' }, // Token expiration
}),
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
exports: [AuthService, JwtModule, PassportModule],
})
export class AuthModule {}
And create your JWT strategy:
// src/auth/jwt.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PrismaService } from '../prisma/prisma.service';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
private prisma: PrismaService,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET,
});
}
async validate(payload: any) {
// Here you can fetch user details from DB based on payload.sub
// This is crucial for ensuring the token is still valid against your user base
const user = await this.prisma.user.findUnique({ where: { id: payload.sub } });
if (!user) {
return false; // Or throw an error
}
return { userId: payload.sub, email: payload.email };
}
}
Proteja sus rutas con el decorador `@UseGuards(JwtAuthGuard)`.
### Get Current User with Access Token
To access user-specific data, you often need to know who the current user is.
// src/auth/auth.controller.ts
import { Controller, Post, Body, UseGuards, Get, Req } from '@nestjs/common';
import { AuthService } from './auth.service';
import { SignupDto } from './dto/signup.dto';
import { SigninDto } from './dto/signin.dto';
import { JwtAuthGuard } from './jwt-auth.guard';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('signup')
signup(@Body() signupDto: SignupDto) {
return this.authService.signup(signupDto);
}
@Post('signin')
signin(@Body() signinDto: SigninDto) {
return this.authService.signin(signinDto);
}
@UseGuards(JwtAuthGuard)
@Get('profile')
getProfile(@Req() req) {
// req.user is populated by the JwtStrategy's validate method
return req.user;
}
}
The `JwtAuthGuard` will automatically attach the validated user information to the `req.user` object.
Securing Resources with Guards and Decorators
Authorization is the next critical layer. Guards ensure that only authenticated and authorized users can access specific endpoints.
### NestJS Guards
We've already seen `JwtAuthGuard` in action. You can create custom guards for more granular control.
// src/common/guards/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler());
if (!requiredRoles) {
return true; // No roles specified, allow access
}
const { user } = context.switchToHttp().getRequest();
// Assuming user object has a 'roles' property from your validate method
return requiredRoles.some((role) => user.roles?.includes(role));
}
}
You would then use this guard with a custom role decorator.
### NestJS Custom Param Decorator
Custom parameter decorators can simplify data extraction from requests.
// src/common/decorators/get-user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const GetUser = createParamDecorator(
(data: string | undefined, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
if (data) {
return request.user[data]; // e.g., GetUser('userId')
}
return request.user; // Return the entire user object
},
);
This decorator, `GetUser`, can be used in your controller methods to easily access the authenticated user's details.
End-to-End Testing: The Final Fortress
Robust end-to-end (E2E) tests are your final line of defense, simulating real-world user interactions and validating the entire application flow. PactumJS is an excellent choice for this.
### E2E Tests with PactumJS
PactumJS allows you to define API interactions and assertions in a clear, readable syntax.
First, install Pactum:
npm install pactum --save-dev
You'll need to set up a test environment, often a separate Docker network or a dedicated test database.
### Setting Up the Test Database
Automating the test database setup and teardown is crucial for reliable E2E tests.
// src/test/jest-setup.ts (example for Jest)
import { INestApplication, ValidationPipe } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { AppModule } from '../app.module';
import * as request from 'supertest';
import * as pactum from 'pactum';
import { PrismaService } from '../prisma/prisma.service';
let app: INestApplication;
let prisma: PrismaService;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true }));
await app.init();
await app.listen(0); // Listen on a random port
prisma = moduleFixture.get<PrismaService>(PrismaService);
// Set Pactum base URL
const appAddress = app.getHttpServer().address();
pactum.request.setBaseURL(`http://localhost:${appAddress.port}`);
// Cleanup database before tests
await prisma.bookmark.deleteMany();
await prisma.user.deleteMany();
});
afterAll(async () => {
await app.close();
});
export { app, prisma };
You might use `dotenv-cli` to manage test-specific `.env` files.
# package.json scripts for testing
"test": "jest --config ./jest.config.js",
"test:e2e": "jest --config ./jest.e2e.config.js"
### Automating Test Database Restart & Migrations
Ensure your test database is clean and migrated before each test run. This can be integrated into your test setup scripts.
### Auth E2E Tests
Simulate signup and signin flows with Pactum.
// src/auth/auth.e2e-spec.ts (example)
import * as pactum from 'pactum';
import { INestApplication } from '@nestjs/common';
let app: INestApplication; // Assume app is initialized from jest-setup.ts
describe('Auth', () => {
beforeAll(async () => {
// Initialize app and prisma if not done globally
});
it('should signup', async () => {
return pactum.spec()
.post('/auth/signup')
.withJson({
email: 'test@example.com',
password: 'password123',
})
.expectStatus(201)
.expectBodyContains('') // Check for presence of token, e.g., { access_token: '...' }
.stores('userToken', 'access_token'); // Store token for later use
});
it('should signin and get token', async () => {
return pactum.spec()
.post('/auth/signin')
.withJson({
email: 'test@example.com',
password: 'password123',
})
.expectStatus(200)
.expectBodyContains('') // Similar check as signup
.stores('userToken', 'access_token'); // Re-store or use existing
});
});
### User E2E Tests
Test user-related endpoints, ensuring authorization works.
// src/user/user.e2e-spec.ts (example)
import * as pactum from 'pactum';
// ... imports and setup
describe('User', () => {
it('should get current user profile', async () => {
return pactum.spec()
.get('/auth/profile') // Assuming this endpoint returns user info
.withHeaders({
Authorization: 'Bearer $S{userToken}', // Use stored token
})
.expectStatus(200)
.expectBodyContains('test@example.com'); // Check for user's email
});
});
### Bookmarks E2E Test
Test CRUD operations for bookmarks, ensuring they are secured.
// src/bookmark/bookmark.e2e-spec.ts (example)
import * as pactum from 'pactum';
// ... imports and setup
describe('Bookmarks', () => {
it('should create a bookmark', async () => {
return pactum.spec()
.post('/bookmarks')
.withHeaders({
Authorization: 'Bearer $S{userToken}',
})
.withJson({
title: 'NestJS Docs',
url: 'https://docs.nestjs.com/',
description: 'Official NestJS documentation',
})
.expectStatus(201)
.stores('bookmarkId', 'id'); // Store bookmark ID
});
it('should get all bookmarks', async () => {
return pactum.spec()
.get('/bookmarks')
.withHeaders({
Authorization: 'Bearer $S{userToken}',
})
.expectStatus(200)
.expectJsonLength(1); // Expecting one bookmark
});
it('should get a specific bookmark', async () => {
return pactum.spec()
.get('/bookmarks/$S{bookmarkId}')
.withHeaders({
Authorization: 'Bearer $S{userToken}',
})
.expectStatus(200)
.expectBodyContains('NestJS Docs');
});
it('should update a bookmark', async () => {
return pactum.spec()
.patch('/bookmarks/$S{bookmarkId}')
.withHeaders({
Authorization: 'Bearer $S{userToken}',
})
.withJson({
description: 'Updated NestJS documentation link',
})
.expectStatus(200)
.expectBodyContains('Updated NestJS documentation link');
});
it('should delete a bookmark', async () => {
return pactum.spec()
.delete('/bookmarks/$S{bookmarkId}')
.withHeaders({
Authorization: 'Bearer $S{userToken}',
})
.expectStatus(204); // No content on successful deletion
});
});
### Prisma Database Teardown Logic
Crucially, ensure your test data is cleaned up after tests to prevent state leakage between test runs. This is typically handled in `afterAll` or `afterEach` hooks in your test setup.
Veredicto del Ingeniero: ¿Vale la pena adoptar NestJS para aplicaciones seguras?
NestJS es una potencia para construir APIs backend robustas y escalables. Su arquitectura modular, el fuerte énfasis en la validación y las capacidades de inyección de dependencias lo convierten en una opción sólida. Cuando se combina con herramientas como Docker para el aislamiento, Prisma para una gestión de datos disciplinada, y PactumJS para pruebas exhaustivas, se sienta una base fuerte para la seguridad.
**Pros:**
- **Estructura Robusta**: Impone una arquitectura organizada, reduciendo el caos y los errores comunes.
- **Validación Integrada**: Pipes y DTOs simplifican la sanitización de entradas, un punto crítico para la seguridad.
- **Extensibilidad**: Fácil integración con librerías de autenticación como Passport.js y herramientas de testing como Pactum.
- **Comunidad Creciente**: Un ecosistema en expansión y buena documentación.
**Contras:**
- **Curva de Aprendizaje**: Para quienes vienen de frameworks más simples, la abstracción y la configuración pueden ser un obstáculo inicial.
- **Configuración Delicada**: Si bien las herramientas son potentes, una configuración incorrecta de JWT, permisos o validaciones puede dejar brechas significativas.
- **No una Bala de Plata**: NestJS no te hace inmune a las vulnerabilidades; sigue siendo necesario un conocimiento profundo de seguridad de aplicaciones web.
**Respuesta:** Sí. Para proyectos que requieren escalabilidad, mantenibilidad y un enfoque estructurado en la seguridad desde el principio, NestJS es una excelente elección. Sin embargo, la seguridad efectiva depende de una implementación diligente de sus características y de una comprensión continua de las amenazas.
Arsenal del Operador/Analista
Para aquellos que operan en las trincheras digitales, el equipo adecuado es tan crucial como el conocimiento. Aquí hay algunos elementos que considerar:
- **Software de Desarrollo y Testing**:
- **Visual Studio Code**: El editor omnipresente, con extensiones para TypeScript, Docker, y NestJS.
- **Docker Desktop**: Esencial para aislar entornos y replicar configuraciones de producción.
- **Postman / Insomnia**: Para pruebas manuales de API durante el desarrollo.
- **PactumJS**: Para automatizar sus suites de pruebas end-to-end.
- **Herramientas de Monitorización y Logging**:
- **ELK Stack (Elasticsearch, Logstash, Kibana)** o **Grafana/Prometheus**: Para recolectar, analizar y visualizar logs y métricas de la aplicación, permitiendo la detección temprana de anomalías.
- **Libros Clave**:
- "The Web Application Hacker's Handbook" (Dafydd Stuttard, Marcus Pinto): Un clásico para entender las vulnerabilidades web.
- "OWASP Top 10": El estándar de oro para las vulnerabilidades de seguridad de aplicaciones web.
- "Node.js Design Patterns" (Mario Casciaro): Para comprender patrones de diseño avanzados en Node.js.
- **Certificaciones Relevantes**:
- **OSCP (Offensive Security Certified Professional)**: Para una comprensión profunda de las técnicas de hacking ofensivo.
- **CISSP (Certified Information Systems Security Professional)**: Para una perspectiva de gestión y arquitectura de seguridad.
- **Certificaciones específicas de Cloud (AWS, Azure, GCP)**: Dado que las aplicaciones modernas suelen desplegarse en la nube.
Preguntas Frecuentes
¿Es NestJS seguro por defecto?
NestJS proporciona herramientas y patrones que *facilitan* la construcción de aplicaciones seguras (como pipes para validación, guards para autorización). Sin embargo, la seguridad por defecto no existe. La implementación correcta de estas características, junto con prácticas de codificación seguras y la gestión de dependencias, es lo que determina la seguridad real de su aplicación.
¿Qué vulnerabilidades son comunes en aplicaciones NestJS?
Las vulnerabilidades en aplicaciones NestJS suelen ser las mismas que en cualquier aplicación Node.js: inyección de código (si no se validan las entradas), autenticación y autorización débiles, exposición de información sensible en logs o respuestas de API, dependencias desactualizadas con vulnerabilidades conocidas (CVEs), y problemas de configuración de seguridad en Docker o en el entorno de despliegue.
¿Cómo puedo mejorar la seguridad de mi API NestJS?
1. **Validación Rigurosa**: Use `class-validator` y `class-transformer` con `ValidationPipe` para validar TODAS las entradas.
2. **Autenticación y Autorización Fuertes**: Implemente JWT con una estrategia segura (corta expiración, secretos fuertes), use Guards para proteger rutas, y considere roles o permisos para acceso granular.
3. **Gestión de Dependencias**: Audite regularmente sus dependencias con `npm audit` o herramientas similares y manténgalas actualizadas.
4. **Configuración Segura**: Gestione las variables de entorno (`.env`) de forma segura, especialmente secretos de JWT, claves de API y credenciales de base de datos. NO las incluya en el código fuente.
5. **Logging y Monitorización**: Implemente logging detallado y monitorización para detectar actividades sospechosas o errores.
6. **Pruebas Exhaustivas**: Escriba pruebas E2E con herramientas como PactumJS para cubrir flujos de seguridad críticos.
7. **Despliegue Seguro**: Configure Docker y su servidor de despliegue con los principios de menor privilegio y retire la información de depuración en producción.
¿Qué herramienta de testing de API es mejor: PactumJS o Supertest?
Ambas son excelentes para Node.js. Supertest es más de bajo nivel y se integra bien con frameworks de testing como Jest, enfocándose en la interacción HTTP. PactumJS está diseñado específicamente para testing de APIs, ofreciendo una sintaxis más declarativa, manejo de variables de entorno para credenciales, y un enfoque más "end-to-end" que simula mejor el uso de un cliente real. Para pruebas de seguridad de APIs, PactumJS a menudo se siente más natural y potente.
El Contrato: Audit your Dependencies
Has construido una API, has configurado la autenticación, has implementado validaciones. Pero el código rara vez vive aislado. Las dependencias que incluyes son puntos de entrada potenciales para el adversario.
Tu contrato es simple: **Ejecuta `npm audit` en tu proyecto de NestJS (en desarrollo y producción) y revisa cada vulnerabilidad reportada**. No te limites a parchear; comprende el riesgo. ¿Es una vulnerabilidad crítica en una dependencia central? ¿O una de baja severidad en una herramienta de utilidad que no está expuesta directamente? ¿Tienes un plan para actualizar estas dependencias de manera segura?
El verdadero dominio no es solo la creación, sino la vigilancia. Ahora, sal y audita el perímetro de tus propias creaciones. Demuéstrame que no solo construyes, sino que proteges.