From 2e0ab32b5274a041837fa8018c0668c4b55f42e4 Mon Sep 17 00:00:00 2001 From: Billy Lynch Date: Wed, 14 Jan 2026 11:09:45 -0500 Subject: [PATCH] sbom: Use layer digest for layer package version Currently we use the base OS version for the layer version, which confusingly sets most image layers for Wolfi based packages to an old looking package version which doesn't really have much to do with the image layer. For the top level image, we use the image digest. This change duplicates that behavior for image layers. --- .../golden/sboms/sbom-aarch64.spdx.json | 2 +- .../golden/sboms/sbom-x86_64.spdx.json | 2 +- pkg/sbom/generator/spdx/spdx.go | 8 +-- pkg/sbom/generator/spdx/spdx_test.go | 27 ++++++++ .../both-describes-methods.spdx.json | 1 - .../custom-license.spdx.json | 1 - .../describes-relationship.spdx.json | 1 - .../layer-with-digest.spdx.json | 66 +++++++++++++++++++ .../no-supplier.spdx.json | 1 - .../package-deduplicating.spdx.json | 1 - .../unbound-package-dedupe.spdx.json | 1 - 11 files changed, 99 insertions(+), 12 deletions(-) create mode 100644 pkg/sbom/generator/spdx/testdata/expected_image_sboms/layer-with-digest.spdx.json diff --git a/internal/cli/testdata/golden/sboms/sbom-aarch64.spdx.json b/internal/cli/testdata/golden/sboms/sbom-aarch64.spdx.json index e66c557fc..fbe0164cf 100644 --- a/internal/cli/testdata/golden/sboms/sbom-aarch64.spdx.json +++ b/internal/cli/testdata/golden/sboms/sbom-aarch64.spdx.json @@ -42,7 +42,7 @@ { "SPDXID": "SPDXRef-Package-sha256-b075b4a14ed0c1e236bac3448fa494c77772feb140cfad4033450e45010da27f", "name": "sha256:b075b4a14ed0c1e236bac3448fa494c77772feb140cfad4033450e45010da27f", - "versionInfo": "1.0.0", + "versionInfo": "sha256:b075b4a14ed0c1e236bac3448fa494c77772feb140cfad4033450e45010da27f", "filesAnalyzed": false, "description": "apko operating system layer", "downloadLocation": "NOASSERTION", diff --git a/internal/cli/testdata/golden/sboms/sbom-x86_64.spdx.json b/internal/cli/testdata/golden/sboms/sbom-x86_64.spdx.json index 7849da08d..9aef4ce99 100644 --- a/internal/cli/testdata/golden/sboms/sbom-x86_64.spdx.json +++ b/internal/cli/testdata/golden/sboms/sbom-x86_64.spdx.json @@ -42,7 +42,7 @@ { "SPDXID": "SPDXRef-Package-sha256-622ca92e75385bab9884a8c8c65c3f4a4c3dd0eafbd2a57f2762bafcb393a456", "name": "sha256:622ca92e75385bab9884a8c8c65c3f4a4c3dd0eafbd2a57f2762bafcb393a456", - "versionInfo": "1.0.0", + "versionInfo": "sha256:622ca92e75385bab9884a8c8c65c3f4a4c3dd0eafbd2a57f2762bafcb393a456", "filesAnalyzed": false, "description": "apko operating system layer", "downloadLocation": "NOASSERTION", diff --git a/pkg/sbom/generator/spdx/spdx.go b/pkg/sbom/generator/spdx/spdx.go index ba694bfce..17e41abc9 100644 --- a/pkg/sbom/generator/spdx/spdx.go +++ b/pkg/sbom/generator/spdx/spdx.go @@ -427,13 +427,13 @@ func (sx *SPDX) imagePackage(opts *options.Options) (p *Package) { // LayerPackage returns a package describing the layer func (sx *SPDX) layerPackage(opts *options.Options, layer v1.Descriptor) *Package { - layerPackageName := hashToString(layer.Digest) - mainPkgID := stringToIdentifier(layerPackageName) + layerDigest := hashToString(layer.Digest) + mainPkgID := stringToIdentifier(layerDigest) return &Package{ ID: fmt.Sprintf("SPDXRef-Package-%s", mainPkgID), - Name: layerPackageName, - Version: opts.OS.Version, + Name: layerDigest, + Version: layerDigest, FilesAnalyzed: false, Description: "apko operating system layer", DownloadLocation: NOASSERTION, diff --git a/pkg/sbom/generator/spdx/spdx_test.go b/pkg/sbom/generator/spdx/spdx_test.go index 9c5a5bada..53a3b7129 100644 --- a/pkg/sbom/generator/spdx/spdx_test.go +++ b/pkg/sbom/generator/spdx/spdx_test.go @@ -231,6 +231,33 @@ func TestSPDX_Generate(t *testing.T) { }, }, }, + { + name: "layer-with-digest", + opts: &options.Options{ + ImageInfo: options.ImageInfo{ + Layers: []v1.Descriptor{{ + Digest: v1.Hash{ + Algorithm: "sha256", + Hex: "abc123def456", + }, + }}, + }, + OS: options.OSInfo{ + Name: "unknown", + ID: "unknown", + Version: "3.0", + }, + FileName: "sbom", + Packages: []*apk.InstalledPackage{ + { + Package: apk.Package{ + Name: "libattr1", + Version: "2.5.1-r2", + }, + }, + }, + }, + }, } for _, tt := range tests { diff --git a/pkg/sbom/generator/spdx/testdata/expected_image_sboms/both-describes-methods.spdx.json b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/both-describes-methods.spdx.json index db96f3e7a..639ccbaf5 100644 --- a/pkg/sbom/generator/spdx/testdata/expected_image_sboms/both-describes-methods.spdx.json +++ b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/both-describes-methods.spdx.json @@ -19,7 +19,6 @@ { "SPDXID": "SPDXRef-Package-", "name": "", - "versionInfo": "3.0", "filesAnalyzed": false, "description": "apko operating system layer", "downloadLocation": "NOASSERTION", diff --git a/pkg/sbom/generator/spdx/testdata/expected_image_sboms/custom-license.spdx.json b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/custom-license.spdx.json index afd8caa44..5babbf83d 100644 --- a/pkg/sbom/generator/spdx/testdata/expected_image_sboms/custom-license.spdx.json +++ b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/custom-license.spdx.json @@ -19,7 +19,6 @@ { "SPDXID": "SPDXRef-Package-", "name": "", - "versionInfo": "3.0", "filesAnalyzed": false, "description": "apko operating system layer", "downloadLocation": "NOASSERTION", diff --git a/pkg/sbom/generator/spdx/testdata/expected_image_sboms/describes-relationship.spdx.json b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/describes-relationship.spdx.json index 7effffba0..d2e1e1e7c 100644 --- a/pkg/sbom/generator/spdx/testdata/expected_image_sboms/describes-relationship.spdx.json +++ b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/describes-relationship.spdx.json @@ -19,7 +19,6 @@ { "SPDXID": "SPDXRef-Package-", "name": "", - "versionInfo": "3.0", "filesAnalyzed": false, "description": "apko operating system layer", "downloadLocation": "NOASSERTION", diff --git a/pkg/sbom/generator/spdx/testdata/expected_image_sboms/layer-with-digest.spdx.json b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/layer-with-digest.spdx.json new file mode 100644 index 000000000..962af89b0 --- /dev/null +++ b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/layer-with-digest.spdx.json @@ -0,0 +1,66 @@ +{ + "SPDXID": "SPDXRef-DOCUMENT", + "name": "sbom-sha256:abc123def456", + "spdxVersion": "SPDX-2.3", + "creationInfo": { + "created": "0001-01-01T00:00:00Z", + "creators": [ + "Tool: apko (devel)", + "Organization: Chainguard, Inc" + ], + "licenseListVersion": "3.16" + }, + "dataLicense": "CC0-1.0", + "documentNamespace": "https://spdx.org/spdxdocs/apko/", + "documentDescribes": [ + "SPDXRef-Package-sha256-abc123def456" + ], + "packages": [ + { + "SPDXID": "SPDXRef-Package-sha256-abc123def456", + "name": "sha256:abc123def456", + "versionInfo": "sha256:abc123def456", + "filesAnalyzed": false, + "description": "apko operating system layer", + "downloadLocation": "NOASSERTION", + "supplier": "Organization: unknown", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:oci/image@sha256%3Aabc123def456?mediaType=\u0026os=linux", + "referenceType": "purl" + } + ] + }, + { + "SPDXID": "SPDXRef-OperatingSystem-unknown", + "name": "unknown", + "versionInfo": "3.0", + "filesAnalyzed": false, + "description": "Operating System", + "downloadLocation": "NOASSERTION", + "supplier": "Organization: unknown", + "primaryPackagePurpose": "OPERATING_SYSTEM" + }, + { + "SPDXID": "SPDXRef-Package-libattr1-2.5.1-r2", + "name": "libattr1", + "versionInfo": "2.5.1-r2", + "filesAnalyzed": false, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "GPL-2.0-or-later", + "downloadLocation": "NOASSERTION", + "originator": "Organization: unknown", + "supplier": "Organization: unknown", + "copyrightText": "TODO\n", + "externalRefs": [ + { + "referenceCategory": "PACKAGE_MANAGER", + "referenceLocator": "pkg:apk/wolfi/libattr1@2.5.1-r2?arch=x86_64", + "referenceType": "purl" + } + ] + } + ], + "relationships": [] +} diff --git a/pkg/sbom/generator/spdx/testdata/expected_image_sboms/no-supplier.spdx.json b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/no-supplier.spdx.json index 981f91d5f..b44b10db6 100644 --- a/pkg/sbom/generator/spdx/testdata/expected_image_sboms/no-supplier.spdx.json +++ b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/no-supplier.spdx.json @@ -19,7 +19,6 @@ { "SPDXID": "SPDXRef-Package-", "name": "", - "versionInfo": "3.0", "filesAnalyzed": false, "description": "apko operating system layer", "downloadLocation": "NOASSERTION", diff --git a/pkg/sbom/generator/spdx/testdata/expected_image_sboms/package-deduplicating.spdx.json b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/package-deduplicating.spdx.json index 4d3a07643..7da6be988 100644 --- a/pkg/sbom/generator/spdx/testdata/expected_image_sboms/package-deduplicating.spdx.json +++ b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/package-deduplicating.spdx.json @@ -19,7 +19,6 @@ { "SPDXID": "SPDXRef-Package-", "name": "", - "versionInfo": "3.0", "filesAnalyzed": false, "description": "apko operating system layer", "downloadLocation": "NOASSERTION", diff --git a/pkg/sbom/generator/spdx/testdata/expected_image_sboms/unbound-package-dedupe.spdx.json b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/unbound-package-dedupe.spdx.json index 6bc8eeb6c..17db87999 100644 --- a/pkg/sbom/generator/spdx/testdata/expected_image_sboms/unbound-package-dedupe.spdx.json +++ b/pkg/sbom/generator/spdx/testdata/expected_image_sboms/unbound-package-dedupe.spdx.json @@ -19,7 +19,6 @@ { "SPDXID": "SPDXRef-Package-", "name": "", - "versionInfo": "3.0", "filesAnalyzed": false, "description": "apko operating system layer", "downloadLocation": "NOASSERTION",