CodeQL 2.26.0 de GitHub añade una consulta integrada que detecta patrones de inyección de prompts de IA, y el cambio ya está provocando que los pipelines de CI marquen nuevos riesgos. La actualización por sí sola no es suficiente: los equipos necesitan una suite de pruebas de regresión que garantice que la regla siga siendo efectiva a medida que el código evoluciona.
Por qué es importante un fixture de regresión
La inyección de prompts permite que un atacante introduzca instrucciones maliciosas en un prompt que un modelo de lenguaje seguirá posteriormente. Con la nueva consulta, el análisis estático puede rastrear datos desde una fuente no confiable hasta un sink de llamada al modelo. Si la regla simplemente se activa y nunca se verifica, una refactorización posterior podría romper la ruta de flujo de datos y la alerta desaparecería silenciosamente. Un fixture de regresión captura las rutas exactas que deberían activar (o no activar) la regla, convirtiendo el resultado del análisis estático en un contrato que el build hace cumplir.
Los tres ingredientes de un fixture fiable
- Fuente no confiable – cualquier función que traiga datos desde fuera de la base de código confiable (por ejemplo, el cuerpo de un issue de GitHub, el payload de un webhook).
- Construcción del prompt – el código que ensambla la solicitud al modelo, normalmente una llamada a un SDK de cliente.
- Sink del modelo – el método del SDK que envía el prompt al modelo. El motor de flujo de datos de CodeQL necesita ver una llamada real desde su stack de producción para reconocer el sink.
La consulta solo se activa cuando los tres elementos están presentes.
Organización de los archivos de prueba
Un diseño convencional mantiene la suite fácil de auditar:
security-fixtures/prompt-injection/
├─ positive/
│ ├─ direct-flow.ts
│ └─ helper-flow.ts
├─ negative/
│ └─ trusted-instruction.ts
└─ expected-alerts.json
Los archivos positivos contienen código que debería ser marcado; los archivos negativos contienen patrones seguros que deben permanecer en silencio.
Escritura de los casos positivos
El ejemplo más sencillo muestra un flujo directo desde un valor no confiable hasta la llamada al modelo:
import { model } from "./supported-client";
declare function loadIssueBody(id: number): Promise<string>;
export async function summarize(id: number) {
const untrusted = await loadIssueBody(id);
return model.generate({
system: "Summarize the issue",
user: untrusted,
});
}
Aquí loadIssueBody es la fuente no confiable, model.generate es el sink, y los datos pasan sin ningún paso de sanitización, exactamente lo que la consulta está diseñada para detectar.
Un segundo caso positivo debe enrutar los datos a través de una función auxiliar, demostrando que el análisis sigue rutas indirectas:
function wrapUserInput(input: string) {
return { system: "Summarize the issue", user: input };
}
export async function summarizeViaHelper(id: number) {
const raw = await loadIssueBody(id);
return model.generate(wrapUserInput(raw));
}
Ambos archivos pertenecen a positive/.
Escritura del caso negativo
El fixture negativo debe demostrar que la entrada del usuario no puede alterar la instrucción del modelo. Un error común es asumir que una función llamada sanitize() garantiza la seguridad. El analizador estático no trata el nombre como una prueba, por lo que la prueba debe evitar cualquier stub de sanitización engañoso:
export async function safeSummarize(id: number) {
const trusted = "Summarize the issue";
const user = await loadIssueBody(id); // not used in the system prompt
return model.generate({
system: trusted,
user: "Static placeholder",
});
}
Debido a que los datos no confiables nunca llegan al campo system, la regla debería permanecer en silencio.
Declaración de expectativas en JSON
El contrato de la suite reside en expected-alerts.json. Enumera las alertas requeridas y las rutas explícitamente prohibidas:
{
"required": [
{
"ruleId": "USE_ACTUAL_RULE_ID",
"pathSuffix": "positive/direct-flow.ts"
},
{
"ruleId": "USE_ACTUAL_RULE_ID",
"pathSuffix": "positive/helper-flow.ts"
}
],
"forbiddenPathSuffixes": [
"negative/trusted-instruction.ts"
]
}
Reemplace USE_ACTUAL_RULE_ID con el identificador que se muestra en la documentación de CodeQL o en la salida SARIF. No adivine los IDs; la cadena exacta es importante para la comprobación de CI.
Integración del fixture en CI
- Fije la versión de la CLI de CodeQL utilizada en el pipeline a la 2.26.0 (o posterior).
- Construya una base de datos desechable a partir del checkout actual antes de ejecutar el fixture.
- Ejecute la consulta, capture las alertas y compá
