Codex y Claude Code pasaron por alto 9 de cada 10 errores reales en 200 casos de prueba. La configuración de SAST por IA más sólida detectó el 72% a un menor costo por vulnerabilidad encontrada. Probamos cinco configuraciones de IA contra los mismos 200 fragmentos de código: 100 vulnerabilidades reales y 100 señuelos que parecen riesgosos pero no lo son.
Tres ejecutaron el SAST por IA de Fluid Attacks en tres modelos diferentes. Los otros dos fueron agentes de codificación de propósito general, Codex y Claude Code, a los que se les dio una única instrucción detallada de auditoría de seguridad.
La lección más clara: el sistema alrededor del modelo importaba más que el modelo mismo. Los agentes de codificación fueron casi perfectos en precisión (casi sin falsas alarmas), pero casi ciegos en cobertura, pasando por alto más de 9 de cada 10 vulnerabilidades reales. El SAST por IA (gpt-5-mini) sacrificó parte de esa precisión por una cobertura mucho mayor, detectando el 72% de las vulnerabilidades reales a un costo de $16.92 por hallazgo, menos que cualquier otra configuración probada.
Agente de codificación frente a escáner de seguridad de IA: lo que queríamos saber
Los asistentes de codificación de IA y los escáneres de seguridad de IA se siguen agrupando en la misma categoría, sobre todo porque ambos se presentan como formas de detectar vulnerabilidades en el código real. No son el mismo tipo de herramienta, por lo que queríamos una respuesta directa: ¿qué tan diferente es su rendimiento en los mismos objetivos? Evaluamos cinco configuraciones en cuanto a precisión, exhaustividad (recall) y F1, utilizando señuelos diseñados para activar falsos positivos reales, no solo un número de "encontrado, sí o no".
Metodología
Todas las herramientas fueron expuestas al mismo conjunto de datos:
52 bases de código, 68 versiones, aproximadamente 98 millones de líneas de código en total.
200 fragmentos de código: 100 vulnerabilidades reales, 100 señuelos que parecen vulnerables pero no lo son.
Dos tipos de vulnerabilidades, divididas equitativamente: inyección SQL y scripting entre sitios (XSS), dos de las formas más comunes en que los atacantes explotan el código inseguro.
Tres configuraciones ejecutan el AI SAST de Fluid Attacks (el mismo pipeline, cambiando de modelo cada vez): gpt-5-mini, GPT-5.5 y Opus 4.8. Las otras dos son agentes de codificación a los que se les dio la misma instrucción de auditoría: Codex en GPT-5.5 y Claude Code en Claude Opus 4.7.
AI SAST no es el modelo. Es un pipeline fijo que extrae código, marca candidatos y rastrea el flujo de datos. Un sistema de agentes, que trabaja de manera coordinada, juzga qué rutas marcadas son realmente explotables, por lo que cambiar el modelo subyacente solo modifica ese paso. (Pipeline completo aquí.) Ese diseño plantea dos experimentos naturales, ya que Codex y Claude Code se ejecutan en modelos casi idénticos a dos de las tres configuraciones de AI SAST.
Ambos agentes recibieron una única instrucción para una auditoría de toda la base de código, reproducida íntegramente a continuación, para que no haya dudas de si tuvieron una oportunidad real:
You are auditing the codebase rooted at the current working directory fortwo specific vulnerability classes:1.SQL Injection -> subcategory="SQL Injection"2.Cross-Site Scripting / HTML injection -> subcategory="Cross-Site Scripting"You have full sandbox access. Useshell commands(grep,find,ls,cat),read files,take notes,reasonas much as you need. Thereis no turn limit.
Recommendedapproach—-----
1.Identify the language(s)and framework(s)used by thisrepo(look atmanifest files:package.json,pom.xml,requirements.txt,Gemfile,composer.json,go.mod,Cargo.toml,build.gradle,pyproject.toml).
2.Enumerate the typical SQL and XSS sinks forthat stack. Examples:
Python:cursor.execute(f"..."),Django .raw(), .extra(where=...),Jinja2 |safe,mark_safe(),format_html
Java:Statement.execute(...),createQuery/createNativeQuery withstring concatenation,JSP <%= ... %>,response.getWriter().printJS/TS:knex.raw(...),pg/mysql query templates,res.send/res.writewithunescaped input,innerHTML/dangerouslySetInnerHTML
Ruby:ActiveRecord find_by_sql / where("..."+x),html_safe,raw(),<%== %> (Slim/ERB unescaped)
PHP: mysql_query("... $..."), echo $_GET[...], unescaped Twig
Go: db.Query/Exec with fmt.Sprintf, html/template vs text/template
3. For each candidate sink, trace data flow BACKWARD to a user-controlled
source (HTTP params, query string, request body, headers, cookies, file
uploads). A finding requires BOTH a vulnerable sink AND a reachable
user-controlled source. Otherwise it's not a vulnerability.
4. Ignore other classes (NoSQL injection, command injection, SSRF, weak
crypto, deserialization, path traversal, auth flaws, etc.). Even if you
spot them, do not include them in your output.
Output
—---
When your audit is complete, WRITE your findings to a file named
`{SCANNER_OUTPUT_FILENAME}` at the working-directory root. The file must
contain a single JSON object:
{"findings": [
{"path": "<repo-relativepath>",
"line_start": <int>,
"line_end": <int|null>,
"subcategory": "SQL Injection" | "Cross-Site Scripting",
"severity": "low" | "medium" | "high" | "critical",
"message": "<one-sentencejustification>"
}
],
"summary": "<paragraphdescribingwhatyouauditedandyourconfidence>",
"scanner_self_confidence": "low" | "medium" | "high"
}
Example finding object:
{"path": "src/users/views.py", "line_start": 42, "line_end": 44,
"subcategory": "SQL Injection", "severity": "high",
"message": "User-controlled `username` from request.GET is interpolated
into raw SQL via cursor.execute(f\"SELECT...\"); attacker
can break out of the string."}
If you find no SQL Injection / Cross-Site Scripting vulnerabilities, still
write the file with an empty findings list:
{"findings": [], "summary": "...why...",
"scanner_self_confidence": "high"}
The subcategory field MUST be exactly "SQL Injection" or "Cross-Site Scripting";
any other value will be discarded. Paths must be relative to the working
directory.
Your stdout (reasoning, shell-command output, anything else) is informational
and will be ignored. Only the contents of `{SCANNER_OUTPUT_FILENAME}
You are auditing the codebase rooted at the current working directory fortwo specific vulnerability classes:1.SQL Injection -> subcategory="SQL Injection"2.Cross-Site Scripting / HTML injection -> subcategory="Cross-Site Scripting"You have full sandbox access. Useshell commands(grep,find,ls,cat),read files,take notes,reasonas much as you need. Thereis no turn limit.
Recommendedapproach—-----
1.Identify the language(s)and framework(s)used by thisrepo(look atmanifest files:package.json,pom.xml,requirements.txt,Gemfile,composer.json,go.mod,Cargo.toml,build.gradle,pyproject.toml).
2.Enumerate the typical SQL and XSS sinks forthat stack. Examples:
Python:cursor.execute(f"..."),Django .raw(), .extra(where=...),Jinja2 |safe,mark_safe(),format_html
Java:Statement.execute(...),createQuery/createNativeQuery withstring concatenation,JSP <%= ... %>,response.getWriter().printJS/TS:knex.raw(...),pg/mysql query templates,res.send/res.writewithunescaped input,innerHTML/dangerouslySetInnerHTML
Ruby:ActiveRecord find_by_sql / where("..."+x),html_safe,raw(),<%== %> (Slim/ERB unescaped)
PHP: mysql_query("... $..."), echo $_GET[...], unescaped Twig
Go: db.Query/Exec with fmt.Sprintf, html/template vs text/template
3. For each candidate sink, trace data flow BACKWARD to a user-controlled
source (HTTP params, query string, request body, headers, cookies, file
uploads). A finding requires BOTH a vulnerable sink AND a reachable
user-controlled source. Otherwise it's not a vulnerability.
4. Ignore other classes (NoSQL injection, command injection, SSRF, weak
crypto, deserialization, path traversal, auth flaws, etc.). Even if you
spot them, do not include them in your output.
Output
—---
When your audit is complete, WRITE your findings to a file named
`{SCANNER_OUTPUT_FILENAME}` at the working-directory root. The file must
contain a single JSON object:
{"findings": [
{"path": "<repo-relativepath>",
"line_start": <int>,
"line_end": <int|null>,
"subcategory": "SQL Injection" | "Cross-Site Scripting",
"severity": "low" | "medium" | "high" | "critical",
"message": "<one-sentencejustification>"
}
],
"summary": "<paragraphdescribingwhatyouauditedandyourconfidence>",
"scanner_self_confidence": "low" | "medium" | "high"
}
Example finding object:
{"path": "src/users/views.py", "line_start": 42, "line_end": 44,
"subcategory": "SQL Injection", "severity": "high",
"message": "User-controlled `username` from request.GET is interpolated
into raw SQL via cursor.execute(f\"SELECT...\"); attacker
can break out of the string."}
If you find no SQL Injection / Cross-Site Scripting vulnerabilities, still
write the file with an empty findings list:
{"findings": [], "summary": "...why...",
"scanner_self_confidence": "high"}
The subcategory field MUST be exactly "SQL Injection" or "Cross-Site Scripting";
any other value will be discarded. Paths must be relative to the working
directory.
Your stdout (reasoning, shell-command output, anything else) is informational
and will be ignored. Only the contents of `{SCANNER_OUTPUT_FILENAME}
You are auditing the codebase rooted at the current working directory fortwo specific vulnerability classes:1.SQL Injection -> subcategory="SQL Injection"2.Cross-Site Scripting / HTML injection -> subcategory="Cross-Site Scripting"You have full sandbox access. Useshell commands(grep,find,ls,cat),read files,take notes,reasonas much as you need. Thereis no turn limit.
Recommendedapproach—-----
1.Identify the language(s)and framework(s)used by thisrepo(look atmanifest files:package.json,pom.xml,requirements.txt,Gemfile,composer.json,go.mod,Cargo.toml,build.gradle,pyproject.toml).
2.Enumerate the typical SQL and XSS sinks forthat stack. Examples:
Python:cursor.execute(f"..."),Django .raw(), .extra(where=...),Jinja2 |safe,mark_safe(),format_html
Java:Statement.execute(...),createQuery/createNativeQuery withstring concatenation,JSP <%= ... %>,response.getWriter().printJS/TS:knex.raw(...),pg/mysql query templates,res.send/res.writewithunescaped input,innerHTML/dangerouslySetInnerHTML
Ruby:ActiveRecord find_by_sql / where("..."+x),html_safe,raw(),<%== %> (Slim/ERB unescaped)
PHP: mysql_query("... $..."), echo $_GET[...], unescaped Twig
Go: db.Query/Exec with fmt.Sprintf, html/template vs text/template
3. For each candidate sink, trace data flow BACKWARD to a user-controlled
source (HTTP params, query string, request body, headers, cookies, file
uploads). A finding requires BOTH a vulnerable sink AND a reachable
user-controlled source. Otherwise it's not a vulnerability.
4. Ignore other classes (NoSQL injection, command injection, SSRF, weak
crypto, deserialization, path traversal, auth flaws, etc.). Even if you
spot them, do not include them in your output.
Output
—---
When your audit is complete, WRITE your findings to a file named
`{SCANNER_OUTPUT_FILENAME}` at the working-directory root. The file must
contain a single JSON object:
{"findings": [
{"path": "<repo-relativepath>",
"line_start": <int>,
"line_end": <int|null>,
"subcategory": "SQL Injection" | "Cross-Site Scripting",
"severity": "low" | "medium" | "high" | "critical",
"message": "<one-sentencejustification>"
}
],
"summary": "<paragraphdescribingwhatyouauditedandyourconfidence>",
"scanner_self_confidence": "low" | "medium" | "high"
}
Example finding object:
{"path": "src/users/views.py", "line_start": 42, "line_end": 44,
"subcategory": "SQL Injection", "severity": "high",
"message": "User-controlled `username` from request.GET is interpolated
into raw SQL via cursor.execute(f\"SELECT...\"); attacker
can break out of the string."}
If you find no SQL Injection / Cross-Site Scripting vulnerabilities, still
write the file with an empty findings list:
{"findings": [], "summary": "...why...",
"scanner_self_confidence": "high"}
The subcategory field MUST be exactly "SQL Injection" or "Cross-Site Scripting";
any other value will be discarded. Paths must be relative to the working
directory.
Your stdout (reasoning, shell-command output, anything else) is informational
and will be ignored. Only the contents of `{SCANNER_OUTPUT_FILENAME}
You are auditing the codebase rooted at the current working directory fortwo specific vulnerability classes:1.SQL Injection -> subcategory="SQL Injection"2.Cross-Site Scripting / HTML injection -> subcategory="Cross-Site Scripting"You have full sandbox access. Useshell commands(grep,find,ls,cat),read files,take notes,reasonas much as you need. Thereis no turn limit.
Recommendedapproach—-----
1.Identify the language(s)and framework(s)used by thisrepo(look atmanifest files:package.json,pom.xml,requirements.txt,Gemfile,composer.json,go.mod,Cargo.toml,build.gradle,pyproject.toml).
2.Enumerate the typical SQL and XSS sinks forthat stack. Examples:
Python:cursor.execute(f"..."),Django .raw(), .extra(where=...),Jinja2 |safe,mark_safe(),format_html
Java:Statement.execute(...),createQuery/createNativeQuery withstring concatenation,JSP <%= ... %>,response.getWriter().printJS/TS:knex.raw(...),pg/mysql query templates,res.send/res.writewithunescaped input,innerHTML/dangerouslySetInnerHTML
Ruby:ActiveRecord find_by_sql / where("..."+x),html_safe,raw(),<%== %> (Slim/ERB unescaped)
PHP: mysql_query("... $..."), echo $_GET[...], unescaped Twig
Go: db.Query/Exec with fmt.Sprintf, html/template vs text/template
3. For each candidate sink, trace data flow BACKWARD to a user-controlled
source (HTTP params, query string, request body, headers, cookies, file
uploads). A finding requires BOTH a vulnerable sink AND a reachable
user-controlled source. Otherwise it's not a vulnerability.
4. Ignore other classes (NoSQL injection, command injection, SSRF, weak
crypto, deserialization, path traversal, auth flaws, etc.). Even if you
spot them, do not include them in your output.
Output
—---
When your audit is complete, WRITE your findings to a file named
`{SCANNER_OUTPUT_FILENAME}` at the working-directory root. The file must
contain a single JSON object:
{"findings": [
{"path": "<repo-relativepath>",
"line_start": <int>,
"line_end": <int|null>,
"subcategory": "SQL Injection" | "Cross-Site Scripting",
"severity": "low" | "medium" | "high" | "critical",
"message": "<one-sentencejustification>"
}
],
"summary": "<paragraphdescribingwhatyouauditedandyourconfidence>",
"scanner_self_confidence": "low" | "medium" | "high"
}
Example finding object:
{"path": "src/users/views.py", "line_start": 42, "line_end": 44,
"subcategory": "SQL Injection", "severity": "high",
"message": "User-controlled `username` from request.GET is interpolated
into raw SQL via cursor.execute(f\"SELECT...\"); attacker
can break out of the string."}
If you find no SQL Injection / Cross-Site Scripting vulnerabilities, still
write the file with an empty findings list:
{"findings": [], "summary": "...why...",
"scanner_self_confidence": "high"}
The subcategory field MUST be exactly "SQL Injection" or "Cross-Site Scripting";
any other value will be discarded. Paths must be relative to the working
directory.
Your stdout (reasoning, shell-command output, anything else) is informational
and will be ignored. Only the contents of `{SCANNER_OUTPUT_FILENAME}
La comparación se mantiene honesta en cuatro aspectos. Los fallos son vulnerabilidades (CVE) públicas en el código de terceros. Nada fue adaptado a este conjunto de datos. El alcance se bloqueó antes de ejecutar cualquier análisis. Y un acierto solo cuenta si la herramienta identifica el archivo, la función y el tipo de vulnerabilidad correctos.
Resultados
La siguiente tabla evalúa las cinco configuraciones (las tres de AI SAST de Fluid Attacks y los dos agentes de codificación) frente al mismo punto de referencia.
Métrica
AI SAST (gpt-5-mini)
Codex
Claude Code
AI SAST (5.5)
AI SAST (Opus 4.8)
Verdaderos positivos
72
9
8
19
47
Falsos positivos
66
2
0
14
30
Verdaderos negativos
34
98
100
86
70
Falsos negativos
28
91
92
81
53
Exhaustividad (Recall)
0.72
0.09
0.08
0.19
0.47
Precisión
0.52
0.82
1
0.58
0.61
F0.5 (ponderado por precisión)
0.55
0.31
0.3
0.41
0.58
Puntuación F1
0.61
0.16
0.15
0.29
0.53
F2 (ponderado por exhaustividad)
0.67
0.11
0.1
0.22
0.49
Especificidad
0.34
0.98
1
0.86
0.7
Tasa de falsos positivos (FPR)
0.66
0.02
0
0.14
0.3
Exactitud (Accuracy)
0.53
0.535
0.54
0.525
0.585
Costo total
$1,218
$359
$554
$4,689*
$4,264*
Costo por vulnerabilidad encontrada
$16.92
$39.92
$69.21
$246.80*
$90.72*
Tiempo de ejecución (min)
85.4
5.1
15.5
n/a
n/a
*Costo estimado, proyectado a partir de una ejecución dirigida; verificado dentro de aproximadamente un 1% de una línea base.
La precisión y la exhaustividad tiraron en direcciones opuestas. Claude Code nunca generó una falsa alarma (precisión de 1.00), pero omitió 92 de 100 vulnerabilidades reales; Codex omitió 91 a pesar de una precisión de 0.82. AI SAST (gpt-5-mini) detectó 72 de 100, más que cualquier otra configuración, pero fue engañado por 66 de los 100 señuelos, quedando con una precisión de 0.52. Un extremo ofrece alarmas casi perfectas a costa de una ceguera casi total; el otro, una cobertura mayor a cambio de una carga de revisión más pesada.
La exactitud oculta más de lo que revela aquí, porque el conjunto de datos está equilibrado al 50/50. Todas las herramientas se situaron entre 0.525 y 0.585, lo que hace que las cinco parezcan intercambiables, cuando no lo son. Una herramienta que etiqueta casi todo como "no es una vulnerabilidad" obtiene una puntuación cercana a 0.5 por defecto, similar a Codex y Claude Code. El F1 separa el campo de manera más honesta: 0.61 para gpt-5-mini y 0.53 para Opus 4.8, frente a solo 0.15–0.16 para los agentes de codificación.
Gastar más no garantizó una mejor detección. AI SAST (gpt-5-mini) fue el más barato y efectivo, con un costo de $16.92 por vulnerabilidad real encontrada. AI SAST (GPT-5.5), un modelo más costoso en el mismo sistema, tuvo un costo estimado de $246.80 por vulnerabilidad (catorce veces más), mientras que quedó rezagado en exhaustividad y F1.
El scripting entre sitios (XSS) fue casi un punto ciego total para los agentes de codificación: de 50 casos reales de XSS, Claude Code no encontró ninguno y Codex encontró solo 2; AI SAST (GPT-5.5) detectó solo 3. Solo gpt-5-mini (40 de 50) y Opus 4.8 (21 de 50) manejaron el XSS de manera significativa, lo que demuestra que una cifra alta de exhaustividad puede ocultar un fallo casi total en un tipo de vulnerabilidad.
El sistema, no el modelo, impulsó la detección
Claude Code y AI SAST (Opus 4.8) se ejecutan casi con el mismo modelo. Solo, encontró 8 de 100 vulnerabilidades reales; dentro de AI SAST, 47, lo que representa una ganancia de 5.9 veces (p < 10⁻⁹). La pareja con GPT-5.5 cuenta la misma historia: Codex solo encontró 9 de 100; AI SAST (GPT-5.5) encontró 19, una ganancia de 2.1 veces (p ≈ 0.04).
Ya habíamos llegado a una conclusión similar al analizar Claude Code de forma individual: los resultados se debían a la ingeniería en torno al modelo, no al modelo en sí. Este punto de referencia apunta en la misma dirección: gpt-5-mini, el modelo más pequeño y económico, ganó en exhaustividad y F1, por delante de ambos modelos más grandes.
Conclusiones clave
Elegir entre estas herramientas depende del tipo de omisión con el que su equipo pueda convivir. Los agentes de codificación que priorizan la precisión casi nunca dan falsas alarmas, pero omiten la mayoría de los problemas reales, lo que los hace poco adecuados como escáner principal. Los equipos que desean un asistente de codificación con contexto de seguridad integrado están mejor atendidos por una herramienta diseñada para ese fin, como AI Code Security Assistance. Si omitir una vulnerabilidad representa el mayor riesgo, las configuraciones de AI SAST con mayor exhaustividad sacan a la luz muchas más vulnerabilidades reales, y el AI SAST de Fluid Attacks con gpt-5-mini lo hace de la forma más económica, aunque con una cola de revisión más pesada.
Una herramienta que no encuentra casi nada no parece rota, parece tranquilizadora. Claude Code omitió 92 de 100 vulnerabilidades reales aquí y aun así entregó un informe limpio, sin falsas alarmas que tener que justificar. Ese es el costo real de un escáner con exhaustividad cercana a cero: no los hallazgos en los que se equivoca, sino los que nunca menciona. Cualquiera que sea el perfil que se adapte a su tolerancia al riesgo, primero pruébelo y adminístrelo.
Una nota final: la ejecución que se autoexcluyó
Una configuración nunca llegó a los resultados finales: Claude Code en Fable 5. Fable 5 rechazó de plano la tarea de ciberseguridad y, en su lugar, delegó el trabajo en Opus 4.8. Al no haber nada que calificar, lo dejamos fuera, un recordatorio de que la disposición de un modelo para intentar una tarea es en sí misma una variable, previa a la precisión y la exhaustividad.
¿Quieres ver cómo funciona AI SAST en tu propio código? Explora AI Security o descubre cómo se gestionan los hallazgos desde su detección hasta su solución en nuestra plataforma.
Las soluciones de Fluid Attacks permiten a las organizaciones identificar, priorizar y remediar vulnerabilidades en su software a lo largo del SDLC. Con el apoyo de la IA, herramientas automatizadas y pentesters, Fluid Attacks acelera la mitigación de la exposición al riesgo de las empresas y fortalece su postura de ciberseguridad.
Las soluciones de Fluid Attacks permiten a las organizaciones identificar, priorizar y remediar vulnerabilidades en su software a lo largo del SDLC. Con el apoyo de la IA, herramientas automatizadas y pentesters, Fluid Attacks acelera la mitigación de la exposición al riesgo de las empresas y fortalece su postura de ciberseguridad.
Las soluciones de Fluid Attacks permiten a las organizaciones identificar, priorizar y remediar vulnerabilidades en su software a lo largo del SDLC. Con el apoyo de la IA, herramientas automatizadas y pentesters, Fluid Attacks acelera la mitigación de la exposición al riesgo de las empresas y fortalece su postura de ciberseguridad.