SBOM
Uma SBOM deve descrever o build que foi distribuído
Uma SBOM gerada durante o build é registada enquanto o build é executado, a partir do tráfego que realmente entrou nele. Em vez disso, a maioria dos geradores de SBOM reconstrói a lista a posteriori, a partir do lockfile ou do resultado, o que deixa de fora as dependências ocultas que um build obtém por si próprio. Eis porquê, e o que muda ao registar o build.
Atualizado a
SBOM de build, SBOM de código-fonte, SBOM analisada
As orientações da CISA sobre tipos de SBOM classificam as SBOM por quando e como são feitas. Três delas cobrem quase todos os geradores de SBOM em uso:
| Tipo | Como é feita | O que pode deixar de fora |
|---|---|---|
| Código-fonte | A partir de ficheiros de código-fonte, manifestos e lockfiles, normalmente por uma ferramenta de SCA | Componentes de runtime, plugins ou plataforma; tudo o que não está declarado nos ficheiros |
| Build | Como parte do build que cria o artefacto a lançar | O que o ambiente de build não consegue ver, e é por isso que a completude deve ser indicada |
| Analisada | Depois do build, através da análise de um executável, pacote ou imagem de contentor | Componentes que a ferramenta não consegue reconhecer; os resultados dependem de heurísticas |
A CISA indica dois benefícios de uma SBOM de build: mais confiança de que a SBOM representa corretamente o artefacto, devido
à informação disponível durante o build e o processo de CI/CD, e mais fiabilidade, porque o mesmo workflow de build
pode assinar tanto a SBOM como o artefacto. A desvantagem indicada é que pode ter de alterar o processo de build para
gerar uma. Com o CRACI, essa alteração é uma linha, runs-on: craci, e o registo vem com o
runner.
Analisar uma pasta versus registar um build
A maioria dos geradores de SBOM são scanners. Aponta-se um para uma árvore de código-fonte ou uma imagem de contentor, e este deduz o que pensa que entrou lendo manifestos, lockfiles, pacotes instalados ou binários. Isso é útil e, para software que não compilou, muitas vezes é a única opção.
A fragilidade é que um scanner só vê o que tem à frente. Se não tiver sido feito commit de nenhum lockfile, se as dependências não tiverem sido instaladas antes da análise ou se o produto for distribuído como binário compilado, o scanner tem menos com que trabalhar do que o build teve. Uma análise que não encontra nenhum componente produz na mesma um documento SBOM válido, e é essa a parte que apanha as pessoas desprevenidas.
O CRACI segue uma abordagem diferente. Gera a SBOM dentro do seu próprio runner de CI, pelo que o artefacto distribuído é o artefacto descrito. Não é uma análise executada algures ali perto.
SBOM basics: SPDX, CycloneDX and OpenVEX
O que uma SBOM regista, como os formatos SPDX e CycloneDX diferem na prática e o que o OpenVEX acrescenta sobre se uma vulnerabilidade afeta realmente um produto.
Petteri Pulkkinen e Erika Marttinen em Kubernetes Community Days Helsinki 2026, de 16:50 a 22:05 de SBOMbastic: Getting Ready for Upcoming EU Cybersecurity Regulation in Software Supply Chains.
De que depende uma análise
O Syft, o Trivy, o cdxgen, o Microsoft SBOM Tool, a exportação do grafo de dependências do GitHub e a Socket CLI deduzem todos uma SBOM a partir do que conseguem ler. Os padrões abaixo decorrem da forma como estas ferramentas estão documentadas.
As ferramentas discordam sobre o que é um componente
Cada gerador decide por si o que conta como componente e em que ficheiros confiar, pelo que o mesmo commit fixado dá a cada um uma lista diferente.
Parte disso tem a ver com nomes. As ferramentas identificam a mesma GitHub Action com tipos de package URL diferentes, pelo que podem listar a mesma coisa e, ainda assim, não coincidir. Isso também faz parte do problema: uma SBOM que usa um identificador diferente para um componente é mais difícil de cruzar com dados de vulnerabilidades.
Ter instalado primeiro pode importar mais do que a ferramenta
O Express 5.2.1 não faz commit de nenhum lockfile. Apontado para um checkout limpo, um
gerador que resolve dependências a partir de lockfiles ou de uma árvore instalada
não tem nenhum dos dois à frente, pelo que nenhuma das dependências npm declaradas em
package.json chega ao documento. Pode ainda assim devolver uma lista,
feita do que mais reconhecer, como GitHub Actions e os ficheiros de
workflow que as referem. Nenhuma das ferramentas está avariada.
Numa cópia instalada do mesmo repositório, uma flag do Syft volta a mudar a resposta. A mesma ferramenta, a mesma árvore, a mesma versão. Por conceção, uma análise de diretório do Syft reporta o que um projeto declara, e o cataloger que lê os pacotes instalados não faz parte do conjunto predefinido para diretórios.
Algum software não tem manifesto para ler
O repositório anthropics/claude-code na v2.1.259 não tem qualquer tipo de manifesto de
dependências: nenhum package.json, lockfile ou
equivalente. O que cada gerador devolve é montado a partir de outros
tipos de ficheiros, pelo que a resposta diz mais sobre a ferramenta do que sobre o projeto.
O pacote npm publicado não é muito diferente. @anthropic-ai/claude-code declara zero dependências de runtime e
oito pacotes específicos de plataforma que encapsulam um binário pré-compilado.
@openai/codex 0.149.1 tem a mesma forma. O que é executado na máquina do
programador é um binário compilado em cujo interior nenhum leitor de manifestos consegue ver.
Isto não é uma falha de uma ferramenta em particular. É um modelo de distribuição que a
análise a posteriori não consegue descrever.
Versões e fornecedores
Quando duas ferramentas encontram o mesmo pacote, nem sempre concordam quanto à sua versão. Uma pode fixar uma GitHub Action a um SHA de commit enquanto outra reporta uma tag de versão. Uma consulta de vulnerabilidades com a versão errada dá uma resposta errada com toda a confiança.
O campo do fornecedor, um dos elementos mínimos da NTIA, fica muitas vezes vazio, porque a maioria dos geradores não tem fonte para ele.
Dependências ocultas: o que nenhuma análise consegue ver
Todas as ferramentas acima partem do lockfile ou do resultado do build e deduzem o que entrou. Um build obtém mais do que isso. Estas dependências ocultas não constam de nenhum lockfile, pelo que uma análise não as consegue encontrar:
-
Hooks de instalação. Os scripts
preinstallepostinstalldo npm são executados durante a instalação e podem descarregar mais código. Foi assim que o worm Shai-Hulud foi executado. - Scripts de build. Os scripts de build de Rust, entre outros, descarregam aquilo de que precisam durante o build.
- Imagens base. Um build de contentor obtém uma imagem base e as respetivas camadas do sistema operativo, que nenhum lockfile de linguagem lista.
- Caches e ferramentas de build. Os pacotes restaurados de caches de CI e as ferramentas instaladas por passos de configuração também chegam fora do lockfile.
Analisar o resultado depois não fecha a lacuna. Um binário acabado, sobretudo código compilado como Rust, deixa o scanner a adivinhar o que entrou nele.
Dois builds, duas SBOM
Há uma segunda razão pela qual uma análise não pode substituir o build: a maioria dos builds não é reprodutível. Compile o mesmo commit duas vezes seguidas e não há garantia de obter os mesmos pacotes. Os gestores de pacotes resolvem e instalam pela ordem em que as respostas chegam, o que importa sobretudo quando não há lockfile, como em muitos projetos Python, e os passos de build podem ter a sua própria aleatoriedade. Os builds reprodutíveis byte a byte são possíveis, com ferramentas como o Nix, mas são raros.
Assim, uma SBOM gerada mais tarde, noutra máquina ou a partir de outro build, descreve um build que pode nunca ter sido distribuído. Só a SBOM registada durante o build de CI que produziu uma versão prova o que está em produção.
O que muda ao registar o build
O CRACI vê o tráfego; os scanners não. Executa os seus jobs de GitHub Actions nos seus próprios runners e vê o tráfego de rede que entra em cada build, pelo que a SBOM lista o que o job realmente trouxe, incluindo dependências transitivas e ocultas, e não o que um manifesto diz que deveria ser necessário.
As caches são onde o registo durante o build se torna difícil. Um pacote restaurado de uma cache nunca toca num registo de pacotes, pelo que um proxy por si só não o veria. O CRACI transporta as evidências de dependências com a cache, pelo que os pacotes restaurados de uma cache ficam na SBOM do job que os restaurou. Consulte o que a cache do GitHub Actions esconde.
Os ecossistemas detetados incluem npm, PyPI, RubyGems, Cargo, Go, Nix, OCI, apt, apk e fontes Git, além de predefinições de descarga de Yocto e OpenEmbedded. Os mirrors npm e PyPI do JFrog Artifactory funcionam. Os metadados de licença declarada são incluídos na exportação. As SBOM são exportadas em CycloneDX e SPDX. A página de geração de SBOM cobre a funcionalidade completa.
Ver o tráfego também funciona na outra direção. Um build precisa de pacotes de fontes conhecidas e publica o seu resultado em locais conhecidos. A política de egress do CRACI permite apenas esses e bloqueia de imediato qualquer outra ligação de saída, pelo que o malware que tente enviar os seus segredos para o servidor de um atacante falha. Como a política permite aquilo de que o build precisa em vez de procurar hosts maliciosos conhecidos, mantém-se mesmo que seja o primeiro alvo de um ataque que ninguém viu antes, como aconteceu com o Shai-Hulud. Consulte como uma política de egress impede a exfiltração.
SBOM de contentores e Docker
É nas imagens de contentor que a diferença mais se nota. As formas habituais de obter uma SBOM de Docker analisam todas a imagem:
o Syft e o Trivy analisam-na, docker scout sbom indexa o seu conteúdo quando a imagem não tem nenhuma atestação
SBOM, e a atestação SBOM do BuildKit, ativada com --sbom=true, executa por defeito um scanner baseado em Syft
sobre a fase final da imagem. O plugin mais antigo docker sbom foi descontinuado em favor de
docker scout sbom.
Cada um destes lê a imagem acabada, pelo que vê o que ainda é reconhecível nas camadas. Não vê o que o build obteve pelo caminho. O CRACI regista o job que compila a imagem: as imagens e os pacotes que o job obtém de registos OCI e fontes de pacotes entram na mesma SBOM que o resto do build. Para uma imagem que não compilou, analisá-la continua a ser a opção prática. Consulte o que escapa às SBOM de imagens Docker.
Completude, indicada por job
Uma SBOM registada só é tão boa quanto o registo. Em vez de apresentar todas as SBOM como completas, o CRACI indica a completude de cada job e de cada cache, num de cinco estados:
- Complete. O CRACI observou evidências completas do job, e todas as caches restauradas estavam completas.
- Complete with connections. Os requisitos foram cumpridos, mas o job usou uma ou mais ligações de rede personalizadas, pelo que vale a pena rever o que passou por elas.
- Incomplete. O CRACI identificou uma condição que pode deixar dependências fora da SBOM.
- Unavailable. O CRACI não conseguiu terminar a avaliação das evidências.
- Not recorded. O job é anterior à indicação de completude.
A completude é transitiva entre caches. Se um job restaurar uma cache incompleta, a sua SBOM também fica incompleta, e qualquer cache que esse job produza transporta o estado incompleto. Sabe em que SBOM pode confiar e quais precisam de uma segunda análise.
The hard parts: hooks, noise and staying current
Três problemas que a palestra aponta: hooks post-install que obtêm coisas que nenhum manifesto enumera, encontrar as dependências que importam numa lista longa e manter a SBOM atualizada, o que significa gerá-la na CI em cada build.
Petteri Pulkkinen e Erika Marttinen em Kubernetes Community Days Helsinki 2026, de 25:39 a 27:22 de SBOMbastic: Getting Ready for Upcoming EU Cybersecurity Regulation in Software Supply Chains.
Proveniência através da API
A SBOM é uma parte do que o CRACI regista para cada job. A API devolve SBOM, traços de rede e proveniência, tudo registado a partir do mesmo build.
Onde um scanner continua a ser a ferramenta certa
O registo durante o build cobre o software que compila. Para uma imagem ou um binário de terceiros que não compilou, um scanner continua a ser a escolha prática, e as ferramentas acima são capazes e amplamente usadas. A questão não é que os scanners sejam maus. É que a SBOM das suas próprias versões deve vir do build que as produziu.
Uma SBOM do CRACI também tem os seus limites. Cobre os jobs executados em runners CRACI, o que hoje significa GitHub Actions em Linux, com outros sistemas de CI no roadmap.
A escolher entre ferramentas? Consulte ferramentas de SBOM comparadas, ou veja como as SBOM geradas durante o build se enquadram no trabalho de conformidade com o CRA. Para o Syft, o Trivy e o cdxgen lado a lado, consulte o que escapa aos geradores de SBOM open source.
SBOM geradas durante o build: perguntas frequentes
O que é uma SBOM gerada durante o build?
Uma SBOM gerada durante o build, a que a CISA chama build SBOM, é gerada como parte da compilação do artefacto a lançar. Em vez de reconstruir depois a lista de componentes a partir de ficheiros, descreve o que o próprio build usou. O CRACI regista-a a partir dos pacotes que cada job de GitHub Actions realmente obteve.
Qual é a diferença entre uma SBOM de build e uma SBOM analisada?
Uma SBOM analisada é gerada depois do build, através da análise de um artefacto como um executável ou uma imagem de contentor, o que geralmente exige heurísticas. Uma SBOM de build é gerada durante o build, a partir da informação que o build tem. As SBOM analisadas funcionam sem acesso ao build, o que as torna a escolha certa para software que não compilou.
Como gero uma SBOM para uma imagem Docker?
Para uma imagem existente, analise-a com um gerador como o Syft, o Trivy ou docker scout sbom. O BuildKit também pode anexar uma atestação SBOM ao compilar, analisando a imagem final. Para registar o que o build de uma imagem trouxe, execute o build num runner CRACI: a SBOM do job cobre as imagens e os pacotes que obteve.
O que são dependências ocultas?
Tudo o que um build obtém e que nenhum lockfile lista: código descarregado por hooks de instalação do npm, ficheiros que os scripts de build de Rust descarregam durante o build, e a imagem base e as respetivas camadas do sistema operativo num build de contentor. As ferramentas que leem o lockfile ou analisam o resultado não as veem. O CRACI regista-as porque vê o tráfego que entra no build.
Porque é que dois builds do mesmo código dão SBOM diferentes?
Porque a maioria dos builds não é reprodutível. O gestor de pacotes resolve e instala pacotes pela ordem em que as respostas chegam, sobretudo sem lockfile, e os passos de build podem ter aleatoriedade. Compile o mesmo commit duas vezes seguidas e o segundo build pode conter pacotes diferentes. Só a SBOM registada durante o build que produziu uma versão prova o que essa versão contém.
Uma SBOM gerada durante o build está sempre completa?
Não automaticamente. Um registo só é tão bom quanto o que consegue observar, pelo que o CRACI indica a completude de cada job e de cada cache: Complete, Complete with connections, Incomplete, Unavailable ou Not recorded. Sabe em que SBOM pode confiar e quais precisam de uma segunda análise.
Uma SBOM gerada durante o build substitui os scanners de SBOM?
Para o software que compila, sim: a SBOM das suas versões deve vir dos builds que as produziram. Para imagens e binários de terceiros que não compilou, um scanner continua a ser a escolha prática. A página Ferramentas de SBOM comparadas analisa as opções.
Que gerador de SBOM usa o CRACI?
Nenhum. O CRACI não analisa ficheiros. Um proxy que reconhece pacotes nos seus runners regista os pacotes que cada job obtém de npm, PyPI, RubyGems, Cargo, Go, Nix, OCI, apt, apk e fontes Git, além de predefinições de descarga de Yocto e OpenEmbedded, e exporta o resultado em CycloneDX ou SPDX.
Compare uma SBOM do seu próprio build
Marque uma demo e execute um dos seus workflows no CRACI. Coloque a sua SBOM ao lado da que a sua ferramenta atual produz e veja onde diferem.
Marcar demo