From 772d1d08f82a4db6013cd8ec3fc11ca409354948 Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Mon, 6 Nov 2023 09:29:13 +0600 Subject: [PATCH] fix(sbom): Use UUID as BomRef for packages with empty purl (#5448) --- pkg/k8s/scanner/scanner.go | 2 +- pkg/purl/purl.go | 13 ++++--- pkg/purl/purl_test.go | 56 +++++++++++------------------- pkg/report/github/github.go | 3 ++ pkg/sbom/cyclonedx/marshal.go | 28 ++++++++++----- pkg/sbom/cyclonedx/marshal_test.go | 23 ++++++++++++ pkg/sbom/spdx/marshal.go | 4 +-- 7 files changed, 78 insertions(+), 51 deletions(-) diff --git a/pkg/k8s/scanner/scanner.go b/pkg/k8s/scanner/scanner.go index f260e8b088e5..861699102c92 100644 --- a/pkg/k8s/scanner/scanner.go +++ b/pkg/k8s/scanner/scanner.go @@ -383,7 +383,7 @@ func clusterInfoToReportResources(allArtifact []*artifacts.Artifact) (*core.Comp return nil, xerrors.Errorf("failed to create PURL: %w", err) } imageComponents = append(imageComponents, &core.Component{ - PackageURL: &imagePURL, + PackageURL: imagePURL, Type: cdx.ComponentTypeContainer, Name: name, Version: cDigest, diff --git a/pkg/purl/purl.go b/pkg/purl/purl.go index a7bbbbfef6b3..947c6eb696be 100644 --- a/pkg/purl/purl.go +++ b/pkg/purl/purl.go @@ -199,7 +199,7 @@ func (p *PackageURL) BOMRef() string { } // nolint: gocyclo -func NewPackageURL(t ftypes.TargetType, metadata types.Metadata, pkg ftypes.Package) (PackageURL, error) { +func NewPackageURL(t ftypes.TargetType, metadata types.Metadata, pkg ftypes.Package) (*PackageURL, error) { var qualifiers packageurl.Qualifiers if metadata.OS != nil { qualifiers = parseQualifier(pkg) @@ -235,7 +235,7 @@ func NewPackageURL(t ftypes.TargetType, metadata types.Metadata, pkg ftypes.Pack case packageurl.TypeGolang: namespace, name = parseGolang(name) if name == "" { - return PackageURL{PackageURL: *packageurl.NewPackageURL("", "", "", "", nil, "")}, nil + return nil, nil } case packageurl.TypeNPM: namespace, name = parseNpm(name) @@ -246,12 +246,15 @@ func NewPackageURL(t ftypes.TargetType, metadata types.Metadata, pkg ftypes.Pack case packageurl.TypeOCI: purl, err := parseOCI(metadata) if err != nil { - return PackageURL{}, err + return nil, err } - return PackageURL{PackageURL: purl}, nil + if purl.Type == "" { + return nil, nil + } + return &PackageURL{PackageURL: purl}, nil } - return PackageURL{ + return &PackageURL{ PackageURL: *packageurl.NewPackageURL(ptype, namespace, name, ver, qualifiers, subpath), FilePath: pkg.FilePath, }, nil diff --git a/pkg/purl/purl_test.go b/pkg/purl/purl_test.go index dfb3bf841b28..c6195d003a77 100644 --- a/pkg/purl/purl_test.go +++ b/pkg/purl/purl_test.go @@ -20,7 +20,7 @@ func TestNewPackageURL(t *testing.T) { typ ftypes.TargetType pkg ftypes.Package metadata types.Metadata - want purl.PackageURL + want *purl.PackageURL wantErr string }{ { @@ -30,7 +30,7 @@ func TestNewPackageURL(t *testing.T) { Name: "org.springframework:spring-core", Version: "5.3.14", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeMaven, Namespace: "org.springframework", @@ -46,7 +46,7 @@ func TestNewPackageURL(t *testing.T) { Name: "org.springframework:spring-core", Version: "5.3.14", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeMaven, Namespace: "org.springframework", @@ -62,7 +62,7 @@ func TestNewPackageURL(t *testing.T) { Name: "@xtuc/ieee754", Version: "1.2.0", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeNPM, Namespace: "@xtuc", @@ -78,7 +78,7 @@ func TestNewPackageURL(t *testing.T) { Name: "lodash", Version: "4.17.21", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeNPM, Name: "lodash", @@ -93,7 +93,7 @@ func TestNewPackageURL(t *testing.T) { Name: "@xtuc/ieee754", Version: "1.2.0", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeNPM, Namespace: "@xtuc", @@ -109,7 +109,7 @@ func TestNewPackageURL(t *testing.T) { Name: "lodash", Version: "4.17.21", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeNPM, Name: "lodash", @@ -124,7 +124,7 @@ func TestNewPackageURL(t *testing.T) { Name: "Django_test", Version: "1.2.0", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypePyPi, Name: "django-test", @@ -139,7 +139,7 @@ func TestNewPackageURL(t *testing.T) { Name: "absl-py", Version: "0.4.1", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeConda, Name: "absl-py", @@ -154,7 +154,7 @@ func TestNewPackageURL(t *testing.T) { Name: "symfony/contracts", Version: "v1.0.2", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeComposer, Namespace: "symfony", @@ -170,7 +170,7 @@ func TestNewPackageURL(t *testing.T) { Name: "github.com/go-sql-driver/Mysql", Version: "v1.5.0", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeGolang, Namespace: "github.com/go-sql-driver", @@ -186,14 +186,7 @@ func TestNewPackageURL(t *testing.T) { Name: "./private_repos/cnrm.googlesource.com/cnrm/", Version: "(devel)", }, - want: purl.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: "", - Namespace: "", - Name: "", - Version: "", - }, - }, + want: nil, }, { name: "hex package", @@ -209,7 +202,7 @@ func TestNewPackageURL(t *testing.T) { }, }, }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeHex, Name: "bunt", @@ -224,7 +217,7 @@ func TestNewPackageURL(t *testing.T) { Name: "http", Version: "0.13.2", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: purl.TypeDart, Name: "http", @@ -240,7 +233,7 @@ func TestNewPackageURL(t *testing.T) { Name: "github.com/apple/swift-atomics", Version: "1.1.0", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeSwift, Namespace: "github.com/apple", @@ -257,7 +250,7 @@ func TestNewPackageURL(t *testing.T) { Name: "GoogleUtilities/NSData+zlib", Version: "7.5.2", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeCocoapods, Name: "GoogleUtilities", @@ -274,7 +267,7 @@ func TestNewPackageURL(t *testing.T) { Name: "abomination", Version: "0.7.3", }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeCargo, Name: "abomination", @@ -304,7 +297,7 @@ func TestNewPackageURL(t *testing.T) { Name: "8", }, }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeRPM, Namespace: "redhat", @@ -342,7 +335,7 @@ func TestNewPackageURL(t *testing.T) { Architecture: "amd64", }, }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeOCI, Namespace: "", @@ -372,14 +365,7 @@ func TestNewPackageURL(t *testing.T) { }, ImageID: "sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", }, - want: purl.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: "", - Namespace: "", - Name: "", - Version: "", - }, - }, + want: nil, }, { name: "container with implicit registry", @@ -397,7 +383,7 @@ func TestNewPackageURL(t *testing.T) { Architecture: "amd64", }, }, - want: purl.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeOCI, Namespace: "", diff --git a/pkg/report/github/github.go b/pkg/report/github/github.go index b17bc29fda9a..4a5007c79872 100644 --- a/pkg/report/github/github.go +++ b/pkg/report/github/github.go @@ -165,5 +165,8 @@ func buildPurl(t ftypes.TargetType, pkg ftypes.Package) (string, error) { if err != nil { return "", xerrors.Errorf("purl error: %w", err) } + if packageUrl == nil { + return "", nil + } return packageUrl.ToString(), nil } diff --git a/pkg/sbom/cyclonedx/marshal.go b/pkg/sbom/cyclonedx/marshal.go index 9ee79da064c1..c6934571c0a0 100644 --- a/pkg/sbom/cyclonedx/marshal.go +++ b/pkg/sbom/cyclonedx/marshal.go @@ -193,6 +193,11 @@ func (e *Marshaler) marshalPackage(pkg Package, pkgs map[string]Package, compone if err != nil { return nil, xerrors.Errorf("failed to parse pkg: %w", err) } + + // Skip component that can't be converted from `Package` + if component == nil { + return nil, nil + } components[pkg.ID] = component // Iterate dependencies @@ -234,8 +239,9 @@ func (e *Marshaler) rootComponent(r types.Report) (*core.Component, error) { p, err := purl.NewPackageURL(purl.TypeOCI, r.Metadata, ftypes.Package{}) if err != nil { return nil, xerrors.Errorf("failed to new package url for oci: %w", err) - } else if p.Type != "" { - root.PackageURL = &p + } + if p != nil { + root.PackageURL = p } case ftypes.ArtifactVM: @@ -315,11 +321,17 @@ func pkgComponent(pkg Package) (*core.Component, error) { } name := pkg.Name + version := pkg.Version var group string - // use `group` field for GroupID and `name` for ArtifactID for jar files - if pkg.Type == ftypes.Jar { - name = pu.Name - group = pu.Namespace + // there are cases when we can't build purl + // e.g. local Go packages + if pu != nil { + version = pu.Version + // use `group` field for GroupID and `name` for ArtifactID for jar files + if pkg.Type == ftypes.Jar { + name = pu.Name + group = pu.Namespace + } } properties := []core.Property{ @@ -369,8 +381,8 @@ func pkgComponent(pkg Package) (*core.Component, error) { Type: cdx.ComponentTypeLibrary, Name: name, Group: group, - Version: pu.Version, - PackageURL: &pu, + Version: version, + PackageURL: pu, Supplier: pkg.Maintainer, Licenses: pkg.Licenses, Hashes: lo.Ternary(pkg.Digest == "", nil, []digest.Digest{pkg.Digest}), diff --git a/pkg/sbom/cyclonedx/marshal_test.go b/pkg/sbom/cyclonedx/marshal_test.go index ddc68131ea0c..59f77a24ad63 100644 --- a/pkg/sbom/cyclonedx/marshal_test.go +++ b/pkg/sbom/cyclonedx/marshal_test.go @@ -168,6 +168,11 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "golang.org/x/crypto", Version: "v0.0.0-20210421170649-83a5a9bb288b", }, + // dependency has been replaced with local directory + { + Name: "./api", + Version: "(devel)", + }, }, }, }, @@ -302,6 +307,19 @@ func TestMarshaler_Marshal(t *testing.T) { }, }, }, + { + // Use UUID for local Go packages + BOMRef: "3ff14136-e09f-4df9-80ea-000000000007", + Type: cdx.ComponentTypeLibrary, + Name: "./api", + Version: "(devel)", + Properties: &[]cdx.Property{ + { + Name: "aquasecurity:trivy:PkgType", + Value: "gobinary", + }, + }, + }, { BOMRef: "pkg:gem/actioncontroller@7.0.0", Type: cdx.ComponentTypeLibrary, @@ -441,9 +459,14 @@ func TestMarshaler_Marshal(t *testing.T) { { Ref: "3ff14136-e09f-4df9-80ea-000000000006", Dependencies: &[]string{ + "3ff14136-e09f-4df9-80ea-000000000007", "pkg:golang/golang.org/x/crypto@v0.0.0-20210421170649-83a5a9bb288b", }, }, + { + Ref: "3ff14136-e09f-4df9-80ea-000000000007", + Dependencies: lo.ToPtr([]string{}), + }, { Ref: "pkg:gem/actioncontroller@7.0.0", Dependencies: &[]string{ diff --git a/pkg/sbom/spdx/marshal.go b/pkg/sbom/spdx/marshal.go index dbb72af2e69c..12e07e3de07d 100644 --- a/pkg/sbom/spdx/marshal.go +++ b/pkg/sbom/spdx/marshal.go @@ -233,7 +233,7 @@ func (m *Marshaler) rootPackage(r types.Report, pkgDownloadLocation string) (*sp // When the target is a container image, add PURL to the external references of the root package. if p, err := purl.NewPackageURL(purl.TypeOCI, r.Metadata, ftypes.Package{}); err != nil { return nil, xerrors.Errorf("failed to new package url for oci: %w", err) - } else if p.Type != "" { + } else if p != nil { externalReferences = append(externalReferences, purlExternalReference(p.ToString())) } @@ -327,7 +327,7 @@ func (m *Marshaler) pkgToSpdxPackage(t ftypes.TargetType, pkgDownloadLocation st } var pkgExtRefs []*spdx.PackageExternalReference - if packageURL.Type != "" { + if packageURL != nil { pkgExtRefs = []*spdx.PackageExternalReference{purlExternalReference(packageURL.String())} }