Hay un patrón que vemos una y otra vez en auditorías: un equipo despliega contenedores durante meses, todo funciona, y la primera vez que alguien se entera de que una imagen tiene una CVE crítica y activamente explotada es cuando el equipo de seguridad del cliente corre su propio escáner, o peor, después de un incidente. El escaneo de vulnerabilidades existía todo ese tiempo. Simplemente se ejecutaba demasiado tarde para importar.
Shift-left security significa mover ese escaneo de "cuando a alguien se le ocurra revisar" a "cada vez que cambia el código", y ejecutarlo en CI en lugar de contra un registry que nadie vigila. La idea no es nueva, pero Trivy es la herramienta que la ha hecho realmente práctica: es rápida, es un único binario estático y cubre mucho más terreno del que la gente espera cuando escucha "escáner de contenedores". Así lo integramos realmente en los pipelines de nuestros clientes.
Por qué escanear en CI en lugar de enterarte en runtime
El enfoque tradicional de seguridad de imágenes es escanear el registry periódicamente, o depender del escáner integrado del proveedor cloud para que avise después de que la imagen ya se ha subido y posiblemente ya está desplegada. Para cuando ese reporte llega, la imagen ya pasó por code review, por el pipeline de build y, muy a menudo, por un despliegue en producción. Arreglar la vulnerabilidad ahora implica una PR nueva, un build nuevo, un deploy nuevo y una explicación de por qué no se detectó antes.
Escanear en CI, justo después de construir la imagen, mueve ese mismo hallazgo a un punto donde corregirlo cuesta casi nada. El desarrollador que introdujo la dependencia vulnerable sigue mirando esa PR. El contexto sigue fresco. Y lo más importante: la imagen nunca se sube al registry ni se despliega en ningún sitio si el pipeline bloquea el build, que es el objetivo real del shift-left: no "detectar más rápido", sino "evitar que llegue a producción".
Qué escanea Trivy en realidad (no es solo un escáner de imágenes)
El malentendido más frecuente que nos encontramos con clientes es pensar que Trivy es un escáner de imágenes de contenedor y punto. Empezó ahí, pero ha crecido hasta convertirse en una única herramienta que cubre la mayor parte de lo que necesita un pipeline con conciencia de seguridad, y precisamente por eso merece la pena estandarizar en torno a ella en lugar de ensamblar cuatro herramientas distintas.
Paquetes de sistema operativo y dependencias de lenguaje
Dentro de una imagen de contenedor, Trivy inspecciona tanto la base de datos del gestor de paquetes del sistema operativo (apt, yum, apk, según la imagen base) como los manifiestos de dependencias de lenguaje horneados en las capas — un package-lock.json, un go.sum, un requirements.txt, un Gemfile.lock. Cruza todo lo que encuentra contra su base de datos de vulnerabilidades y reporta CVEs tanto de la capa del sistema operativo como del árbol de dependencias real de tu aplicación, algo importante porque la mayoría de equipos solo piensan en uno de los dos.
Configuraciones inseguras en Terraform, Dockerfiles y manifiestos de Kubernetes
Esta es la parte que suele sorprender: Trivy tiene un modo completo de escaneo de IaC y configuración que no tiene nada que ver con imágenes de contenedor. Lee ficheros Terraform, Dockerfiles, manifiestos de Kubernetes, charts de Helm y plantillas de CloudFormation, y marca configuraciones inseguras contra un conjunto de políticas integradas — cosas como un Dockerfile sin instrucción USER que termina ejecutándose como root, o un bucket S3 definido en Terraform sin cifrado configurado.
Secretos horneados en imágenes o commiteados en el repo
Trivy también ejecuta un escaneo de secretos, buscando patrones que coinciden con claves de acceso de AWS, cabeceras de claves privadas, tokens de API y credenciales similares, tanto dentro de las capas del filesystem de una imagen construida como en los ficheros de un repositorio git. Esto detecta el error clásico de un fichero .env o un token hardcodeado que termina con un COPY dentro de una imagen durante un build apresurado, o commiteado en una rama que nunca se limpió.
Generación de SBOM
Además de todo esto, Trivy puede generar un Software Bill of Materials de una imagen o un filesystem, en formato CycloneDX o SPDX. Para clientes en sectores regulados esto ha dejado de ser un extra agradable — es un requisito de procurement, y generarlo como subproducto del mismo escaneo que ya ejecutas en CI es mucho menos doloroso que montar después una herramienta de SBOM separada.
GitHub Actions: escanear justo después del build
El patrón que usamos en casi todos los pipelines de GitHub Actions es el mismo: construir la imagen, etiquetarla, y ejecutar inmediatamente trivy image contra ese tag exacto usando la acción oficial aquasecurity/trivy-action, antes de que la imagen llegue siquiera a subirse al registry. El step toma la referencia de la imagen como entrada y ejecuta el mismo escaneo que obtendrías en local, solo que como parte del workflow.
A partir de ahí, los equipos suelen querer uno de dos resultados, y la mayoría de pipelines terminan haciendo ambos. El primero es bloquear el build directamente cuando aparece algo severo, lo que la acción hace fijando --severity a los niveles que te importan y --exit-code 1 para que el step devuelva un estado distinto de cero y el job se detenga. El segundo es subir los resultados como un reporte SARIF — fijando format: sarif en el escaneo y pasando el fichero de salida a github/codeql-action/upload-sarif — para que los hallazgos aparezcan de forma nativa en la pestaña Security del repositorio y se muestren como anotaciones directamente en la pull request, en lugar de quedarse solo en un log de CI que alguien tiene que ir a buscar.
trivy image termina con código 0 incluso cuando encuentra vulnerabilidades críticas. Si no fijas --exit-code explícitamente, el escaneo se ejecuta, el reporte se imprime y el build pasa igualmente. Hemos visto más de un setup que "tenía Trivy en el pipeline" y en realidad no hacía nada por culpa de esto.GitLab CI: la misma idea, otro envoltorio
En GitLab el patrón de fondo es el mismo con otra forma. GitLab incluye un template nativo de Container Scanning en su etapa Secure que ejecuta Trivy por debajo contra la imagen que acabas de construir, y escribe sus hallazgos en el formato propio del Security Dashboard de GitLab. Si un cliente prefiere control más directo, a menudo ejecutamos directamente el contenedor aquasec/trivy como un job del pipeline — se trae la imagen construida en el stage anterior, se escanea y se bloquea el pipeline con el exit code exactamente igual que en GitHub Actions. En cualquier caso, el principio no cambia: escanear inmediatamente después del build, antes de que la imagen se acerque siquiera a un registry del que tiran otros entornos.
Afinar exit codes y severidad para que el gate no se ignore
La forma más rápida de matar la credibilidad de un gate de seguridad es hacer que falle por todo. Un escaneo crudo de trivy image contra casi cualquier imagen base del mundo real devuelve docenas de hallazgos, la mayoría de severidad LOW o MEDIUM en paquetes que nadie va a parchear este sprint. Si el build falla por todo ese ruido, la primera reacción del equipo no es arreglar las vulnerabilidades: es buscar la forma de saltarse el check.
La solución es afinar tres flags hasta que el gate sea realmente útil. --severity CRITICAL,HIGH reduce los hallazgos que pueden bloquear el build a los que de verdad merece la pena bloquear un release por ellos. --exit-code 1 hace que el job falle cuando aparecen esos hallazgos, en lugar del comportamiento por defecto de pasar igualmente. Y --ignore-unfixed filtra las CVEs que todavía no tienen un parche publicado — porque bloquear un build por una vulnerabilidad que nadie puede arreglar ahora mismo no hace el software más seguro, solo le enseña al equipo a tratar un pipeline en rojo como algo normal y empezar a ignorarlo.
Para el puñado de hallazgos que de verdad necesitan una excepción — una CVE en una dependencia que todavía no puedes actualizar, con un motivo documentado — un fichero .trivyignore con el ID de la CVE y un comentario explicando el porqué mantiene esa decisión explícita y revisable en lugar de enterrada en la memoria de alguien.
Escanear la infraestructura antes de que se aplique nada
El modo de escaneo de IaC es donde Trivy se gana el puesto fuera del mundo de contenedores. Ejecutar trivy config contra un directorio de Terraform o un Dockerfile detecta el tipo de error que es caro una vez en producción y casi gratis de arreglar durante el review de la PR: un Dockerfile que nunca declara un USER y ejecuta silenciosamente el contenedor como root, un bucket S3 definido en Terraform sin cifrado del lado del servidor, un security group abierto a 0.0.0.0/0 en un puerto que no debería, o un manifiesto de Kubernetes pidiendo un contenedor privilegiado sin límites de recursos.
La diferencia clave respecto al escaneo de imágenes es el momento: esto se ejecuta contra ficheros fuente, no artefactos construidos. Puedes escanear un plan de Terraform antes de que nadie ejecute terraform apply, o un Dockerfile antes de que se construya nunca en una imagen, detectando la configuración insegura en el momento en que se escribió en lugar de después de que ya haya aprovisionado infraestructura real o enviado un contenedor vulnerable.
Trivy Operator: escaneo continuo dentro del clúster
Todo lo anterior ocurre en un único punto en el tiempo: el momento en que corre el pipeline. Eso deja un hueco real — una imagen puede estar completamente limpia cuando se escanea y se despliega, y tres semanas después puede publicarse una CVE nueva contra una de sus dependencias. Nada en el pipeline de CI va a volver a dispararse para una imagen que ya se envió.
Ese es el problema que resuelve el Trivy Operator, y es una preocupación genuinamente distinta del escaneo en tiempo de CI, no una copia redundante de este. Desplegado dentro del clúster, vigila las cargas de trabajo que realmente están corriendo, escanea las imágenes detrás de ellas contra una base de datos de vulnerabilidades actualizada de forma periódica, y escribe los resultados como recursos personalizados VulnerabilityReport que puedes consultar con kubectl o alimentar a un dashboard. Es la única capa de todo este montaje que detecta vulnerabilidades publicadas después del despliegue, en imágenes que estaban genuinamente limpias cuando salieron.
Modos de escaneo de Trivy: qué detecta cada uno y cuándo usarlo
trivy image — paquetes de sistema operativo y dependencias de lenguaje horneados en una imagen de contenedor construida. Ejecútalo en CI justo después del build, antes de subir la imagen a ningún sitio.trivy fs — dependencias vulnerables y secretos directamente en el código fuente, sin necesidad de construir una imagen. Útil para un check rápido en la PR o para proyectos que no contenerizas.trivy config — configuraciones inseguras en Terraform, Dockerfiles, manifiestos de Kubernetes y charts de Helm. Detecta el error antes de que se aplique o se construya nada.Reporting y hacer que los hallazgos lleguen a la gente
El formato de salida de Trivy importa tanto como el escaneo en sí. --format table es el que quieres en una terminal durante el desarrollo local, legible y rápido de revisar visualmente. --format json es el que debería consumir cualquier herramienta propia o dashboard interno, al ser estructurado y estable de parsear. --format sarif es el que se le pasa a GitHub o GitLab para que los hallazgos aparezcan de forma nativa en su interfaz de seguridad en lugar de como un fichero de log opaco.
Ninguno de esos formatos sirve de mucho si la única persona que los mira es quien abre el job de CI que falló. Normalmente añadimos un paso pequeño que parsea la salida JSON buscando el número de críticas y altas y manda un resumen corto a un canal de Slack que el equipo realmente vigila — "3 críticas, 7 altas en service-x, build bloqueado" con un enlace al reporte completo. Es un añadido pequeño, y es la diferencia entre un escáner del que el equipo es consciente y uno que técnicamente corre pero es funcionalmente invisible.
Conclusión: sube el listón poco a poco, no actives todo a la vez
El despliegue que realmente se mantiene con un equipo es el aburrido. Empieza bloqueando builds solo por severidad CRITICAL con parche disponible — --severity CRITICAL --ignore-unfixed --exit-code 1 — y nada más. Deja que el equipo se acostumbre a triar hallazgos reales sin que el pipeline de deploy se convierta en una carrera de obstáculos desde el primer día. Cuando los hallazgos CRITICAL estén habitualmente en cero, aprieta el umbral para incluir HIGH, y luego revisa si --ignore-unfixed todavía tiene sentido para tus servicios más expuestos.
Activar todas las políticas, todas las severidades y el escáner de IaC en todos los repositorios la misma semana te da una pared de pipelines en rojo, una avalancha de ruido en Slack que nadie lee, y un equipo que en un mes ha descubierto discretamente cómo saltarse el check. Un gate que se endurece gradualmente, que el equipo respeta porque solo bloquea lo que de verdad merece la pena bloquear, es el que sigue aplicándose un año después.
¿Sigues enterándote de vulnerabilidades después de desplegar?
Te ayudamos a integrar Trivy en tu pipeline de CI/CD y a afinar el gate de severidad para que bloquee lo que de verdad importa, sin ahogar a tu equipo en falsas alarmas.
Hablar con un experto →