SBOM
Een SBOM moet de build beschrijven die is geleverd
Een SBOM tijdens de build wordt vastgelegd terwijl de build draait, op basis van het verkeer dat daadwerkelijk is binnengekomen. De meeste SBOM-generatoren reconstrueren de lijst in plaats daarvan achteraf uit de lockfile of de uitvoer, en missen zo de verborgen afhankelijkheden die een build zelf ophaalt. Dit is waarom, en dit verandert er als u de build vastlegt.
Bijgewerkt op
Build-SBOM, bron-SBOM, geanalyseerde SBOM
De richtlijnen van CISA over SBOM-typen delen SBOM's in op wanneer en hoe ze worden gemaakt. Drie daarvan dekken vrijwel elke SBOM-generator die wordt gebruikt:
| Type | Hoe die wordt gemaakt | Wat die kan missen |
|---|---|---|
| Bron | Uit bronbestanden, manifests en lockfiles, meestal door een SCA-tool | Runtime-, plug-in- of platformcomponenten; alles wat niet in de bestanden is gedeclareerd |
| Build | Als onderdeel van de build die het uitbrengbare artefact maakt | Wat de buildomgeving niet kan zien, en daarom moet de volledigheid worden vermeld |
| Geanalyseerd | Na de build, door een uitvoerbaar bestand, pakket of containerimage te analyseren | Componenten die de tool niet kan herkennen; resultaten hangen af van heuristieken |
CISA noemt twee voordelen van een build-SBOM: meer zekerheid dat de SBOM het artefact juist weergeeft, dankzij de informatie die tijdens de build en het CI/CD-proces beschikbaar is, en meer vertrouwen, omdat dezelfde buildworkflow zowel de SBOM als het artefact kan ondertekenen. Het genoemde nadeel is dat u het buildproces mogelijk moet wijzigen om er een te genereren. Bij CRACI is die wijziging één regel, runs-on: craci, en het vastleggen komt met de runner mee.
Een map scannen versus een build vastleggen
De meeste SBOM-generatoren zijn scanners. U richt er een op een broncodeboom of containerimage, en hij leidt af wat er volgens hem in is gegaan door manifests, lockfiles, geïnstalleerde pakketten of binaries te lezen. Dat is nuttig, en voor software die u niet zelf hebt gebouwd vaak de enige optie.
De zwakte is dat een scanner alleen ziet wat hij voor zich heeft. Is er geen lockfile gecommit, zijn de afhankelijkheden vóór de scan niet geïnstalleerd, of wordt het product als gecompileerde binary geleverd, dan heeft de scanner minder om mee te werken dan de build had. Een scan die helemaal geen componenten vindt, levert nog steeds een geldig SBOM-document op, en dat is het deel waar mensen de mist mee ingaan.
CRACI pakt het anders aan. Het genereert de SBOM in zijn eigen CI-runner, zodat het geleverde artefact ook het beschreven artefact is. Het is geen scan die ergens in de buurt heeft gedraaid.
SBOM basics: SPDX, CycloneDX and OpenVEX
Wat een SBOM vastlegt, hoe de formaten SPDX en CycloneDX in de praktijk verschillen en wat OpenVEX toevoegt over de vraag of een kwetsbaarheid een product echt raakt.
Petteri Pulkkinen en Erika Marttinen op Kubernetes Community Days Helsinki 2026, 16:50 tot 22:05 van SBOMbastic: Getting Ready for Upcoming EU Cybersecurity Regulation in Software Supply Chains.
Waar een scan van afhangt
Syft, Trivy, cdxgen, de Microsoft SBOM Tool, de dependency-graph-export van GitHub en de Socket CLI leiden allemaal een SBOM af uit wat ze kunnen lezen. De onderstaande patronen volgen uit hoe deze tools volgens hun documentatie werken.
Tools zijn het oneens over wat als component telt
Elke generator bepaalt zelf wat als component telt en welke bestanden hij vertrouwt, dus dezelfde vastgepinde commit geeft elk van hen een andere lijst.
Een deel daarvan zit in de naamgeving. Tools identificeren dezelfde GitHub Action onder verschillende package-URL-typen, waardoor ze hetzelfde kunnen vermelden en toch niet matchen. Dat hoort ook bij het probleem: een SBOM die een andere identifier voor een component gebruikt, is moeilijker te matchen met kwetsbaarheidsgegevens.
Of u eerst hebt geïnstalleerd, kan meer uitmaken dan de tool
Express 5.2.1 commit geen lockfile. Op een schone checkout heeft een generator die afhankelijkheden oplost uit lockfiles of een geïnstalleerde boom, geen van beide voor zich, dus geen van de npm-afhankelijkheden die in package.json zijn gedeclareerd, bereikt het document. Hij kan nog steeds een lijst teruggeven, gemaakt van wat hij verder herkent, zoals GitHub Actions en de workflowbestanden die ernaar verwijzen. Geen van deze tools is kapot.
Op een geïnstalleerde kopie van dezelfde repository verandert één Syft-vlag het antwoord opnieuw. Dezelfde tool, dezelfde boom, dezelfde versie. Een Syft-mapscan rapporteert bewust wat een project declareert, en de cataloger die geïnstalleerde pakketten leest, zit niet in de standaardset voor mappen.
Sommige software heeft geen manifest om te lezen
De repository anthropics/claude-code op v2.1.259 heeft geen enkel manifest met afhankelijkheden: geen package.json, lockfile of iets vergelijkbaars. Wat elke generator ervoor teruggeeft, is samengesteld uit andere bestandstypen, dus het antwoord zegt meer over de tool dan over het project.
Het gepubliceerde npm-pakket verschilt niet veel. @anthropic-ai/claude-code declareert nul runtime-afhankelijkheden en acht platformspecifieke pakketten die een voorgecompileerde binary verpakken. @openai/codex 0.149.1 heeft dezelfde vorm. Wat op de machine van de ontwikkelaar draait, is een gecompileerde binary waarin geen enkele manifestlezer kan kijken. Dit is geen fout in één tool. Het is een distributiemodel dat scannen achteraf niet kan beschrijven.
Versies en leveranciers
Waar twee tools hetzelfde pakket vinden, zijn ze het niet altijd eens over de versie. De een pint een GitHub Action vast op een commit-SHA, terwijl de ander een releasetag rapporteert. Een zoekopdracht naar kwetsbaarheden op de verkeerde versie geeft een zelfverzekerd fout antwoord.
Het leveranciersveld, een van de NTIA-minimumelementen, blijft vaak leeg, omdat de meeste generatoren er geen bron voor hebben.
Verborgen afhankelijkheden: wat geen enkele scan kan zien
Elke tool hierboven begint bij de lockfile of de uitvoer van de build en leidt af wat erin is gegaan. Een build haalt meer op dan dat. Deze verborgen afhankelijkheden staan in geen enkele lockfile, dus een scan kan ze niet vinden:
-
Install-hooks. De npm-scripts
preinstallenpostinstalldraaien tijdens de installatie en kunnen meer code downloaden. Zo draaide de Shai-Hulud-worm. - Buildscripts. Onder meer Rust-buildscripts downloaden tijdens de build wat ze nodig hebben.
- Base images. Een containerbuild haalt een base image en de besturingssysteemlagen daarvan op, die in geen enkele lockfile van een programmeertaal staan.
- Caches en buildtools. Pakketten die uit CI-caches worden teruggezet en tools die door setupstappen worden geïnstalleerd, komen ook buiten de lockfile om binnen.
De uitvoer achteraf scannen dicht dat gat niet. Bij een afgeronde binary, zeker bij gecompileerde code zoals Rust, moet de scanner raden wat erin is gegaan.
Twee builds, twee SBOM's
Er is nog een reden waarom een scan de build niet kan vervangen: de meeste builds zijn niet reproduceerbaar. Bouw dezelfde commit twee keer direct na elkaar, en er is geen garantie dat u dezelfde pakketten krijgt. Pakketbeheerders lossen pakketten op en installeren ze in de volgorde waarin antwoorden binnenkomen, wat vooral van belang is als er geen lockfile is, zoals bij veel Python-projecten, en buildstappen kunnen hun eigen willekeur bevatten. Byte-voor-byte reproduceerbare builds zijn mogelijk, met tools zoals Nix, maar ze zijn zeldzaam.
Een SBOM die later wordt gegenereerd, op een andere machine of uit een andere build, beschrijft dus een build die misschien nooit is geleverd. Alleen de SBOM die is vastgelegd tijdens de CI-build die een release heeft opgeleverd, bewijst wat er in productie draait.
Wat er verandert als u de build vastlegt
CRACI ziet het verkeer; scanners niet. Het draait uw GitHub Actions-jobs op zijn eigen runners en ziet het netwerkverkeer dat elke build binnenkomt, dus de SBOM vermeldt wat de job daadwerkelijk heeft binnengehaald, inclusief transitieve en verborgen afhankelijkheden, en niet wat een manifest zegt dat er nodig zou moeten zijn.
Bij caches wordt vastleggen tijdens de build lastig. Een pakket dat uit een cache wordt teruggezet, komt nooit bij een pakketregistry, dus een proxy alleen zou het missen. CRACI geeft het bewijs van afhankelijkheden mee met de cache, zodat pakketten die uit een cache worden teruggezet, in de SBOM blijven van de job die ze heeft teruggezet. Zie wat de GitHub Actions-cache verbergt.
Gedetecteerde ecosystemen zijn onder meer npm, PyPI, RubyGems, Cargo, Go, Nix, OCI, apt, apk en Git-bronnen, plus downloadpresets voor Yocto en OpenEmbedded. JFrog Artifactory-mirrors voor npm en PyPI werken. De metadata van gedeclareerde licenties zit in de export. SBOM's worden geëxporteerd in CycloneDX en SPDX. De pagina SBOM-generatie behandelt de volledige functionaliteit.
Het verkeer zien werkt ook de andere kant op. Een build heeft pakketten uit bekende bronnen nodig en publiceert zijn uitvoer naar bekende plekken. Het egressbeleid van CRACI staat alleen die toe en blokkeert elke andere uitgaande verbinding onmiddellijk, zodat malware die uw secrets naar de server van een aanvaller probeert te sturen, faalt. Omdat het beleid toestaat wat de build nodig heeft in plaats van te zoeken naar bekende slechte hosts, houdt het stand, zelfs als u het eerste doelwit bent van een aanval die nog niemand heeft gezien, zoals bij Shai-Hulud. Zie hoe een egressbeleid exfiltratie stopt.
Container- en Docker-SBOM's
Bij containerimages is het verschil het duidelijkst. De gebruikelijke manieren om een Docker-SBOM te krijgen, analyseren allemaal de image: Syft en Trivy scannen hem, docker scout sbom indexeert de inhoud wanneer de image geen SBOM-attestatie heeft, en de SBOM-attestatie van BuildKit, ingeschakeld met --sbom=true, draait standaard een op Syft gebaseerde scanner over de laatste imagefase. De oudere plug-in docker sbom is afgeschaft ten gunste van docker scout sbom.
Elk daarvan leest de afgeronde image, en ziet dus wat er nog herkenbaar is in de lagen. Ze zien niet wat de build onderweg heeft opgehaald. CRACI legt de job vast die de image bouwt: de images en pakketten die de job uit OCI-registries en pakketbronnen ophaalt, komen in dezelfde SBOM terecht als de rest van de build. Voor een image die u niet zelf hebt gebouwd, blijft scannen de praktische optie. Zie wat SBOM's van Docker-images missen.
Volledigheid, vermeld per job
Een vastgelegde SBOM is maar zo goed als de vastlegging. In plaats van elke SBOM als volledig te presenteren, vermeldt CRACI de volledigheid voor elke job en elke cache, in een van de vijf statussen:
- Complete. CRACI heeft volledig bewijs voor de job waargenomen en elke teruggezette cache was volledig.
- Complete with connections. Aan de eisen is voldaan, maar de job gebruikte een of meer eigen netwerkverbindingen, dus het is de moeite waard te bekijken wat daardoorheen is gekomen.
- Incomplete. CRACI heeft een omstandigheid vastgesteld waardoor afhankelijkheden in de SBOM kunnen ontbreken.
- Unavailable. CRACI kon de beoordeling van het bewijs niet afronden.
- Not recorded. De job is ouder dan de volledigheidsrapportage.
Volledigheid is transitief over caches. Zet een job een onvolledige cache terug, dan is zijn SBOM ook onvolledig, en elke cache die die job oplevert, neemt de onvolledige status mee. Zo weet u op welke SBOM's u kunt vertrouwen en welke een tweede blik verdienen.
The hard parts: hooks, noise and staying current
Drie problemen die de talk noemt: post-install-hooks die dingen ophalen die in geen enkel manifest staan, de afhankelijkheden vinden die ertoe doen in een lange lijst, en de SBOM actueel houden, wat betekent dat u die bij elke build in CI genereert.
Petteri Pulkkinen en Erika Marttinen op Kubernetes Community Days Helsinki 2026, 25:39 tot 27:22 van SBOMbastic: Getting Ready for Upcoming EU Cybersecurity Regulation in Software Supply Chains.
Herkomstgegevens via de API
De SBOM is één onderdeel van wat CRACI voor elke job vastlegt. De API levert SBOM's, netwerktraces en herkomstgegevens, allemaal vastgelegd uit dezelfde build.
Waar een scanner nog steeds het juiste gereedschap is
Vastleggen tijdens de build dekt software die u bouwt. Voor een image of binary van derden die u niet zelf hebt gebouwd, blijft een scanner de praktische keuze, en de bovenstaande tools zijn krachtig en veelgebruikt. Het punt is niet dat scanners slecht zijn. Het punt is dat de SBOM van uw eigen releases moet komen uit de build die ze heeft gemaakt.
Ook een CRACI-SBOM heeft zijn grenzen. Hij dekt jobs die op CRACI-runners draaien, en dat betekent vandaag GitHub Actions op Linux, met andere CI-systemen op de roadmap.
Kiest u tussen tools? Zie SBOM-tools vergeleken, of zie hoe SBOM's tijdens de build passen bij werk aan CRA-naleving. Zie wat open-source SBOM-generatoren missen voor Syft, Trivy en cdxgen naast elkaar.
SBOM's tijdens de build: veelgestelde vragen
Wat is een SBOM tijdens de build?
Een SBOM tijdens de build, door CISA een build-SBOM genoemd, wordt gegenereerd als onderdeel van het bouwen van het uitbrengbare artefact. In plaats van de componentenlijst achteraf uit bestanden te reconstrueren, beschrijft hij wat de build zelf heeft gebruikt. CRACI legt hem vast op basis van de pakketten die elke GitHub Actions-job daadwerkelijk heeft opgehaald.
Wat is het verschil tussen een build-SBOM en een geanalyseerde SBOM?
Een geanalyseerde SBOM wordt na de build gegenereerd door een artefact zoals een uitvoerbaar bestand of containerimage te analyseren, wat doorgaans heuristieken vereist. Een build-SBOM wordt tijdens de build gegenereerd, uit informatie die de build heeft. Geanalyseerde SBOM's werken zonder toegang tot de build, en daarom zijn ze de juiste keuze voor software die u niet zelf hebt gebouwd.
Hoe genereer ik een SBOM voor een Docker-image?
Scan een bestaande image met een generator zoals Syft, Trivy of docker scout sbom. BuildKit kan bij het bouwen ook een SBOM-attestatie toevoegen door de uiteindelijke image te scannen. Om vast te leggen wat de build van een image heeft binnengehaald, draait u de build op een CRACI-runner: de SBOM van de job dekt de images en pakketten die hij heeft opgehaald.
Wat zijn verborgen afhankelijkheden?
Alles wat een build ophaalt en in geen enkele lockfile staat: code die door npm-install-hooks wordt gedownload, bestanden die Rust-buildscripts tijdens de build downloaden, en de base image met de besturingssysteemlagen in een containerbuild. Tools die de lockfile lezen of de uitvoer scannen, missen ze. CRACI legt ze vast omdat het het verkeer ziet dat de build binnenkomt.
Waarom geven twee builds van dezelfde code verschillende SBOM's?
Omdat de meeste builds niet reproduceerbaar zijn. De pakketbeheerder lost pakketten op en installeert ze in de volgorde waarin antwoorden binnenkomen, vooral zonder lockfile, en buildstappen kunnen willekeur bevatten. Bouw dezelfde commit twee keer direct na elkaar, en de tweede build kan andere pakketten bevatten. Alleen de SBOM die is vastgelegd tijdens de build die een release heeft opgeleverd, bewijst wat die release bevat.
Is een SBOM tijdens de build altijd volledig?
Niet automatisch. Een vastlegging is maar zo goed als wat ze kan waarnemen, dus CRACI vermeldt de volledigheid voor elke job en elke cache: Complete, Complete with connections, Incomplete, Unavailable of Not recorded. Zo weet u op welke SBOM's u kunt vertrouwen en welke een tweede blik verdienen.
Vervangt een SBOM tijdens de build SBOM-scanners?
Voor de software die u bouwt, ja: de SBOM van uw releases moet komen uit de builds die ze hebben gemaakt. Voor images en binaries van derden die u niet zelf hebt gebouwd, blijft een scanner de praktische keuze. SBOM-tools vergeleken behandelt de opties.
Welke SBOM-generator gebruikt CRACI?
Geen. CRACI scant geen bestanden. Een proxy op zijn runners die pakketten herkent, legt de pakketten vast die elke job ophaalt uit npm, PyPI, RubyGems, Cargo, Go, Nix, OCI, apt, apk en Git-bronnen, plus downloadpresets voor Yocto en OpenEmbedded, en exporteert het resultaat in CycloneDX of SPDX.
Vergelijk een SBOM uit uw eigen build
Boek een demo en laat een van uw workflows op CRACI draaien. Leg de SBOM naast die van uw huidige tool en zie waar ze verschillen.
Demo boeken