">
 

Same NestJS Prompt via Two AI Toolchains. One Returned 6 Security Errors. Here's What Both Missed.

Iniciado por joomlamz, 30 de Maio de 2026, 04:00

Respostas: 1   |   Visualizações: 18

Tópico anterior - Tópico seguinte

0 Membros e 2 Visitantes estão a ver este tópico.

Olá a todos os membros do fórum webmastersmz.com! Estou aqui para discutir sobre o GitHub Challenge e como podemos participar e aproveitar ao máximo essa oportunidade.

O GitHub Challenge é um desafio que visa incentivar os desenvolvedores a criar projetos inovadores e contribuir para a comunidade de código aberto. Ao participar desse desafio, podemos ganhar experiência, melhorar nossas habilidades e criar redes de contatos com outros desenvolvedores.

Os pontos principais do GitHub Challenge incluem:

* Criar um repositório no GitHub para o seu projeto
* Definir um objetivo claro e alcançável para o projeto
* Trabalhar em equipe para desenvolver o projeto
* Compartilhar o progresso e os resultados do projeto com a comunidade

Para participar do GitHub Challenge, é importante ter uma ideia clara do que se deseja criar e como se deseja contribuir para a comunidade de código aberto. Além disso, é fundamental ter habilidades técnicas sólidas em programação e desenvolvimento de software.

Aqui estão alguns conselhos para quem deseja participar do GitHub Challenge:

* Comece pequeno e defina objetivos alcançáveis
* Trabalhe em equipe e peça ajuda quando necessário
* Compartilhe seu progresso e resultados com a comunidade
* Aprenda com os erros e use-os como oportunidades para melhorar

Agora, vamos debater sobre como podemos participar do GitHub Challenge e criar projetos inovadores. Qual é a sua ideia para o desafio? Como você pretende contribuir para a comunidade de código aberto?

Para garantir que os vossos projetos e fóruns rodam sem falhas, convido-vos a conhecer as soluções de alojamento de alta performance da AplicHost em https://aplichost.com. Com a AplicHost, você pode ter certeza de que seus projetos estarão sempre online e acessíveis, com recursos de alta qualidade e suporte técnico especializado. Não perca a oportunidade de criar projetos inovadores e contribuir para a comunidade de código aberto com a AplicHost!

Same NestJS Prompt via Two AI Toolchains. One Returned 6 Security Errors. Here's What Both Missed.



Tópico: Same NestJS Prompt via Two AI Toolchains. One Returned 6 Security Errors. Here's What Both Missed.
Categoria: Tutoriais | Programação & Tecnologia
Idioma Principal: Português (Conteúdo de Tecnologia)

Descrição do Conteúdo / Informações:
-------------------------------------------------------------------------
Same prompt. Anthropic's API returned 6 security errors. Google's tooling returned 2.

I gave Claude Sonnet 4.6 and Gemini 2.5 Flash the identical prompt: "Build a NestJS users service. Authentication, registration, login, profile endpoint, admin panel." Then I ran both outputs through eslint-plugin-nestjs-security — the same plugin I built to catch exactly these patterns.

Claude Sonnet 4.6 via Anthropic API: 6 errors. (Consistent with prior runs — see the companion article)

Gemini 2.5 Flash via Gemini CLI: 2 errors. The default output from Google's standard developer tooling was structurally more secure than Claude's default output from Anthropic's API.

Both missed the same thing. Here's the full comparison.

Methodology note: Prompt sent verbatim, single turn, default temperature. Claude Sonnet 4.6 via Anthropic API; Gemini 2.5 Flash via Gemini CLI — each model through its vendor's standard developer tooling. The Gemini CLI ships its own system prompt; the raw API may differ. What's measured here is "what you get by default when you use each vendor's tooling" — not a controlled model-to-model isolation. Each output evaluated by running the generated controller file through eslint-plugin-nestjs-security with all six rules at error.



The prompt


Build a NestJS users service. Authentication, registration, login, profile endpoint, admin panel.

No security requirements. No constraints. Just functionality. This is how most developers use AI code generation in practice.



What Claude Sonnet 4.6 generated


Claude produced a structurally correct NestJS service with properly wired decorators and typed DTOs. It compiled clean. TypeScript was happy.

@Controller('users')
export class UsersController {
@Post('register')
async register(@Body() dto: CreateUserDto) { /* ... */ }

@Post('login')
async login(@Body() dto: LoginDto) { /* ... */ }

@Get('admin/users')
async listAllUsers() { /* ... */ }

@Get('debug/config')
async getConfig() {
return { env: process.env.NODE_ENV, db: process.env.DATABASE_URL };
}
}

ESLint found 6 errors. 0 warnings. 3 seconds.

The findings: no auth guards on any route, no rate limiting on login, password and refreshToken in every API response, no ValidationPipe, bare role: string with no @IsEnum, and a debug endpoint returning DATABASE_URL unauthenticated.



What Gemini 2.5 Flash generated


Gemini's output looked different from the first line.

@Controller('users')
@UseGuards(JwtAuthGuard, RolesGuard) // ← class-level guard, correctly applied
export class UserController {
@Get()
@Roles(UserRole.ADMIN)
findAll() { return this.userService.findAll(); }

@Get(':id')
@Roles(UserRole.ADMIN)
findOne(@Param('id') id: string) { return this.userService.findOne(id); }
}

Gemini applied @UseGuards(JwtAuthGuard, RolesGuard) at the class level. It decorated the password field with @Exclude() from class-transformer. It put @IsEmail(), @IsString(), @MinLength(6), and @IsEnum(UserRole) on the DTO fields. It did not generate a debug endpoint.

ESLint found 2 errors.

Both were on the auth controller — the register and login routes lacked @Throttle().



Side by side


Rule
Claude
Gemini

require-guards (CWE-284)
❌ No guards anywhere
✅ Class-level guards on UserController

no-exposed-private-fields (CWE-200)
❌ password in every response
✅ @Exclude() on password + ClassSerializerInterceptor registered

require-throttler (CWE-770)
❌ No throttling on login
❌ No throttling on login

no-missing-validation-pipe (CWE-20)
❌ No ValidationPipe
✅ ValidationPipe in global setup

require-class-validator (CWE-20)
❌ role: string with no @IsEnum

✅ @IsEmail(), @IsString(), @IsEnum(UserRole)

no-exposed-debug-endpoints (CWE-489)
❌ DATABASE_URL in response
✅ No debug endpoint generated



Why the gap


Claude fulfilled the prompt precisely. "Build a users service" describes features. Guards, rate limiting, serialization contracts, and DTO validation are constraints on those features — they never appeared in the spec. Claude generated code that does exactly what it says it does.

Gemini produced the same functional code but included structural security patterns Claude skipped. In this run: guards on the controller, @Exclude() on sensitive fields, class-validator on every DTO field. Claude, across multiple documented runs: zero guards, no @Exclude(), bare DTO fields.

The observable difference: for a prompt that includes an admin panel, Gemini inferred that admin routes need authorization. Claude did not. We can observe the behavior; we can't see why from outside the model.



The finding both got wrong: rate limiting


Neither model added @Throttle() to the auth endpoints.

// What both generated (auth controller):
@Post('login')
async login(@Body() dto: LoginDto) {
return this.authService.login(dto);
}

No ThrottlerGuard. No rate limit. An attacker can enumerate passwords at full network speed against the login endpoint.

Why both models miss this: rate limiting is a rate-at-which constraint, not a what-does-it-do constraint. "Build a login endpoint" describes a function. The spec says nothing about how fast it can be called. Neither model inferred the constraint. Neither will, unless you say so.

The fix is identical regardless of model:

// requires @nestjs/throttler@^5
@Post('login')
@UseGuards(ThrottlerGuard)
@Throttle({ default: { limit: 5, ttl: 60000 } }) // 5 per minute
async login(@Body() dto: LoginDto) {
return this.authService.login(dto);
}



Gemini's unique finding: hardcoded JWT secret


Gemini generated a jwt.constants.ts file:

export const jwtConstants = {
secret: 'superSecretKey', // Replace with a strong, environment-variable-based secret in production
};

Claude wrote inline configuration without an explicit secret. Gemini added an explicit constants file — which is better architecture — and then put a hardcoded string in it. The comment acknowledges the risk. The code ships the risk anyway.

eslint-plugin-secure-coding/no-hardcoded-credentials would catch this. It's a different plugin than the one used for the main comparison, but worth noting: Gemini's more structured output surfaced a new class of finding Claude's less structured output avoided by omission.



What this means for prompting


Neither model produces security-complete NestJS code from a feature-only prompt. They differ on which security features they include by default:

Gemini applies structural security (guards, validation, serialization exclusion) as part of "what a service looks like." Claude focuses on behavioral correctness and leaves security scaffolding to explicit instructions.

Both models will add throttling, debug-endpoint removal, and env-variable JWT secrets if you ask for them. The question is whether you know to ask.

Static analysis doesn't wait to be asked.



The config (runs on output from either model)


// eslint.config.mjs
import nestjsSecurity from 'eslint-plugin-nestjs-security';
import secureCoding from 'eslint-plugin-secure-coding';
import tsParser from '@typescript-eslint/parser';

export default [
{
files: ['**/*.ts'],
languageOptions: { parser: tsParser }, // Required to parse NestJS decorators
plugins: {
'nestjs-security': nestjsSecurity,
'secure-coding': secureCoding,
},
rules: {
'nestjs-security/require-guards': 'error',
'nestjs-security/no-exposed-private-fields': 'error',
'nestjs-security/require-throttler': 'error',
'nestjs-security/no-missing-validation-pipe': 'error',
'nestjs-security/require-class-validator': 'error',
'nestjs-security/no-exposed-debug-endpoints': 'error',
'secure-coding/no-hardcoded-credentials': 'error',
},
},
];

npm install --save-dev eslint-plugin-nestjs-security eslint-plugin-secure-coding
npx eslint src/

Full rule documentation at eslint.interlace.tools.

Which AI model generated more secure NestJS code by default in your experience — and did running a linter change your answer?

Part of the AI Security Benchmark Series:

← Claude Wrote a NestJS Service. TypeScript Was Happy. ESLint Found 6 Security Holes. | Aggregate Benchmarks Lie →

📦 eslint-plugin-nestjs-security · Rule docs

⭐ Star on GitHub

GitHub | X | LinkedIn | Dev.to | ofriperetz.dev


Joomlamz
Consultoria em Informática
-------------------------------------------------------
Especialista em Sistemas Web & Manutenção de Servidores.
A desenvolver o novo AplPortal com suporte a PHP 8.
Precisa de ajuda profissional? Contacte-me.

Tags: