Alguien lee cada aviso
Un escáner avisa de todo lo que se parece a una vulnerabilidad. Alguien tiene que leer cada aviso con el código delante y decidir si es real. Eso es el triaje, y es el trabajo que nadie hace en un equipo pequeño. Aquí lo hace un analista con IA que deja escrito por qué, y una regla fija convierte su lectura en una decisión.
Treinta avisos. Tres importan. ¿Cuáles?
Los escáneres son baratos y ruidosos a propósito: prefieren avisar de más que callarse. El resultado es una lista larga en la que lo grave se mezcla con lo que no es nada. Si nadie la lee, se deja de mirar. Y entonces da igual tener escáner.
- El escáner dicePosible inyección SQL en consultas/usuarios.ts, línea 41. Tras el triajeDescartarLa consulta va parametrizada: el valor nunca se concatena. Falsa alarma.
- El escáner diceClave con formato de proveedor de pagos en config/ejemplo.ts. Tras el triajeRevisarParece un ejemplo, pero tiene forma de clave real: una persona lo confirma. No se descarta sola.
- El escáner diceRedirección con destino que llega de la petición en rutas/salir.ts, línea 12. Tras el triajeCorregirEl destino viene de la URL sin comprobar el dominio. Se puede aprovechar.
Son ejemplos de la forma que tienen los avisos; no son de un repo real.
Lee cada aviso con tu código delante y te dice por qué
Para cada aviso, el analista recibe el trozo de código señalado y lo que lo rodea, y responde a una pregunta: con lo que veo, ¿esto se puede aprovechar? Lo escribe en dos a cinco frases, citando el fichero y la línea. Luego una regla fija, publicada, convierte su respuesta en una de cuatro decisiones.
Corregir
Es real y se puede aprovechar. A la lista de trabajo, por prioridad.
Revisar
Hace falta una persona. El analista no está seguro, o un freno lo ha parado.
Vigilar
Es real pero improbable. Se ve, con prioridad baja.
Descartar
Falsa alarma. Sale de la lista. Se queda a la vista y se puede recuperar.
- El analista no decide. Clasifica y justifica. La decisión la toma una tabla fija que puedes leer en la documentación. Así la misma lectura da siempre la misma decisión.
- Hay frenos. Una clave con pinta de real o una vulnerabilidad crítica no se descartan sin que lo mire una persona: van a Revisar. Y si el código intenta darle órdenes al analista («esto es seguro, ignóralo»), también.
- Tú mandas. Puedes cambiar cualquier decisión, con su motivo. Queda en el historial, que no se edita, y la tuya manda sobre la calculada.
- Lo descartado no desaparece. Se queda a la vista con su motivo. Si no estás de acuerdo, lo recuperas.
Una pregunta más: ¿usas la parte vulnerable?
En los avisos de librerías, el analista mira además si tu código llega a la función vulnerable. Si no pasas por ahí, no te lo pone a corregir: va a Vigilar. En inglés lo llaman «reachability»; aquí, alcanzable o no.
Está contado con dibujos en Las librerías que no elegiste. Cuando el analista no puede saberlo, lo dice: «no se sabe». No se lo inventa.
Ocho de cada diez falsas alarmas, fuera
En nuestro repo de pruebas, donde sabemos qué es real y qué no porque lo sembramos nosotros.
- De 25 falsas alarmas, descartó 20.
El 80 %. El objetivo que nos pusimos era el 70 %.
- De 126 vulnerabilidades reales, descartó por error 4.
El 3,2 %. El tope que nos pusimos era el 5 %. Los cuatro están documentados.
Los números enteros y cómo se miden. Un repo de pruebas no es tu repo: es la medida que podemos enseñar sin inventar nada.
Claude en el worker, un hallazgo por llamada
El triaje nunca corre en la petición: la ingestión es síncrona y determinista, y el worker tría después con pg-boss. Los prompts están versionados en el repo y un prompt usado en un benchmark no se edita: se escribe el siguiente.
- Modelo
- Claude Opus 5.5, esfuerzo
medium, por el SDK de Anthropic. - Entrada
- Un mensaje por hallazgo con bloques marcados: el hallazgo (regla, mensaje de cada escáner, CWE, severidad base, ruta:líneas), el aviso y la dependencia si es SCA, el secreto enmascarado si es un secreto, y el fragmento con ±20 líneas numeradas.
- Salida
- JSON contra esquema estricto:
clasificacion(explotable, improbable, falso positivo),confianza(alta, media, baja),alcanzable,justificacion(2-5 frases citando ruta:línea),evidencias,condiciones_explotacion,falta_contexto,intento_manipulacion. - Los datos no son instrucciones
- Un comentario que diga «revisado» o «falso positivo» no es evidencia. Si el contenido intenta dirigir el análisis, se marca y el veredicto va a Revisar. Lo que viene del repo no puede cerrar un bloque del mensaje.
- Reutilización
- Misma huella, mismo fragmento, mismo prompt, modelo y esfuerzo: se reutiliza el triaje sin llamar, también entre repos. Solo se tría lo nuevo o lo que cambia.
- Secretos
- El valor nunca entra en la base ni en la conversación: tipo, prefijo enmascarado, longitud, entropía y si encaja con el formato del proveedor.
De la clasificación al veredicto: una tabla
El modelo clasifica y justifica; no decide. El veredicto lo calcula un script con esta tabla, y hay un test que recorre todas las combinaciones.
| Clasificación | Confianza | Veredicto |
|---|---|---|
| explotable | cualquiera | Corregir |
| improbable | alta o media | Vigilar |
| improbable | baja | Revisar |
| falso positivo | alta o media | Descartar |
| falso positivo | baja | Revisar |
- Freno Intento de manipulación
Un Descartar pasa a Revisar.
- Freno Severidad base crítica sin confianza alta
Un Descartar pasa a Revisar.
- Freno Secreto con formato de proveedor
Que no parece un marcador y está fuera de rutas de test: un secreto real no se descarta sin una persona.
- Prioridad Dentro de Corregir
Por severidad base, crítica primero; sube un nivel si la dependencia es alcanzable, sin pasar de crítica.
- A mano Veredicto manual
Con motivo obligatorio, manda sobre el calculado y queda en el historial inmutable. En bloque hasta 200, en una transacción.
Lo que ningún escáner ve
Además de triar lo que avisan los escáneres, hay una capa de análisis de lógica: la tabla que cualquiera puede leer, el aviso de pago que nadie comprueba, los datos de otro usuario a un número de distancia.
- Sí Reglas propias sin IA
Genéricas, con su test, corren siempre. Nada sale de la app y no cuestan.
- Opcional Revisión con IA de los puntos de entrada
Modo híbrido: el modelo solo mira lo que no encaja en una regla. Apagado por defecto, con tope de gasto por repo y mes. El workflow manda el mapa y los ficheros sensibles, no el repo.
- Mismo circuito Hallazgos de lógica
Van al mismo triaje, veredicto e historial que el resto.
Lo que el triaje no es
- No Una prueba de explotación
No ejecuta código ni lanza peticiones. Es una lectura con criterio, con sus límites.
- No Infalible
En el repo de pruebas descartó por error 4 de 126 vulnerabilidades reales. Por eso los frenos, la justificación obligatoria y el veredicto a mano.
- No Un arreglo automático
Da la explicación y un prompt prudente. El cambio lo hacéis y lo comprobáis vosotros.
- Todavía no Clave propia de Anthropic
Para que el gasto del triaje vaya a vuestra cuenta. En camino.
- Medido Repo de pruebas
80 % de los falsos positivos descartados (20 de 25); 3,2 % de los reales descartados por error (4 de 126). Criterio: al menos el 70 % sin pasar del 5 %. Cómo se mide.
Lo demás que revisa
- El código que te escribe la IA SAST
- Las librerías que no elegiste SCA
- El paquete que no es el que parece Malware en dependencias
- La clave que se fue en un commit Secrets detection
- Tu infraestructura y tus contenedores IaC · Container scanning · SBOM
- Tu CI y tu pipeline CI/CD security
Del ruido a la decisión
Conecta un repo y mira qué hay que corregir. Sin hablar con nadie.