DEV Community

Cover image for N-Day-Bench: ¿pueden los LLMs encontrar vulnerabilidades reales en código real?
Juan Torchia
Juan Torchia

Posted on • Originally published at juanchi.dev

N-Day-Bench: ¿pueden los LLMs encontrar vulnerabilidades reales en código real?

Hay una diferencia enorme entre el plomero que te arregla el caño y el que te lo rompe. Pero si el mismo plomero puede hacer las dos cosas dependiendo de si le preguntás o no, tenés un problema de proceso, no de herramienta.

Eso es exactamente lo que me pasó. Aprobé tres PRs en el mismo sprint. Las tres tenían keys hardcodeadas. Las tres venían con sugerencias parciales de Copilot o Claude. Y cuando finalmente alguien me señaló el problema en code review — semanas después — pensé: ¿por qué ninguno de los dos lo vio antes? Peor: ¿lo hubieran visto si alguien les preguntaba directamente?

N-Day-Bench intenta responder exactamente esa pregunta. Y la respuesta me dejó con más preguntas que antes.

LLMs vulnerabilidades seguridad benchmark: qué mide N-Day-Bench realmente

N-Day-Bench es un benchmark publicado a principios de 2025 que evalúa si los LLMs pueden identificar vulnerabilidades reales — no sintéticas, no de CTF — en codebases de producción reales. "N-Day" porque trabaja con vulnerabilidades ya conocidas (tienen CVE asignado), no con zero-days.

La metodología es más honesta que la mayoría:

  1. Toman CVEs reales con código afectado real
  2. Le dan a los modelos el contexto relevante (no todo el repo, sino los archivos pertinentes)
  3. Piden que identifiquen la vulnerabilidad sin darles pistas del CVE
  4. Miden si el modelo encuentra el problema correcto, no si genera texto plausible sobre seguridad

Esto último es importante. Muchos benchmarks de seguridad se conforman con que el modelo mencione el tipo correcto de vulnerabilidad. N-Day-Bench requiere precisión: archivo correcto, línea aproximada, mecanismo real de explotación.

Los resultados publicados muestran que los mejores modelos (GPT-4o, Claude 3.5 Sonnet, Gemini 1.5 Pro en las versiones evaluadas) identifican correctamente entre el 20% y el 35% de las vulnerabilidades cuando se los interroga de forma directa. Suena bajo. Pero compará con un developer promedio haciendo code review manual de código ajeno: el número no es tan diferente.

El problema real está en otro lado.

El gap que nadie menciona en los papers

Hay algo que N-Day-Bench no mide directamente pero que se infiere de los datos: la diferencia entre modo generación y modo auditoría.

Cuando un LLM está completando código — que es como lo usamos el 90% del tiempo — no está en modo crítico. Está en modo colaborativo. Su objetivo implícito es producir código que funcione y sea coherente con el contexto. La seguridad es una constraint secundaria a menos que vos la pongas explícitamente en primer plano.

Cuando le pedís específicamente que audite, cambia el frame. El mismo modelo, con el mismo código, encuentra cosas que no marcó mientras lo generaba.

// Ejemplo de lo que me pasó — reconstruido
// Generación: "completame esta función de conexión a la DB"
const connectDB = async () => {
  return await mongoose.connect(
    'mongodb://admin:MiPassword123@prod-server:27017/mydb', // el modelo completó esto
    { useNewUrlParser: true }
  );
};

// Auditoría: "encontrá problemas de seguridad en este código"
// Respuesta del mismo modelo:
// "Línea 3: credencial hardcodeada en el string de conexión.
//  Vector de ataque: exposición en repositorios, logs, stack traces.
//  Severidad: CRÍTICA. Solución: usar variables de entorno."
Enter fullscreen mode Exit fullscreen mode

Es el mismo modelo. El mismo código. Distinto prompt, distinto output.

Eso no es un bug del modelo. Es un bug mío. Yo no lo puse en modo auditoría mientras revisaba esos PRs.

Los números que me incomodan

Volvamos a N-Day-Bench. El 20-35% de detección suena razonable hasta que mirás de qué tipo son las vulnerabilidades que no encuentra.

Los modelos son razonablemente buenos con:

  • SQL injection en patrones clásicos
  • Credenciales hardcodeadas (el benchmark confirma esto)
  • XSS obvio en templates
  • Dependencias con CVEs conocidos si les dás el package.json

Los modelos fallan consistentemente con:

  • Vulnerabilidades de lógica de negocio (el código es "correcto" pero el flujo es explotable)
  • Race conditions sutiles
  • Problemas de autorización que requieren entender el modelo de datos completo
  • Vulnerabilidades que emergen de la interacción entre componentes, no de un componente aislado

Ese segundo grupo es exactamente el tipo de vulnerabilidad que te arruina en producción. No es el password hardcodeado — eso lo encontrás con un grep. Es el endpoint que valida permisos correctamente pero que, combinado con una feature de "importar configuración", te da path traversal arbitrario.

N-Day-Bench confirma lo que sospechaba: los LLMs son buenos como primera línea de defensa contra lo obvio. Son pésimos como sustitutos de un security review real.

Lo que cambié en mi workflow después de leer el paper

No soy researcher de seguridad. Soy un arquitecto que aprendió esto por las malas — igual que aprendí infraestructura tirando un servidor con rm -rf en mi primera semana de hosting, igual que aprendí sobre cold starts migrando de Vercel a Railway un fin de semana.

Lo que incorporé:

# Pre-commit hook que agregué al proyecto
# No reemplaza nada, es la primera línea

#!/bin/bash
echo "Corriendo auditoría básica pre-commit..."

# Secrets obvios
git diff --cached | grep -iE \
  '(password|secret|key|token)\s*[:=]\s*["\x27][^"\x27]{8,}' \
  && echo "⚠️  Posible credential hardcodeada detectada" \
  && exit 1

# Para el review del LLM, esto va en el PR template:
# "Pegá los archivos nuevos en Claude con el prompt:
#  'Sos un security auditor. Encontrá vulnerabilidades de seguridad
#  en este código. Sé específico: archivo, línea, mecanismo de explotación.
#  No me digas que use HTTPS — eso ya lo sé. Dame lo no obvio.'"
Enter fullscreen mode Exit fullscreen mode

El cambio real no es técnico. Es que ahora el PR template tiene una sección obligatoria: "Security audit prompt output". No podés mergear sin pegarla. Fuerza el cambio de frame del modelo.

Errores comunes cuando usás LLMs para security review

Error 1: Prompt genérico. "¿Tiene problemas de seguridad este código?" es el peor prompt posible. El modelo va a listar las mejores prácticas de OWASP que ya sabés. Mejor: "Asumí que soy un atacante con acceso de lectura al repo. ¿Cómo explotarías este código específicamente?"

Error 2: Contexto insuficiente. Le mandás una función aislada. El modelo no puede detectar vulnerabilidades que dependen del contexto más amplio. Mandá al menos los archivos que interactúan directamente con ese código.

Error 3: Confiar en el silencio. Si el modelo no encontró nada, no significa que no hay nada. Significa que no encontró nada con ese prompt y ese contexto. N-Day-Bench muestra que el 65-80% de las vulnerabilidades reales pasan el filtro del LLM.

Error 4: No iterar. Si el modelo dice "no veo problemas", preguntá de nuevo con el frame cambiado: "¿Qué input inesperado podría romper esta función?" o "¿Cómo abusarías del manejo de errores?"

Error 5: Usarlo solo para el código nuevo. Las vulnerabilidades más peligrosas suelen estar en el código viejo que nadie toca. Ese código no tiene tests, no tiene context, y nadie lo pone en el PR template.

Para tener el contexto de cómo pienso sobre herramientas y sus limitaciones, mi approach con Docker sigue la misma lógica: entender qué mide el tool antes de confiar en que mide lo que necesitás.

FAQ: LLMs y detección de vulnerabilidades

¿N-Day-Bench prueba vulnerabilidades de producción reales o ejemplos armados?
Vulnerabilidades reales con CVEs asignados. Eso es lo que lo diferencia de benchmarks anteriores. Toman el código afectado del commit que introdujo el bug, le dan contexto relevante al modelo, y verifican si puede identificar el mismo problema que el investigador que reportó el CVE. No es un ejercicio académico.

¿Qué modelo tiene mejor performance en el benchmark?
En las versiones evaluadas, los frontier models (GPT-4o, Claude 3.5 Sonnet) se mantienen en rangos similares — 30-35% en condiciones óptimas. La diferencia entre modelos es menor que la diferencia entre buenos y malos prompts con el mismo modelo. Eso es técnicamente interesante y prácticamente importante.

¿Tiene sentido usar LLMs para security review si solo encuentran el 35%?
Depende de qué reemplaza ese 35%. Si reemplaza zero review, es una mejora enorme. Si reemplaza un security engineer dedicado, es un riesgo. El benchmark no dice que los LLMs son malos en seguridad — dice que son buenos en un subconjunto específico de vulnerabilidades. Usarlos bien significa conocer ese subconjunto.

¿Por qué el mismo modelo genera código vulnerable y puede encontrarlo en auditoría?
El frame del prompt cambia el comportamiento. En modo generación, el objetivo es completar código funcional y coherente. En modo auditoría, el objetivo es encontrar problemas. No es inconsistencia del modelo — es que vos le estás pidiendo dos cosas distintas. N-Day-Bench opera exclusivamente en modo auditoría, que es el que te interesa para security review.

¿Esto reemplaza herramientas de SAST como Semgrep o Snyk?
No, y N-Day-Bench no lo pretende. SAST es determinístico — busca patrones conocidos con alta precisión. LLMs son probabilísticos — pueden razonar sobre contexto y semántica pero con menor consistencia. Son complementarios. SAST para lo conocido y sistemático, LLMs para razonamiento sobre lógica de negocio y patrones emergentes.

¿El benchmark considera el costo de falsos positivos?
Ahí hay una limitación real del paper: mide recall (cuántos bugs reales encontró) pero no mide precision de la misma forma (cuántas alertas eran ruido). En la práctica, un modelo que genera 50 alertas por PR con 2 reales es peor que uno que genera 5 con 2 reales. Es un gap que los autores reconocen y que versiones futuras del benchmark deberían incorporar.

Lo que haría diferente

Mi crítica a cómo se comunica N-Day-Bench no es al paper en sí — es metodológicamente honesto. Es a cómo se va a leer.

El headline "LLMs can find real vulnerabilities" va a generar confianza donde debería generar proceso. Los equipos van a leer el 35% como "pasamos el código por el modelo y listo". No funciona así. Igual que con datos abiertos — cuando sonifiqué el tráfico de colectivos aprendí que tener los datos no es lo mismo que entenderlos. El modelo tiene los datos de seguridad. Usarlos bien requiere diseño.

Lo que haría diferente en un equipo hoy:

  1. Prompt library de security review — no inventar el prompt cada vez. Tener 5-6 prompts probados que cambian el frame del modelo de formas distintas.
  2. Mandatory LLM audit en PR template — como hice, pero con prompts específicos, no "¿tiene problemas?"
  3. Categorizar por tipo de vulnerabilidad — usar LLMs para lo que son buenos (credenciales, XSS obvio, patrones conocidos) y SAST + human review para lógica de negocio.
  4. No asumir que el silencio es seguridad — documentar explícitamente qué auditaste, con qué tool, con qué scope.

El gap entre "encontrar" y "no cometer" es real. Yo me estaba mintiendo. Pero la mentira no era que los modelos son inútiles para seguridad — era que el modo en que los estaba usando era el equivocado.

La diferencia entre el plomero que arregla y el que rompe no es el plomero. Es quién lo está supervisando y qué le estás pidiendo que haga.

Eso lo puedo controlar. Y ahora lo hago.


Este artículo fue publicado originalmente en juanchi.dev

Top comments (0)