SBOM
Przykłady SBOM
Najszybszy sposób, by zrozumieć SBOM, to go przeczytać. Oto ta sama mała aplikacja w formatach CycloneDX i SPDX, wraz z objaśnieniem każdego pola, opisem słabego SBOM i sposobem sprawdzenia własnego.
Zaktualizowano
Aplikacja użyta w przykładach
Oba pliki opisują example-api 2.4.0, przykładową usługę Node.js z jedną bezpośrednią zależnością,
express. Express z kolei zależy od accepts, więc przykłady pokazują też jedną zależność
przechodnią. Prawdziwy SBOM aplikacji Express wymienia znacznie więcej komponentów, ale struktura jest taka sama.
Przykład SBOM CycloneDX
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"metadata": {
"timestamp": "2026-10-30T09:00:00Z",
"lifecycles": [{ "phase": "build" }],
"tools": {
"components": [
{ "type": "application", "name": "example-sbom-tool", "version": "1.0.0" }
]
},
"component": {
"type": "application",
"bom-ref": "example-api",
"name": "example-api",
"version": "2.4.0"
}
},
"components": [
{
"type": "library",
"bom-ref": "pkg:npm/[email protected]",
"name": "express",
"version": "4.21.2",
"purl": "pkg:npm/[email protected]",
"licenses": [{ "license": { "id": "MIT" } }]
},
{
"type": "library",
"bom-ref": "pkg:npm/[email protected]",
"name": "accepts",
"version": "1.3.8",
"purl": "pkg:npm/[email protected]",
"licenses": [{ "license": { "id": "MIT" } }]
}
],
"dependencies": [
{ "ref": "example-api", "dependsOn": ["pkg:npm/[email protected]"] },
{ "ref": "pkg:npm/[email protected]", "dependsOn": ["pkg:npm/[email protected]"] }
]
}
| Pole | Co mówi |
|---|---|
specVersion |
Której wersji formatu używa plik, aby narzędzia mogły go przetworzyć. |
serialNumber, version |
Który to SBOM i która jego wersja. |
metadata.lifecycles |
Kiedy powstał SBOM. Tutaj: w trakcie buildu. |
metadata.tools |
Jakie narzędzie go utworzyło. Dwa narzędzia mogą różnie opisać ten sam commit. |
metadata.component |
Produkt, który opisuje SBOM. |
purl |
Package URL, identyfikator pozwalający narzędziu dopasować komponent do biuletynów bezpieczeństwa. |
licenses |
Deklarowana licencja komponentu. |
dependencies |
Kto pobiera kogo. Dzięki temu accepts jest widoczny jako zależność przechodnia. |
Przykład SBOM SPDX
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "example-api-2.4.0",
"documentNamespace": "https://example.com/spdx/example-api-2.4.0",
"creationInfo": {
"created": "2026-10-30T09:00:00Z",
"creators": ["Tool: example-sbom-tool-1.0.0"]
},
"packages": [
{
"name": "example-api",
"SPDXID": "SPDXRef-Package-example-api",
"versionInfo": "2.4.0",
"downloadLocation": "NOASSERTION"
},
{
"name": "express",
"SPDXID": "SPDXRef-Package-express",
"versionInfo": "4.21.2",
"downloadLocation": "NOASSERTION",
"licenseDeclared": "MIT",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:npm/[email protected]"
}
]
},
{
"name": "accepts",
"SPDXID": "SPDXRef-Package-accepts",
"versionInfo": "1.3.8",
"downloadLocation": "NOASSERTION",
"licenseDeclared": "MIT",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:npm/[email protected]"
}
]
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relationshipType": "DESCRIBES",
"relatedSpdxElement": "SPDXRef-Package-example-api"
},
{
"spdxElementId": "SPDXRef-Package-example-api",
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-express"
},
{
"spdxElementId": "SPDXRef-Package-express",
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-Package-accepts"
}
]
}
Informacje są te same, ale inaczej ułożone. SPDX utrzymuje listę packages i podaje każde powiązanie między
elementami w relationships. licenseDeclared to informacja, którą pakiet podaje o sobie, a
downloadLocation może mieć wartość NOASSERTION, gdy narzędzie jej nie zna. Czym te dwa formaty
różnią się poza układem, wyjaśnia porównanie CycloneDX vs SPDX.
Jak wygląda słaby SBOM
Plik może być poprawny, a mimo to niemal bezużyteczny. Na co zwrócić uwagę:
- Brak relacji zależności. Płaska lista mówi, że pakiet jest obecny, ale nie mówi, co go pobrało, a to właśnie jest potrzebne, aby naprawić zależność przechodnią.
- Brak package URL. Bez identyfikatora takiego jak purl dopasowanie komponentu do biuletynów sprowadza się do zgadywania na podstawie nazw.
-
Brak licencji. Wiele pól z wartością
NOASSERTIONoznacza, że SBOM nie odpowie na pytania o licencje. - Podejrzanie mało komponentów. Garść komponentów dla dużej aplikacji zwykle oznacza tylko zależności bezpośrednie albo tylko to, co zadeklarował jeden lockfile.
- Brak informacji, kiedy i jak powstał. Bez informacji o narzędziu i kontekście generowania nie da się ocenić, co SBOM wiarygodnie obejmuje.
- Milczenie o lukach. Dobry SBOM mówi, czego nie mógł zobaczyć. Większość plików po prostu się kończy, a czytelnik zakłada, że są kompletne.
Jak sprawdzić własny SBOM
- Zweryfikować format. Przepuścić plik przez walidator schematu dla jego formatu i wersji, ponieważ narzędzie, które nie potrafi przetworzyć pliku, nie może z niego skorzystać.
- Sprawdzić pola. Porównać je z 2026 Minimum Elements lub listą, o którą poprosił klient.
- Wyszukać coś znanego. Wybrać zależność przechodnią, pakiet z obrazu bazowego i narzędzie buildu, a następnie sprawdzić, które z nich się pojawiają.
- Porównać dwa źródła. Wygenerować drugi SBOM inną metodą i przyjrzeć się różnicom. Każdy dodatkowy komponent w jednym pliku to pytanie o drugi.
SBOM z czasu buildu od CRACI
CRACI eksportuje te same formaty. Różni się źródło zawartości: CRACI rejestruje, co pobrało każde zadanie GitHub Actions, więc każdy komponent w SBOM faktycznie trafił do buildu, a pakiety przywrócone z cache CI są odpowiednio oznaczone. Akcje GitHub Actions uruchomione przez workflow również pojawiają się jako komponenty, przypięte do użytego commita. SBOM każdego zadania ma też stan kompletności, więc luki są jawnie podane i nie trzeba ich szukać samodzielnie. Zob. generowanie SBOM w czasie buildu lub porównanie narzędzi SBOM.
Przykłady SBOM: najczęściej zadawane pytania
Jak wygląda SBOM?
Zwykle jest to plik JSON, czasem XML. Zaczyna się od danych o samym SBOM, takich jak narzędzie i znacznik czasu, następnie wymienia każdy komponent z nazwą, wersją, identyfikatorem i licencją, a na końcu opisuje, jak komponenty zależą od siebie nawzajem.
Czy SBOM to plik JSON?
Zazwyczaj tak, ponieważ JSON to najczęściej używane kodowanie zarówno CycloneDX, jak i SPDX. Oba formaty mają też inne kodowania, a SPDX ma dodatkowo tekstową postać tag-value.
Czym jest purl w SBOM?
Purl, czyli package URL, to standardowy identyfikator pakietu, na przykład pkg:npm/[email protected]. W jednym ciągu podaje ekosystem, pakiet i wersję, dzięki czemu narzędzia mogą niezawodnie dopasować komponent do danych o podatnościach.
Ile komponentów powinien mieć SBOM?
Nie ma właściwej liczby, ale powinien obejmować zależności przechodnie, których jest zwykle znacznie więcej niż bezpośrednich. Bardzo mało komponentów dla dużej aplikacji zwykle oznacza, że narzędzie widziało tylko jej część.
Czy można zobaczyć SBOM projektu open source?
Czasami. Niektóre projekty publikują SBOM-y razem z wydaniami, a GitHub potrafi wyeksportować SBOM z grafu zależności repozytorium. Taki eksport odzwierciedla to, co repozytorium deklaruje, a nie to, czego użył konkretny build.
Proszę porównać swój SBOM z zarejestrowanym
Proszę uruchomić jeden workflow GitHub Actions na CRACI i zobaczyć, których komponentów nie wymienia Państwa obecny SBOM.
Umów demo