SBOM
SBOM:n pitäisi kuvata se buildi, joka toimitettiin
Buildin aikainen SBOM tallennetaan buildin ollessa käynnissä siitä liikenteestä, joka buildiin todella tuli. Useimmat SBOM-generaattorit sen sijaan kokoavat listan jälkikäteen lukitustiedostosta (lockfile) tai lopputuloksesta, jolloin niiltä jäävät huomaamatta piilevät riippuvuudet, jotka buildi hakee itse. Tässä kerrotaan miksi ja mitä buildin tallentaminen muuttaa.
Päivitetty
Build SBOM, source SBOM, analysoitu SBOM
CISA:n SBOM-tyyppejä koskeva ohjeistus jaottelee SBOM:t sen mukaan, milloin ja miten ne tehdään. Kolme tyyppiä kattaa lähes jokaisen käytössä olevan SBOM-generaattorin:
| Tyyppi | Miten se tehdään | Mitä siltä voi jäädä pois |
|---|---|---|
| Source | Lähdekooditiedostoista, manifesteista ja lukitustiedostoista, yleensä SCA-työkalulla | Ajonaikaiset, lisäosa- tai alustakomponentit; kaikki, mitä tiedostoissa ei ole ilmoitettu |
| Build | Osana buildia, joka luo julkaistavan artefaktin | Kaikki, mitä build-ympäristö ei näe, minkä vuoksi kattavuus pitäisi ilmoittaa |
| Analyzed | Buildin jälkeen analysoimalla suoritettava tiedosto, paketti tai konttikuva | Komponentit, joita työkalu ei tunnista; tulokset riippuvat heuristiikoista |
CISA mainitsee build SBOM:lle kaksi etua: suurempi varmuus siitä, että SBOM kuvaa artefaktin oikein, koska
buildin ja CI/CD-prosessin aikana on saatavilla tietoa, sekä suurempi luotettavuus, koska sama build-työnkulku
voi allekirjoittaa sekä SBOM:n että artefaktin. Haittana se mainitsee, että build-prosessia voi joutua muuttamaan
SBOM:n generoimiseksi. CRACI:lla muutos on yksi rivi, runs-on: craci, ja tallennus tulee
runnerin mukana.
Kansion skannaus vai buildin tallentaminen
Useimmat SBOM-generaattorit ovat skannereita. Osoitat sen lähdekoodipuuhun tai konttikuvaan, ja se päättelee, mitä siihen sen mielestä meni, lukemalla manifesteja, lukitustiedostoja, asennettuja paketteja tai binäärejä. Se on hyödyllistä, ja ohjelmistolle, jota et ole itse rakentanut, se on usein ainoa vaihtoehto.
Heikkous on se, että skanneri näkee vain sen, mitä sen edessä on. Jos lukitustiedostoa ei ole commitoitu, jos riippuvuuksia ei ole asennettu ennen skannausta tai jos tuote toimitetaan käännettynä binäärinä, skannerilla on vähemmän tietoa kuin buildilla oli. Skannaus, joka ei löydä yhtään komponenttia, tuottaa silti validin SBOM-dokumentin, ja juuri se yllättää monet.
CRACI toimii toisin. Se generoi SBOM:n omassa CI-runnerissaan, joten toimitettu artefakti on sama kuin kuvattu artefakti. Kyse ei ole jossain lähistöllä ajetusta skannauksesta.
SBOM basics: SPDX, CycloneDX and OpenVEX
Mitä SBOM kirjaa, miten SPDX- ja CycloneDX-formaatit eroavat käytännössä ja mitä OpenVEX lisää tietoon siitä, vaikuttaako haavoittuvuus tuotteeseen todella.
Petteri Pulkkinen ja Erika Marttinen, Kubernetes Community Days Helsinki 2026, 16:50–22:05 videosta SBOMbastic: Getting Ready for Upcoming EU Cybersecurity Regulation in Software Supply Chains.
Mistä skannaus riippuu
Syft, Trivy, cdxgen, Microsoft SBOM Tool, GitHubin riippuvuusgraafin vienti ja Socket CLI päättelevät kaikki SBOM:n siitä, mitä ne pystyvät lukemaan. Alla kuvatut ilmiöt seuraavat siitä, miten näiden työkalujen on dokumentoitu toimivan.
Työkalut ovat eri mieltä siitä, mikä on komponentti
Kukin generaattori päättää itse, mikä lasketaan komponentiksi ja mihin tiedostoihin luotetaan, joten sama lukittu commit tuottaa kullekin eri listan.
Osa tästä on nimeämistä. Työkalut tunnistavat saman GitHub Actionin eri package URL -tyypeillä, joten ne voivat luetella saman asian eivätkä silti täsmää. Sekin on osa ongelmaa: SBOM, joka käyttää komponentille eri tunnistetta, on vaikeampi verrata haavoittuvuustietoihin.
Se, asensitko ensin, voi merkitä enemmän kuin työkalu
Express 5.2.1 ei commitoi lukitustiedostoa. Puhtaasta checkoutista
generaattori, joka selvittää riippuvuudet lukitustiedostoista tai asennetusta puusta,
ei löydä kumpaakaan, joten yksikään tiedostossa
package.json ilmoitetuista npm-riippuvuuksista ei päädy dokumenttiin. Se voi silti palauttaa listan,
joka koostuu kaikesta muusta, mitä se tunnistaa, kuten GitHub Actionseista ja
niihin viittaavista työnkulkutiedostoista. Yksikään työkalu ei tässä ole rikki.
Saman repositorion asennetussa kopiossa yksi Syftin lippu muuttaa vastauksen jälleen. Sama työkalu, sama puu, sama versio. Syftin hakemistoskannaus raportoi suunnitellusti sen, mitä projekti ilmoittaa, eikä asennettuja paketteja lukeva cataloger kuulu hakemistojen oletusjoukkoon.
Joillakin ohjelmistoilla ei ole luettavaa manifestia
Repositoriossa anthropics/claude-code versiossa v2.1.259 ei ole minkäänlaista
riippuvuusmanifestia: ei package.json-tiedostoa, lukitustiedostoa eikä
vastaavaa. Se, mitä kukin generaattori sille palauttaa, kootaan muista
tiedostotyypeistä, joten vastaus kertoo enemmän työkalusta kuin projektista.
Julkaistu npm-paketti ei juuri eroa tästä. @anthropic-ai/claude-code ilmoittaa nolla ajonaikaista riippuvuutta ja
kahdeksan alustakohtaista pakettia, jotka käärivät valmiiksi käännetyn binäärin.
@openai/codex 0.149.1 on rakenteeltaan samanlainen. Kehittäjän
koneella ajetaan käännettyä binääriä, jonka sisään mikään manifestinlukija ei näe.
Kyse ei ole yksittäisen työkalun viasta. Kyse on jakelumallista, jota
jälkikäteen tehtävä skannaus ei pysty kuvaamaan.
Versiot ja toimittajat
Kun kaksi työkalua löytää saman paketin, ne eivät aina ole yhtä mieltä sen versiosta. Toinen voi lukita GitHub Actionin commitin SHA-tunnisteeseen, kun taas toinen raportoi julkaisutagin. Väärään versioon perustuva haavoittuvuushaku antaa varman mutta väärän vastauksen.
Toimittajakenttä, yksi NTIA:n vähimmäiselementeistä, jää usein tyhjäksi, koska useimmilla generaattoreilla ei ole sille lähdettä.
Piilevät riippuvuudet: mitä mikään skannaus ei näe
Jokainen yllä mainittu työkalu lähtee lukitustiedostosta tai buildin lopputuloksesta ja päättelee, mitä siihen meni. Buildi hakee enemmän kuin sen. Näitä piileviä riippuvuuksia ei ole missään lukitustiedostossa, joten skannaus ei löydä niitä:
-
Asennuskoukut. npm:n
preinstall- japostinstall-skriptit ajetaan asennuksen aikana, ja ne voivat ladata lisää koodia. Näin Shai-Hulud-mato toimi. - Build-skriptit. Muun muassa Rustin build-skriptit lataavat tarvitsemansa buildin aikana.
- Peruskuvat. Konttibuildi hakee peruskuvan ja sen käyttöjärjestelmäkerrokset, joita mikään kielen lukitustiedosto ei luettele.
- Välimuistit ja build-työkalut. CI-välimuisteista palautetut paketit ja asennusvaiheiden asentamat työkalut tulevat nekin lukitustiedoston ohi.
Lopputuloksen skannaaminen jälkikäteen ei sulje aukkoa. Valmis binääri, varsinkin käännetty koodi kuten Rust, jättää skannerin arvaamaan, mitä siihen meni.
Kaksi buildia, kaksi SBOM:ia
On toinenkin syy, miksi skannaus ei voi korvata buildia: useimmat buildit eivät ole toistettavia. Buildaa sama commit kahdesti peräkkäin, eikä ole takeita siitä, että saat samat paketit. Paketinhallintatyökalut selvittävät ja asentavat paketit siinä järjestyksessä, jossa vastaukset saapuvat, mikä merkitsee eniten silloin, kun lukitustiedostoa ei ole, kuten monissa Python-projekteissa, ja build-vaiheissa voi olla omaa satunnaisuuttaan. Tavulleen toistettavat buildit ovat mahdollisia esimerkiksi Nixin kaltaisilla työkaluilla, mutta ne ovat harvinaisia.
Myöhemmin, toisella koneella tai toisesta buildista generoitu SBOM kuvaa siis buildia, jota ei ehkä koskaan toimitettu. Vain julkaisun tuottaneen CI-buildin aikana tallennettu SBOM todistaa, mitä tuotannossa on.
Mitä buildin tallentaminen muuttaa
CRACI näkee liikenteen; skannerit eivät. Se ajaa GitHub Actions -jobisi omilla runnereillaan ja näkee jokaiseen buildiin tulevan verkkoliikenteen, joten SBOM luettelee sen, mitä jobi todella haki, transitiiviset ja piilevät riippuvuudet mukaan lukien, eikä sitä, mitä manifestin mukaan pitäisi tarvita.
Välimuistit ovat kohta, jossa buildin aikainen tallennus vaikeutuu. Välimuistista palautettu paketti ei koskaan käy pakettirekisterissä, joten pelkkä välityspalvelin ei näkisi sitä. CRACI kuljettaa riippuvuusnäytön välimuistin mukana, joten välimuistista palautetut paketit pysyvät sen jobin SBOM:ssa, joka ne palautti. Katso, mitä GitHub Actionsin välimuisti piilottaa.
Tunnistettuja ekosysteemejä ovat npm-, PyPI-, RubyGems-, Cargo-, Go-, Nix-, OCI-, apt-, apk- ja Git-lähteet sekä Yocton ja OpenEmbeddedin latausesiasetukset. JFrog Artifactoryn npm- ja PyPI-peilit toimivat. Ilmoitetut lisenssitiedot sisältyvät vientiin. SBOM:t viedään CycloneDX- ja SPDX-muodossa. Sivu SBOM-generointi kertoo koko ominaisuudesta.
Liikenteen näkeminen toimii myös toiseen suuntaan. Buildi tarvitsee paketteja tunnetuista lähteistä ja julkaisee lopputuloksensa tunnettuihin paikkoihin. CRACI:n egress-käytäntö sallii vain ne ja estää kaikki muut lähtevät yhteydet välittömästi, joten haittaohjelma, joka yrittää lähettää salaisuutesi hyökkääjän palvelimelle, epäonnistuu. Koska käytäntö sallii sen, mitä buildi tarvitsee, eikä etsi tunnettuja haitallisia palvelimia, se pitää, vaikka olisit ensimmäinen kohde hyökkäyksessä, jota kukaan ei ole ennen nähnyt, kuten Shai-Huludissa. Katso, miten egress-käytäntö estää tietojen ulosvuodon.
Kontti- ja Docker-SBOM:t
Konttikuvissa ero näkyy selvimmin. Tavalliset tavat saada Docker-SBOM analysoivat kaikki kuvan:
Syft ja Trivy skannaavat sen, docker scout sbom indeksoi sen sisällön, kun kuvassa ei ole SBOM-attestaatiota,
ja BuildKitin SBOM-attestaatio, joka otetaan käyttöön lipulla --sbom=true, ajaa oletuksena Syft-pohjaisen skannerin
kuvan viimeiselle vaiheelle. Vanhempi docker sbom -lisäosa on vanhentunut, ja sen korvaa
docker scout sbom.
Kaikki nämä lukevat valmiin kuvan, joten ne näkevät sen, mikä on kerroksissa vielä tunnistettavissa. Ne eivät näe sitä, mitä buildi haki matkan varrella. CRACI tallentaa kuvan rakentavan jobin: kuvat ja paketit, jotka jobi hakee OCI-rekistereistä ja pakettilähteistä, päätyvät samaan SBOM:iin kuin muu buildi. Kuvalle, jota et ole itse rakentanut, skannaus on edelleen käytännöllinen vaihtoehto. Katso, mitä Docker-kuvien SBOM:eilta jää huomaamatta.
Kattavuus ilmoitettuna jobikohtaisesti
Tallennettu SBOM on vain yhtä hyvä kuin tallennus. Sen sijaan että CRACI esittäisi jokaisen SBOM:n kattavana, se ilmoittaa kattavuuden jokaiselle jobille ja jokaiselle välimuistille yhtenä viidestä tilasta:
- Complete. CRACI havaitsi jobista kattavan näytön, ja jokainen palautettu välimuisti oli kattava.
- Complete with connections. Vaatimukset täyttyivät, mutta jobi käytti yhtä tai useampaa omaa verkkoyhteyttä, joten niiden kautta tulleet asiat kannattaa tarkistaa.
- Incomplete. CRACI tunnisti tilanteen, joka voi jättää riippuvuuksia SBOM:n ulkopuolelle.
- Unavailable. CRACI ei pystynyt viemään näytön arviointia loppuun.
- Not recorded. Jobi on ajettu ennen kattavuusraportoinnin käyttöönottoa.
Kattavuus periytyy välimuistien kautta. Jos jobi palauttaa puutteellisen välimuistin, myös sen SBOM on puutteellinen, ja jokainen välimuisti, jonka jobi tuottaa, kantaa puutteellisen tilan eteenpäin. Tiedät, mihin SBOM:eihin voit luottaa ja mitkä kaipaavat tarkempaa katsomista.
The hard parts: hooks, noise and staying current
Kolme esityksessä nostettua ongelmaa: post-install-hookit, jotka hakevat asioita, joita mikään manifesti ei luettele, olennaisten riippuvuuksien löytäminen pitkästä listasta ja SBOM:n pitäminen ajan tasalla, mikä tarkoittaa sen generointia CI:ssä jokaisessa buildissa.
Petteri Pulkkinen ja Erika Marttinen, Kubernetes Community Days Helsinki 2026, 25:39–27:22 videosta SBOMbastic: Getting Ready for Upcoming EU Cybersecurity Regulation in Software Supply Chains.
Alkuperätiedot API:n kautta
SBOM on yksi osa sitä, mitä CRACI tallentaa jokaisesta jobista. API palauttaa SBOM:t, verkkojäljet ja alkuperätiedot (provenance), jotka kaikki on tallennettu samasta buildista.
Milloin skanneri on yhä oikea työkalu
Buildin aikainen tallennus kattaa ohjelmistot, jotka rakennat itse. Kolmannen osapuolen kuvalle tai binäärille, jota et ole rakentanut, skanneri on edelleen käytännöllinen valinta, ja yllä mainitut työkalut ovat päteviä ja laajalti käytettyjä. Pointti ei ole se, että skannerit olisivat huonoja. Pointti on se, että omien julkaisujesi SBOM:n pitäisi tulla niistä buildeista, jotka ne tekivät.
Myös CRACI:n SBOM:lla on omat rajoituksensa. Se kattaa jobit, jotka ajetaan CRACI-runnereilla, mikä tarkoittaa toistaiseksi GitHub Actionsia Linuxilla; muut CI-järjestelmät ovat tiekartalla.
Valitsetko työkalujen välillä? Katso SBOM-työkalut vertailussa tai katso, miten buildin aikaiset SBOM:t sopivat CRA-vaatimustenmukaisuustyöhön. Syft, Trivy ja cdxgen rinnakkain: mitä avoimen lähdekoodin SBOM-generaattoreilta jää huomaamatta.
Buildin aikaiset SBOM:t: usein kysytyt kysymykset
Mikä on buildin aikainen SBOM?
Buildin aikainen SBOM, jota CISA kutsuu nimellä build SBOM, generoidaan osana julkaistavan artefaktin rakentamista. Sen sijaan että komponenttilista koottaisiin jälkikäteen tiedostoista, se kuvaa sitä, mitä buildi itse käytti. CRACI tallentaa sen paketeista, jotka kukin GitHub Actions -jobi todella haki.
Mitä eroa on build SBOM:lla ja analysoidulla SBOM:lla?
Analysoitu SBOM generoidaan buildin jälkeen analysoimalla artefakti, kuten suoritettava tiedosto tai konttikuva, mikä vaatii yleensä heuristiikkoja. Build SBOM generoidaan buildin aikana tiedoista, jotka buildilla on. Analysoidut SBOM:t toimivat ilman pääsyä buildiin, mikä tekee niistä oikean valinnan ohjelmistolle, jota et ole itse rakentanut.
Miten generoin SBOM:n Docker-kuvalle?
Olemassa olevan kuvan voit skannata generaattorilla, kuten Syftillä, Trivyllä tai docker scout sbom:lla. BuildKit voi myös liittää SBOM-attestaation buildatessasi skannaamalla valmiin kuvan. Jotta saat tallennettua sen, mitä kuvan buildi haki, aja buildi CRACI-runnerilla: jobin SBOM kattaa sen hakemat kuvat ja paketit.
Mitä piilevät riippuvuudet ovat?
Kaikki, mitä buildi hakee mutta mitä mikään lukitustiedosto ei luettele: npm:n asennuskoukkujen lataama koodi, tiedostot, jotka Rustin build-skriptit lataavat buildin aikana, sekä konttibuildin peruskuva ja sen käyttöjärjestelmäkerrokset. Lukitustiedostoa lukevat tai lopputuloksen skannaavat työkalut eivät huomaa niitä. CRACI tallentaa ne, koska se näkee buildiin tulevan liikenteen.
Miksi saman koodin kaksi buildia tuottavat eri SBOM:t?
Koska useimmat buildit eivät ole toistettavia. Paketinhallintatyökalu selvittää ja asentaa paketit siinä järjestyksessä, jossa vastaukset saapuvat, varsinkin ilman lukitustiedostoa, ja build-vaiheissa voi olla satunnaisuutta. Buildaa sama commit kahdesti peräkkäin, ja toinen buildi voi sisältää eri paketteja. Vain julkaisun tuottaneen buildin aikana tallennettu SBOM todistaa, mitä julkaisu sisältää.
Onko buildin aikainen SBOM aina kattava?
Ei automaattisesti. Tallenne on vain yhtä hyvä kuin se, mitä se pystyy havaitsemaan, joten CRACI ilmoittaa kattavuuden jokaiselle jobille ja jokaiselle välimuistille: Complete, Complete with connections, Incomplete, Unavailable tai Not recorded. Tiedät, mihin SBOM:eihin voit luottaa ja mitkä kaipaavat tarkempaa katsomista.
Korvaako buildin aikainen SBOM SBOM-skannerit?
Itse rakentamasi ohjelmiston osalta kyllä: julkaisujesi SBOM:n pitäisi tulla niistä buildeista, jotka ne tekivät. Kolmannen osapuolen kuville ja binääreille, joita et ole rakentanut, skanneri on edelleen käytännöllinen valinta. SBOM-työkalut vertailussa esittelee vaihtoehdot.
Mitä SBOM-generaattoria CRACI käyttää?
Ei mitään. CRACI ei skannaa tiedostoja. Sen runnereilla toimiva paketit tunnistava välityspalvelin tallentaa paketit, jotka kukin jobi hakee npm-, PyPI-, RubyGems-, Cargo-, Go-, Nix-, OCI-, apt-, apk- ja Git-lähteistä sekä Yocton ja OpenEmbeddedin latausesiasetuksista, ja vie tuloksen CycloneDX- tai SPDX-muodossa.
Vertaa oman buildisi SBOM:ia
Varaa demo ja aja yksi työnkuluistasi CRACI:lla. Aseta sen SBOM nykyisen työkalusi tuottaman rinnalle ja katso, missä ne eroavat.
Varaa demo