Trivy e Grype no DevSecOps: Scan de Vulnerabilidades e SBOM para Contêineres Docker em 2026

Como escanear vulnerabilidades em contêineres com Trivy e Grype, gerar SBOM em CycloneDX/SPDX, integrar a GitHub Actions/GitLab CI e tratar falsos positivos com VEX em 2026.

Trivy vs Grype: Scan de Contêineres 2026

Atualizado: 15 de junho de 2026

Trivy e Grype são os dois scanners de vulnerabilidades mais usados em pipelines DevSecOps para contêineres em 2026. O Trivy (Aqua Security) cobre imagens, sistemas de arquivos, repositórios Git, IaC, Kubernetes e gera SBOMs nativamente; já o Grype (Anchore) foca em imagens e SBOMs com integração estreita ao Syft. Neste guia eu mostro, do ponto de vista de quem ataca contêineres como ganha-pão, como instalar ambos, integrá-los a GitHub Actions e GitLab CI, gerar SBOMs em CycloneDX e SPDX, e tratar falsos positivos sem afrouxar o gate de segurança.

  • Trivy 0.58+ é o canivete suíço do DevSecOps em 2026: scaneia imagens, código, IaC, secrets e gera SBOM em CycloneDX/SPDX num único binário.
  • Grype 0.85+ combinado com Syft 1.18+ oferece a melhor precisão de detecção em imagens distroless e linguagens compiladas como Go e Rust.
  • Falhar o build com --exit-code 1 --severity HIGH,CRITICAL é o gate mínimo aceitável, mas exige uma política de exceções versionada em .trivyignore ou arquivo VEX.
  • SBOMs assinados com Cosign e armazenados em registries OCI viraram exigência da SLSA v1.1 e do Executive Order 14028 dos EUA, com adoção crescente na UE via CRA.
  • Trivy Operator no Kubernetes faz scan contínuo de workloads em execução, complementando ferramentas runtime como Falco e Tetragon.

O que é scan de vulnerabilidades em contêineres

Quando eu invado uma infraestrutura, a primeira coisa que procuro são imagens de contêineres desatualizadas. Uma node:18-alpine pinada há dois anos costuma carregar um OpenSSL com CVE crítico, um glibc vulnerável e meia dúzia de libs Node com prototype pollution. Scan de vulnerabilidades em contêineres é o processo de examinar cada camada da imagem (pacotes do SO base, dependências da linguagem como npm, pip, gem, go.mod, além de binários estáticos e até secrets esquecidos), comparando hashes e versões contra bancos como o NVD, GHSA, GitLab Advisory Database, Red Hat Security Data e o Aqua vuln-list.

Em 2026, o que mudou é a profundidade. Scanners modernos não olham só o manifesto package.json: eles desempacotam layers tar, leem /var/lib/dpkg/status, parseiam binários Go com buildinfo e identificam dependências Rust via Cargo.lock incrustado no binário. O resultado é um SBOM (Software Bill of Materials), basicamente uma lista de ingredientes, e um relatório de CVEs com severidade CVSS v4.0, KEV (Known Exploited Vulnerabilities) da CISA e, agora com Trivy 0.58, scores EPSS (probabilidade de exploração nos próximos 30 dias).

Sem esse scan na pipeline, você está enviando para produção, em média, 180 vulnerabilidades por imagem padrão segundo o relatório Sysdig Cloud-Native Security Report 2025. Com gate adequado, esse número cai para menos de 10, e nenhuma das que sobram é crítica.

Trivy vs Grype: qual escolher em 2026

A pergunta certa não é "qual é melhor", é "qual cobre o seu caso de uso primeiro". Eu uso os dois em projetos diferentes. Abaixo está a comparação que faço para clientes quando eles precisam decidir.

CritérioTrivy 0.58Grype 0.85 + Syft 1.18
MantenedorAqua SecurityAnchore
LicençaApache 2.0Apache 2.0
Alvos suportadosImagens, FS, Git, K8s, IaC, AWS, secretsImagens, FS, SBOM
Formato SBOMCycloneDX 1.6, SPDX 2.3, SPDX 3.0CycloneDX 1.6, SPDX 2.3, Syft JSON
Detecção em distrolessBoa (precisa Buildinfo)Excelente (Syft é melhor)
IaC scanning (Terraform, K8s YAML)Sim, nativoNão (apenas K8s via plugin)
Velocidade média (imagem 500MB)8 a 12s15 a 20s
Integração K8s contínuaTrivy OperatorSem operator próprio
EPSS e KEVSim (0.55+)Sim (0.80+)

Resumindo o que vejo na prática: use Trivy se quiser um único binário para imagens, IaC, secrets e Kubernetes; é o padrão de fato em pipelines GitHub Actions. Use Grype + Syft quando precisa do melhor SBOM do mercado para imagens distroless ou para auditoria de compliance NIST 800-218 SSDF, onde o Syft tem cobertura superior de linguagens compiladas. Honestamente, para máxima confiança eu rodo os dois e cruzo resultados. Leva 30 segundos a mais e elimina os pontos cegos de cada scanner.

Instalando e configurando o Trivy

O Trivy é distribuído como binário estático, container, pacote nativo (apt/dnf/brew) e Helm chart. Em pipelines CI eu prefiro o binário estático: sem dependências, fácil de cachear, fácil de pinar versão. Em workstations Linux, o repositório oficial é a opção mais limpa porque garante atualizações via apt update.

# Debian/Ubuntu — repositório oficial Aqua Security
sudo apt-get install -y wget gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | \
  sudo gpg --dearmor -o /usr/share/keyrings/trivy.gpg
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] \
  https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | \
  sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install -y trivy

# Confirme a versao (0.58+ tem EPSS e SPDX 3.0)
trivy --version

Para pipelines reproducíveis, pino a versão exata e baixo o binário direto:

# Binario estatico, ideal para CI
TRIVY_VERSION="0.58.1"
curl -sL "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" \
  | tar -xz -C /usr/local/bin trivy
trivy --version

Antes do primeiro scan, o Trivy baixa o banco de vulnerabilidades (~120MB). Em CI você quer cachear isso para não rebaixar a cada job. O arquivo ~/.cache/trivy é o alvo. Configure também o TRIVY_DB_REPOSITORY para um mirror OCI privado se sua rede bloquear o GitHub Container Registry. É uma exigência comum em pentests que faço em bancos e telecoms.

Como escanear imagens Docker com Trivy

O comando básico é trivy image <ref>, mas em produção você precisa de pelo menos três flags: --severity para filtrar ruído, --exit-code 1 para quebrar o build e --ignore-unfixed para não falhar por CVEs sem patch upstream (caso contrário você quebra todo build de imagem baseada em Debian estável).

# Scan basico, saida tabela colorida no terminal
trivy image nginx:1.27-alpine

# Scan de producao: falha o build se houver CVEs HIGH ou CRITICAL com fix disponivel
trivy image \
  --severity HIGH,CRITICAL \
  --ignore-unfixed \
  --exit-code 1 \
  --format json \
  --output report.json \
  myregistry.io/api:v2.4.1

# Scan combinado: vulnerabilidades, secrets e misconfig
trivy image \
  --pkg-types os,library \
  --vex repo \
  --severity HIGH,CRITICAL \
  --scanners vuln,secret,misconfig \
  myregistry.io/api:v2.4.1

Note o --scanners vuln,secret,misconfig: o Trivy também procura secrets vazados (chaves AWS, tokens GitHub, JWTs) e más configurações em Dockerfiles (USER root, ADD com URL remota, sem HEALTHCHECK). Em quase todo pentest que executo, encontro pelo menos um token de produção esquecido em uma camada intermediária. Docker layer caching é o pior amigo do desenvolvedor distraído.

Para auditoria detalhada de redes corporativas, costumo cruzar o relatório do Trivy com o output de detecção de intrusões com Wazuh e Osquery. Se uma CVE crítica está presente na imagem E o Wazuh detecta processo suspeito tocando o binário vulnerável, você tem evidência forte de exploração ativa.

Como gerar SBOM com Trivy em CycloneDX e SPDX

SBOM (Software Bill of Materials) é a lista de ingredientes da sua imagem. Desde 2022, com o Executive Order 14028 dos EUA, e em 2026 com o Cyber Resilience Act da União Europeia entrando em vigor pleno, ter SBOM versionado e assinado deixou de ser nicho. Virou regulatório. O Trivy gera SBOM em dois formatos padrão (CycloneDX, mantido pela OWASP, e SPDX, mantido pela Linux Foundation) e em Syft JSON para interoperabilidade.

# SBOM CycloneDX 1.6, preferido pela maioria das ferramentas comerciais
trivy image \
  --format cyclonedx \
  --output sbom.cdx.json \
  myregistry.io/api:v2.4.1

# SBOM SPDX 2.3 JSON, exigido por contratos com governo dos EUA (FedRAMP)
trivy image \
  --format spdx-json \
  --output sbom.spdx.json \
  myregistry.io/api:v2.4.1

# Scan de uma imagem usando SBOM ja gerado (rapido, sem baixar a imagem)
trivy sbom sbom.cdx.json --severity CRITICAL

O fluxo correto em DevSecOps 2026 é assim: gerar SBOM no build, assinar com Cosign, enviar para o registry OCI ao lado da imagem e deixar que consumidores baixem o SBOM, validem assinatura e escaneiem novamente sob demanda. Isso desacopla a geração da análise. Quando o NVD adiciona um CVE novo amanhã para uma lib que você usou hoje, você reescaneia o SBOM em segundos, sem reconstruir a imagem.

# Assinar SBOM com Cosign keyless (OIDC), recomendado em CI
cosign attest \
  --type cyclonedx \
  --predicate sbom.cdx.json \
  --yes \
  myregistry.io/api:v2.4.1

Integrando Trivy ao GitHub Actions e GitLab CI

O lugar certo para o scan é o pipeline, não o laptop do dev. Eu prefiro um job dedicado que falha o build se houver CVEs críticas com fix disponível, gera SBOM como artefato e faz upload do resultado SARIF para o GitHub Security tab. Isso aparece no PR como anotação inline, exatamente onde o revisor olha.

# .github/workflows/security.yml
name: Container Security
on:
  pull_request:
  push:
    branches: [main]
jobs:
  trivy:
    runs-on: ubuntu-24.04
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v4

      - name: Build image
        run: docker build -t app:${{ github.sha }} .

      - name: Trivy vulnerability scan
        uses: aquasecurity/[email protected]
        with:
          image-ref: app:${{ github.sha }}
          format: sarif
          output: trivy-results.sarif
          severity: HIGH,CRITICAL
          ignore-unfixed: true
          exit-code: '1'

      - name: Upload SARIF to GitHub Security
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-results.sarif

      - name: Generate SBOM (CycloneDX)
        uses: aquasecurity/[email protected]
        with:
          image-ref: app:${{ github.sha }}
          format: cyclonedx
          output: sbom.cdx.json

      - uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.cdx.json

Para GitLab CI o equivalente usa o template oficial e o report nativo Container Scanning, que aparece no widget de merge request:

# .gitlab-ci.yml
include:
  - template: Jobs/Container-Scanning.gitlab-ci.yml

container_scanning:
  variables:
    CS_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    CS_SEVERITY_THRESHOLD: HIGH
    CS_IGNORE_UNFIXED: "true"
  artifacts:
    paths: [gl-sbom-*.cdx.json]
    reports:
      container_scanning: gl-container-scanning-report.json
      cyclonedx: gl-sbom-*.cdx.json

Quem implementa pipeline de segurança costuma esquecer um detalhe: o scanner também precisa rodar em imagens já em produção, não só no build. Pelo menos uma vez por dia, scaneie o que está rodando no cluster, porque CVEs novas saem todo dia e a imagem que era limpa na quarta-feira pode ter três CRITICALs na sexta. É exatamente isso que o Trivy Operator resolve, e eu vou cobrir abaixo. Para a parte de runtime detection (processo malicioso já em execução), veja o guia comparando Falco e Tetragon para segurança runtime.

Grype + Syft: a alternativa do Anchore

Grype é, na minha visão, o melhor scanner do mercado para imagens distroless e binários Go/Rust. Isso porque ele consome SBOMs gerados pelo Syft, que tem o melhor catálogo de fontes de pacotes (mais de 30 ecossistemas, incluindo Cargo, Conan, dotnet deps.json, Erlang/Elixir hex e Hackage). Em pentests de stacks polyglot, o Grype encontra CVEs que outros scanners perdem.

# Instalar Grype + Syft (binarios estaticos)
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin

# Gerar SBOM com Syft e escanear com Grype
syft myregistry.io/api:v2.4.1 -o cyclonedx-json > sbom.cdx.json
grype sbom:./sbom.cdx.json --fail-on high

# Fluxo direto (Grype chama Syft internamente)
grype myregistry.io/api:v2.4.1 \
  --only-fixed \
  --fail-on critical \
  --output table

O Grype tem um truque excelente: --add-cpes-if-none. Quando o Syft não consegue inferir o CPE (identificador NVD) de um pacote, o Grype tenta gerar candidatos e cruza com o banco. Isso reduz falsos negativos em libs C/C++ compiladas estaticamente, onde a versão fica enterrada num símbolo BSS. Para mais detalhes consulte a documentação oficial do Grype no GitHub.

Tratando falsos positivos com .trivyignore e VEX

Todo pipeline de segurança morre por excesso de ruído. Quando o time de dev começa a ver builds quebrando por CVEs que não afetam o caminho de código real, ele vai pressionar para desligar o scanner. A solução não é desligar. É documentar exceções de forma versionada e auditável.

O Trivy aceita três mecanismos: .trivyignore (lista plana de CVEs ignorados), trivy-ignore.yaml (com expiração, contexto e razão) e arquivos VEX (Vulnerability Exploitability eXchange) no formato OpenVEX ou CycloneDX VEX. VEX é o padrão correto em 2026 porque é interoperável entre scanners: você mantém um único vex.json e aplica em Trivy, Grype e ferramentas comerciais.

# .trivyignore, formato simples
CVE-2023-12345
CVE-2024-67890

# trivy-ignore.yaml, recomendado em 2026
vulnerabilities:
  - id: CVE-2023-12345
    paths: ["usr/lib/x86_64-linux-gnu/libcrypto.so.3"]
    statement: "Nao exploitavel: a funcao vulneravel EVP_PKEY_decrypt nunca eh chamada"
    expired-at: "2026-12-31"
# vex.json, OpenVEX (formato preferido pela CISA)
{
  "@context": "https://openvex.dev/ns/v0.2.0",
  "@id": "https://meusite.com/vex/api-v2.4.1",
  "author": "[email protected]",
  "timestamp": "2026-06-15T10:00:00Z",
  "statements": [{
    "vulnerability": {"name": "CVE-2024-12345"},
    "products": [{"@id": "pkg:oci/[email protected]"}],
    "status": "not_affected",
    "justification": "vulnerable_code_not_in_execute_path"
  }]
}

Aplicação: trivy image --vex vex.json myregistry.io/api:v2.4.1. Cada exceção precisa ter justification de uma lista fechada do padrão (component_not_present, vulnerable_code_not_present, vulnerable_code_not_in_execute_path, vulnerable_code_cannot_be_controlled_by_adversary, inline_mitigations_already_exist). Isso impede o típico "ignora porque o dev disse que tá tudo bem".

Trivy Operator no Kubernetes

Build-time scan resolve metade do problema. A outra metade é o que acontece com imagens já em execução: uma CVE descoberta hoje numa lib que você usa há seis meses. O Trivy Operator (parte do projeto Aqua Open Source) instala um operador Kubernetes que faz scan contínuo de toda imagem em execução, gera CRDs VulnerabilityReport e ConfigAuditReport, e exporta para Prometheus.

# Instalar via Helm
helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm install trivy-operator aqua/trivy-operator \
  --namespace trivy-system \
  --create-namespace \
  --set trivy.severity=HIGH\,CRITICAL \
  --set trivy.ignoreUnfixed=true \
  --set operator.scanJobsConcurrentLimit=5

# Consultar relatorios
kubectl get vulnerabilityreports -A
kubectl get configauditreports -A

# Exportar metricas para Prometheus
kubectl port-forward -n trivy-system svc/trivy-operator 8080:80
curl http://localhost:8080/metrics | grep trivy_image_vulnerabilities

Combinado com Kyverno ou Kubewarden como admission controller, você pode bloquear deploy de imagens não escaneadas ou com CVE crítica. Política como código, aplicada na entrada do cluster. Para validar a postura geral do host antes de admitir workloads sensíveis, vale rodar uma auditoria periódica com Lynis e CIS Benchmarks no nó worker. Um host comprometido invalida qualquer hardening de contêiner.

Para um mergulho profundo na spec do CycloneDX, incluindo o objeto vulnerabilities embutido e suporte a VDR/VEX, consulte a especificação oficial do CycloneDX mantida pela OWASP. A documentação do Trivy em trivy.dev tem exemplos completos de cada um dos modos cobertos aqui.

Perguntas Frequentes

Trivy é gratuito para uso comercial?

Sim. O Trivy é open source sob licença Apache 2.0, mantido pela Aqua Security, e pode ser usado livremente em projetos comerciais. A Aqua oferece uma versão paga (Aqua Premium) com banco de vulnerabilidades expandido e suporte SLA, mas o Trivy open source cobre os casos de uso da grande maioria das equipes DevSecOps.

Trivy detecta secrets vazados em imagens Docker?

Sim. Com a flag --scanners secret o Trivy procura mais de 100 tipos de secrets (chaves AWS, tokens GitHub, JWTs, chaves SSH privadas e strings de conexão de banco) em qualquer camada da imagem. É comum encontrar secrets em camadas intermediárias do Dockerfile mesmo quando o desenvolvedor "removeu" no estágio final, porque cada RUN gera uma camada permanente.

Como atualizar o banco de vulnerabilidades do Trivy?

O Trivy atualiza o DB automaticamente a cada execução, mas você pode forçar com trivy image --download-db-only. Em CI o DB é cacheado em ~/.cache/trivy; configure o cache do GitHub Actions ou GitLab para evitar download de 120MB a cada job. Desde 2024 o DB é distribuído via OCI em ghcr.io/aquasecurity/trivy-db.

Qual a diferença entre CycloneDX e SPDX para SBOM?

CycloneDX, mantido pela OWASP, é mais focado em segurança e suporta nativamente objetos de vulnerabilidades, VEX e assinaturas. SPDX, mantido pela Linux Foundation e padronizado como ISO/IEC 5962, é exigido em contratos governamentais dos EUA (FedRAMP) e foca em compliance de licenças. Para DevSecOps moderno, gere os dois; o custo é baixo e cobre todos os consumidores.

Posso usar Trivy sem ter Docker instalado?

Sim. O Trivy suporta scan de imagens diretamente de registries OCI via trivy image --image-src remote, sem precisar do Docker daemon. Também escaneia tarballs (trivy image --input image.tar) e sistemas de arquivos locais (trivy fs ./). Isso é útil em runners CI minimalistas ou em ambientes air-gapped.

Felix Lindqvist
Sobre o Autor Felix Lindqvist

Penetration tester and OSCP holder. Reverse engineers misconfigured servers for a living and writes about what he finds.