Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure SPDX IDs are unique for upstream source packages #1588

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions pkg/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -831,8 +831,9 @@ func (b *Build) BuildPackage(ctx context.Context) error {
return fmt.Errorf("unable to run package %s pipeline: %w", b.Configuration.Name(), err)
}

for _, p := range pipelines {
pkg, err := p.SBOMPackageForUpstreamSource(b.Configuration.Package.LicenseExpression(), namespace)
for i, p := range pipelines {
uniqueID := strconv.Itoa(i)
pkg, err := p.SBOMPackageForUpstreamSource(b.Configuration.Package.LicenseExpression(), namespace, uniqueID)
if err != nil {
return fmt.Errorf("creating SBOM package for upstream source: %w", err)
}
Expand Down
30 changes: 27 additions & 3 deletions pkg/build/build_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import (
"archive/tar"
"compress/gzip"
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"testing"

"io"

"chainguard.dev/apko/pkg/sbom/generator/spdx"
"chainguard.dev/melange/pkg/container"
"chainguard.dev/melange/pkg/container/docker"
"github.com/google/go-cmp/cmp"
Expand All @@ -28,6 +30,10 @@ func TestBuild_BuildPackage(t *testing.T) {
name: "crane",
expectedVersion: "0.20.2-r1",
},
{
name: "7zip-two-fetches",
expectedVersion: "2301-r3",
},
}

const arch = "x86_64"
Expand Down Expand Up @@ -111,9 +117,27 @@ func TestBuild_BuildPackage(t *testing.T) {
t.Fatalf("reading actual SBOM: %v", err)
}

if diff := cmp.Diff(expected, actual); diff != "" {
t.Fatalf("SBOMs differ: \n%s\n", diff)
}
t.Run("goldenfile diff", func(t *testing.T) {
if diff := cmp.Diff(expected, actual); diff != "" {
t.Errorf("SBOMs differ: \n%s\n", diff)
}
})

t.Run("unique SPDX IDs", func(t *testing.T) {
doc := new(spdx.Document)
err := json.Unmarshal(actual, doc)
if err != nil {
t.Fatalf("unmarshalling SBOM: %v", err)
}

ids := make(map[string]struct{})
for _, p := range doc.Packages {
if _, ok := ids[p.ID]; ok {
t.Errorf("duplicate SPDX ID found: %s", p.ID)
}
ids[p.ID] = struct{}{}
}
})
})
})
})
Expand Down
65 changes: 65 additions & 0 deletions pkg/build/testdata/build_configs/7zip-two-fetches.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package:
name: 7zip-two-fetches
version: 2301
epoch: 3
description: "File archiver with a high compression ratio"
copyright:
- license: LGPL-2.0-only

environment:
contents:
packages:
- build-base
- busybox
- ca-certificates-bundle
- openssf-compiler-options

pipeline:
- uses: fetch
with:
uri: https://7-zip.org/a/7z${{package.version}}-src.tar.xz
expected-sha512: e39f660c023aa65e55388be225b5591fe2a5c9138693f3c9107e2eb4ce97fafde118d3375e01ada99d29de9633f56221b5b3d640c982178884670cd84c8aa986
strip-components: 0

- uses: fetch
with:
uri: https://7-zip.org/a/7z${{package.version}}-src.tar.xz
expected-sha512: e39f660c023aa65e55388be225b5591fe2a5c9138693f3c9107e2eb4ce97fafde118d3375e01ada99d29de9633f56221b5b3d640c982178884670cd84c8aa986
strip-components: 0

- name: Configure and build
runs: |
cd CPP/7zip/Bundles/Alone2
mkdir -p b/g
make -f ../../cmpl_gcc.mak -j$(nproc) \
CC="${CC:-cc} $CFLAGS $CPPFLAGS $LDFLAGS" \
CXX="${CXX:-c++} $CXXFLAGS $CPPFLAGS $LDFLAGS" \
DISABLE_RAR=1

- runs: |
install -Dm755 CPP/7zip/Bundles/Alone2/b/g/7zz "${{targets.destdir}}"/usr/bin/7zz
ln -s /usr/bin/7zz "${{targets.destdir}}"/usr/bin/7z
install -Dm644 DOC/* -t "${{targets.destdir}}"/usr/share/doc/7zip

- uses: strip

subpackages:
- name: "${{package.name}}-doc"
description: "7zip documentation"
pipeline:
- uses: split/manpages
- runs: |
mkdir -p "${{targets.subpkgdir}}"/usr/share
mv "${{targets.destdir}}"/usr/share/doc/7zip "${{targets.subpkgdir}}"/usr/share

update:
enabled: true
release-monitor:
identifier: 265148

test:
pipeline:
# AUTOGENERATED
- runs: |
7z --help
7zz --help
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
{
"SPDXID": "SPDXRef-DOCUMENT",
"name": "apk-7zip-two-fetches-2301-r3",
"spdxVersion": "SPDX-2.3",
"creationInfo": {
"created": "0001-01-01T00:00:00Z",
"creators": [
"Tool: melange (devel)",
"Organization: Chainguard, Inc"
],
"licenseListVersion": "3.22"
},
"dataLicense": "CC0-1.0",
"documentNamespace": "https://spdx.org/spdxdocs/chainguard/melange/aac0a1c9478955b98366443d654718707b4c3a03",
"documentDescribes": [
"SPDXRef-Package-7zip-two-fetches-2301-r3"
],
"packages": [
{
"SPDXID": "SPDXRef-Package-7zip-two-fetches-2301-r3",
"name": "7zip-two-fetches",
"versionInfo": "2301-r3",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "LGPL-2.0-only",
"downloadLocation": "NOASSERTION",
"originator": "Organization: Wolfi",
"supplier": "Organization: Wolfi",
"copyrightText": "\n",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:apk/wolfi/7zip-two-fetches@2301-r3?arch=x86_64",
"referenceType": "purl"
}
]
},
{
"SPDXID": "SPDXRef-Package-testdata-buildC95configs-7zip-two-fetches.yaml-c0ffee",
"name": "testdata/build_configs/7zip-two-fetches.yaml",
"versionInfo": "c0ffee",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"originator": "Organization: Wolfi",
"supplier": "Organization: Wolfi",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:github/wolfi-dev/os@c0ffee#testdata/build_configs/7zip-two-fetches.yaml",
"referenceType": "purl"
}
]
},
{
"SPDXID": "SPDXRef-Package-7zip-two-fetches-2301-0",
"name": "7zip-two-fetches",
"versionInfo": "2301",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"originator": "Organization: Wolfi",
"supplier": "Organization: Wolfi",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:generic/7zip-two-fetches@2301?checksum=sha512%3Ae39f660c023aa65e55388be225b5591fe2a5c9138693f3c9107e2eb4ce97fafde118d3375e01ada99d29de9633f56221b5b3d640c982178884670cd84c8aa986\u0026download_url=https%3A%2F%2F7-zip.org%2Fa%2F7z2301-src.tar.xz",
"referenceType": "purl"
}
]
},
{
"SPDXID": "SPDXRef-Package-7zip-two-fetches-2301-1",
"name": "7zip-two-fetches",
"versionInfo": "2301",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"originator": "Organization: Wolfi",
"supplier": "Organization: Wolfi",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:generic/7zip-two-fetches@2301?checksum=sha512%3Ae39f660c023aa65e55388be225b5591fe2a5c9138693f3c9107e2eb4ce97fafde118d3375e01ada99d29de9633f56221b5b3d640c982178884670cd84c8aa986\u0026download_url=https%3A%2F%2F7-zip.org%2Fa%2F7z2301-src.tar.xz",
"referenceType": "purl"
}
]
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-Package-7zip-two-fetches-2301-r3",
"relationshipType": "DESCRIBED_BY",
"relatedSpdxElement": "SPDXRef-Package-testdata-buildC95configs-7zip-two-fetches.yaml-c0ffee"
},
{
"spdxElementId": "SPDXRef-Package-7zip-two-fetches-2301-r3",
"relationshipType": "GENERATED_FROM",
"relatedSpdxElement": "SPDXRef-Package-7zip-two-fetches-2301-0"
},
{
"spdxElementId": "SPDXRef-Package-7zip-two-fetches-2301-r3",
"relationshipType": "GENERATED_FROM",
"relatedSpdxElement": "SPDXRef-Package-7zip-two-fetches-2301-1"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
]
},
{
"SPDXID": "SPDXRef-Package-github.com-google-go-containerregistry-v0.20.2-c195f151efe3369874c72662cd69ad43ee485128",
"SPDXID": "SPDXRef-Package-github.com-google-go-containerregistry-v0.20.2-c195f151efe3369874c72662cd69ad43ee485128-0",
"name": "go-containerregistry",
"versionInfo": "v0.20.2",
"filesAnalyzed": false,
Expand All @@ -81,7 +81,7 @@
{
"spdxElementId": "SPDXRef-Package-crane-0.20.2-r1",
"relationshipType": "GENERATED_FROM",
"relatedSpdxElement": "SPDXRef-Package-github.com-google-go-containerregistry-v0.20.2-c195f151efe3369874c72662cd69ad43ee485128"
"relatedSpdxElement": "SPDXRef-Package-github.com-google-go-containerregistry-v0.20.2-c195f151efe3369874c72662cd69ad43ee485128-0"
}
]
}
19 changes: 14 additions & 5 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ type Pipeline struct {
// upstream project into the build. This function helps with generating SBOMs
// for the package being built. If the pipeline step is not a fetch or
// git-checkout step, this function returns nil and no error.
func (p Pipeline) SBOMPackageForUpstreamSource(licenseDeclared, supplier string) (*sbom.Package, error) {
func (p Pipeline) SBOMPackageForUpstreamSource(licenseDeclared, supplier string, uniqueID string) (*sbom.Package, error) {
// TODO: It'd be great to detect the license from the source code itself. Such a
// feature could even eliminate the need for the package's license field in the
// build configuration.
Expand Down Expand Up @@ -438,11 +438,17 @@ func (p Pipeline) SBOMPackageForUpstreamSource(licenseDeclared, supplier string)
return nil, err
}

idComponents := []string{pkgName, pkgVersion}
if uniqueID != "" {
idComponents = append(idComponents, uniqueID)
}

return &sbom.Package{
Name: pkgName,
Version: pkgVersion,
Namespace: supplier,
PURL: pu,
IDComponents: idComponents,
Name: pkgName,
Version: pkgVersion,
Namespace: supplier,
PURL: pu,
}, nil

case "git-checkout":
Expand All @@ -464,6 +470,9 @@ func (p Pipeline) SBOMPackageForUpstreamSource(licenseDeclared, supplier string)
idComponents = append(idComponents, component)
}
}
if uniqueID != "" {
idComponents = append(idComponents, uniqueID)
}

if strings.HasPrefix(repo, "https://github.com/") {
namespace, name, _ := strings.Cut(strings.TrimPrefix(repo, "https://github.com/"), "/")
Expand Down
Loading