
Security Researcher
Heitor Gouvêa

Grype, Trivy e Docker Scout fazem bem o que se propõem: varrer os pacotes de uma imagem contra bases de dados de vulnerabilidades conhecidas (NVD, OSV, GitHub Advisory Database) e reportar correspondências. O modelo é simples:
pacote X na versão Y tem CVE Z associada → reportar.
O que esse modelo não captura é contexto.
Uma CVE é associada a um componente, não a um uso específico dele. Quando o banco de dados registra que libssl 3.0.7 tem CVE-2023-0286, ele não sabe (e não tem como saber) se o código vulnerável dentro de libssl é efetivamente alcançável pela sua aplicação. Não sabe se a função afetada é chamada em algum path de execução. Não sabe se o vetor de ataque pressupõe acesso a uma interface que não existe na imagem.
O resultado prático: scanners reportam vulnerabilidades que, tecnicamente, existem no componente, mas que são estruturalmente inexploráveis no contexto específico da imagem. Isso não é falha dos scanners, é uma limitação inerente à abordagem de matching por banco de dados. O problema é que, sem uma camada adicional de contextualização, toda CVE reportada parece igualmente urgente.
Para um engenheiro, CVEs sem contexto têm um custo direto. Pipelines configurados com políticas de bloqueio em severidade HIGH ou CRITICAL param. Alguém precisa investigar manualmente cada CVE reportada, entender se ela é explorável, e decidir se o bloqueio é justificado. Em imagens com dezenas de pacotes, isso pode significar horas por sprint dedicadas a triagem de vulnerabilidades que, ao final da investigação, são descartadas como não-aplicáveis.
Esse tempo sai de features, infraestrutura e dívida técnica, e vai para triagem de falsos positivos. O VEX foi criado para eliminar esse ruído com justificativa técnica auditável.
O que é VEX (Vulnerability Exploitability eXchange)
VEX significa Vulnerability Exploitability eXchange. É um padrão de metadados que permite ao produtor de um artefato (uma imagem de container, um binário, um pacote) declarar formalmente, para cada CVE, se aquela vulnerabilidade é explorável naquele artefato específico e por quê.
O conceito foi iniciado pela CISA (Cybersecurity and Infrastructure Security Agency) como parte do esforço de maturidade em SBOM. Enquanto o SBOM lista os componentes contidos na imagem, o VEX indica quais desses componentes representam risco real.
Os quatro status do VEX: not_affected, affected, fixed, under_investigation
O VEX modela o ciclo de vida de uma vulnerabilidade em relação a um artefato como uma máquina de quatro estados:
not_affected: o componente com a CVE está presente na imagem, mas a vulnerabilidade não é explorável naquele contexto. Requer uma justificativa técnica obrigatória.
affected: a vulnerabilidade é confirmadamente explorável. Deve incluir informações sobre o impacto esperado e ações recomendadas.
fixed: a vulnerabilidade existia e foi corrigida. Útil quando a imagem já aplica um patch antes que ele seja refletido no banco de dados oficial.
under_investigation: o status ainda está sendo analisado. Indica que o produtor está ciente e trabalhando na avaliação.

No Quor, os status que dominam nossa operação são not_affected e fixed, e cada um por uma razão diferente que vamos explorar em detalhes.
O que é OpenVEX e por que escolhemos esse formato
Existem três formatos principais de VEX: OpenVEX (CISA/OpenSSF), CycloneDX VEX e CSAF VEX. No Quor, adotamos OpenVEX, o formato mais enxuto, projetado especificamente para o contexto de containers e artefatos de software.
OpenVEX é um documento JSON-LD com uma estrutura mínima e bem definida. Vamos destrinchar cada campo relevante. Estrutura de um documento OpenVEX:
Campos que merecem atenção específica:
vulnerability.@id: a referência canônica à CVE, usando a URL oficial do NVD ou CVE.org. Não uma string livre, um identificador resolvível.
products: o artefato avaliado, identificado pelo digest SHA256 da imagem. Isso é fundamental: a declaração VEX está vinculada a uma versão específica e imutável da imagem, não a um nome genérico.
subcomponents: o componente específico dentro do artefato que contém a CVE, identificado por PURL (Package URL). Permite que ferramentas entendam exatamente qual pacote está sendo avaliado.
justification: o campo mais crítico do documento. OpenVEX define um vocabulário controlado de justificativas para status not_affected:
Justificativa | Significado |
component_not_present | O componente não está na imagem (falso positivo de scanner) |
vulnerable_code_not_present | O componente está presente, mas o código vulnerável foi removido (ex: patch parcial, build com feature flags) |
vulnerable_code_not_in_execute_path | O código vulnerável existe, mas não é alcançável por nenhum path de execução |
vulnerable_code_cannot_be_controlled_by_adversary | O code path existe, mas o input necessário para exploração não pode ser controlado externamente |
inline_mitigations_already_exist | Mitigações no nível de configuração ou runtime bloqueiam a exploração |
impact_statement: | A justificativa técnica em linguagem natural. Este campo é onde a análise real é documentada. Deve ser específico o suficiente para que outro engenheiro possa verificar a conclusão de forma independente. |
O vocabulário controlado é intencional. Não é texto livre, é uma taxonomia que ferramentas de segurança conseguem processar automaticamente (viabiliza a realização de parsing).

Quor Newsletter
Exemplos de declarações VEX em casos reais
Caso 1: CVE em código não alcançável (CVE-2023-0286 em libssl)
CVE-2023-0286 afeta a função X509_NAME_oneline() no OpenSSL, no processamento de GeneralNames do tipo ASN1_STRINGem certificados X.509. A vulnerabilidade é explorável quando uma aplicação processa certificados arbitrários fornecidos por clientes externos.
O que o scanner vê: libssl com CVE de severidade HIGH. O que o VEX declara: o vetor de ataque pressupõe processamento de certificados arbitrários, funcionalidade que não existe nessa imagem.
Caso 2: componente não presente, falso positivo de scanner (CVE-2022-37434 em zlib)
Alguns scanners associam CVEs a pacotes com base no nome, sem verificar a presença real do binário ou biblioteca na imagem. É um caso clássico de falso positivo estrutural. CVE-2022-37434 afeta a função inflate() no zlib, explorável via input malformado específico. Trivy e Grype frequentemente reportam essa CVE em imagens Alpine que listam zlib como dependência transitiva, mesmo quando a versão instalada já inclui o patch ou quando o binário vulnerável não está presente.
Este caso expõe um limite importante dos scanners: a base de dados do NVD pode registrar uma CVE como afetando uma faixa de versões sem capturar que distribuições individuais (Alpine, Debian, Red Hat) aplicam patches dentro do mesmo número de versão. O VEX fecha essa lacuna com rastreabilidade direta ao advisory da distribuição e ao commit de patch.
Caso 3: patch antecipado com status fixed (CVE-2024-41110 no Docker Engine)
Este é um dos casos mais valiosos do ponto de vista operacional. Quando compilamos diretamente do código fonte (como descrito no artigo #2), temos a capacidade de aplicar patches de segurança antes que o banco de dados oficial registre a correção, e antes que a distribuição lance uma versão com o fix. CVE-2024-41110 é uma vulnerabilidade crítica no Docker Engine (AuthZ plugin bypass). Para imagens que incluem o Docker CLI como ferramenta auxiliar, o scanner vai reportar a CVE enquanto o advisory não estiver resolvido nos bancos de dados.
Sem o VEX fixed, o scanner vai continuar reportando a CVE enquanto o banco de dados não refletir a versão corrigida. Com o VEX, o pipeline de CI sabe que a imagem já está corrigida, com rastreabilidade até o commit específico.
Caso 4: vetor de ataque não controlável externamente (CVE-2023-45853 em zlib/minizip)
Algumas CVEs são exploráveis apenas sob condições muito específicas, condições que a arquitetura da aplicação torna impossíveis externamente. CVE-2023-45853 afeta o minizip dentro do zlib, explorável via arquivos ZIP com campos de nome de arquivo excessivamente longos. Aplicável apenas em contextos onde a aplicação processa arquivos ZIP de fontes não confiáveis.
Como VEX é consumido pelos scanners
A pergunta prática para um engenheiro de plataforma: como isso funciona na integração com Grype, Trivy e Docker Scout?
O Grype suporta VEX nativamente via flag --vex:
Com o documento VEX carregado, Grype filtra automaticamente as CVEs com status not_affected ou fixed. O resultado é uma lista de vulnerabilidades que representa apenas o risco real — não os falsos positivos contextuais.
Para integração em CI/CD:
O Trivy suporta VEX a partir da versão 0.46.0, via flag --vex:
No output do Trivy, CVEs com declaração VEX aparecem com a coluna VEX Status preenchida, e podem ser filtradas com --ignore-status not_affected.
O Docker Scout suporta VEX via atestados na imagem. O documento VEX é anexado como atestado OCI durante o push:
Com o atestado presente, docker scout cves filtra automaticamente as vulnerabilidades declaradas como not_affected. O VEX torna-se parte inseparável da imagem, qualquer scanner que extraia os atestados OCI recebe o contexto junto.
Esta é a abordagem preferível: ao embutir o VEX como atestado na imagem, o documento segue a imagem para qualquer registry ou ambiente de execução, sem necessidade de distribuição manual de arquivos externos.
O custo operacional de produzir e manter VEX
Antes de fechar, um ponto que costuma ficar de fora: produzir VEX de qualidade tem custo. Cada declaração not_affected exige análise técnica, ler o advisory, verificar se o code path vulnerável é alcançável na imagem, mapear o vetor de ataque contra a arquitetura. Sem esse rigor, a declaração vira falsa segurança.
Para uma equipe de plataforma manter VEX sobre suas próprias imagens, o processo envolve:
Por cada CVE nova reportada: ler o advisory completo, entender o vetor de ataque, verificar o code path na imagem, escrever a justificativa técnica, commitar o documento atualizado, e garantir que a versão da imagem é atualizada com o novo atestado.
Por nova versão da imagem: revisar todas as declarações VEX existentes para garantir que ainda são válidas para a nova versão,aqui falta algo que conecte, não?um patch pode introduzir um novo componente que ativa um code path anteriormente inerte.
Por atualização de advisory: monitorar mudanças no scope de CVEs já declaradas; uma CVE declarada not_affected hoje pode ter seu vetor de ataque ampliado por um advisory suplementar, exigindo nova análise.
Esse é exatamente o trabalho que o Quor faz para o cliente.
Cada imagem do catálogo é distribuída com um documento VEX mantido pela nossa equipe, vinculado ao digest específico da imagem por atestado. Quando um cliente faz pull de quor/node:22-lts, ele não recebe apenas a imagem, recebe a inteligência de exploitabilidade que a acompanha: quais CVEs reportadas pelos scanners são estruturalmente inexploráveis, com a justificativa técnica que suporta cada declaração.
O efeito prático no pipeline de CI: scanners deixam de bloquear builds por CVEs já avaliadas como não exploráveis, e o time de engenharia para de gastar horas por sprint em triagem manual.
Limitações do VEX: o que ele não resolve
VEX resolve um problema específico: separar CVEs exploráveis de alertas que não se aplicam ao contexto analisado. O padrão ajuda a reduzir ruído de scanner, mas não elimina a necessidade de análise técnica contínua.
A análise sustenta o documento. Uma declaração not_affected mal justificada pode suprimir um alerta sem evidência suficiente. Por isso, cada justificativa precisa considerar o advisory, o pacote afetado, o contexto de uso e o code path vulnerável.
A manutenção também é contínua. Um documento VEX vale para a versão da imagem em que a análise foi feita. A vinculação ao digest SHA256 garante que a declaração se aplica a um artefato específico e imutável. Novas versões exigem revisão, e uma CVE já analisada pode mudar de contexto se advisories suplementares ampliarem o vetor de ataque.
O escopo do VEX no contexto de imagens de container também tem limites. Ele não cobre vulnerabilidades no código da aplicação, falhas de lógica de negócio, configurações de runtime ou dependências adicionadas pela aplicação, como pacotes npm, JARs e bibliotecas externas. Esses riscos exigem outras camadas de análise.
O suporte entre ferramentas ainda varia. Grype e Trivy já têm implementações consistentes, mas algumas plataformas de gestão de vulnerabilidades podem exigir configuração adicional para consumir documentos OpenVEX e aplicar a supressão automática dos alertas correspondentes.
Confira os outros conteúdos da série:
→ Como estamos construindo imagens com zero CVEs #1: reduzindo a superfície de ataque (Alpine e distroless)
→ Como estamos construindo imagens com zero CVEs #2: compilando da fonte
With Quor, security becomes your competitive edge. See how in a personalized demo.