From 3ab655ec9bad7a53d4cdf2660fa45528fd01d37e Mon Sep 17 00:00:00 2001 From: javierfreire Date: Thu, 14 Sep 2023 22:28:30 +0200 Subject: [PATCH 1/3] fix(purl): trim a final slash --- pkg/purl/purl.go | 3 +++ pkg/purl/purl_test.go | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/pkg/purl/purl.go b/pkg/purl/purl.go index 2ab2e4e49c5c..361de5fb8977 100644 --- a/pkg/purl/purl.go +++ b/pkg/purl/purl.go @@ -408,6 +408,9 @@ func parseQualifier(pkg ftypes.Package) packageurl.Qualifiers { func parsePkgName(name string) (string, string) { var namespace string + if len(name) > 0 && name[len(name)-1] == '/' { + name = name[:len(name)-1] + } index := strings.LastIndex(name, "/") if index != -1 { namespace = name[:index] diff --git a/pkg/purl/purl_test.go b/pkg/purl/purl_test.go index defd24a309e8..c71c71af58a4 100644 --- a/pkg/purl/purl_test.go +++ b/pkg/purl/purl_test.go @@ -181,6 +181,22 @@ func TestNewPackageURL(t *testing.T) { }, }, }, + { + name: "golang package with a local path", + typ: ftypes.GoModule, + pkg: ftypes.Package{ + Name: "./private_repos/cnrm.googlesource.com/cnrm/", + Version: "(devel)", + }, + want: purl.PackageURL{ + PackageURL: packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Namespace: "./private_repos/cnrm.googlesource.com", + Name: "cnrm", + Version: "(devel)", + }, + }, + }, { name: "hex package", typ: ftypes.Hex, From f31ea9b781b73aa5986ecc8502f700d8127d6705 Mon Sep 17 00:00:00 2001 From: javierfreire Date: Mon, 18 Sep 2023 22:40:11 +0200 Subject: [PATCH 2/3] fix(purl): skip local Go packages --- pkg/purl/purl.go | 6 ++ pkg/purl/purl_test.go | 8 +-- pkg/sbom/spdx/marshal.go | 4 +- pkg/sbom/spdx/marshal_test.go | 112 ++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 5 deletions(-) diff --git a/pkg/purl/purl.go b/pkg/purl/purl.go index 361de5fb8977..2ed01b848985 100644 --- a/pkg/purl/purl.go +++ b/pkg/purl/purl.go @@ -194,6 +194,9 @@ func NewPackageURL(t string, metadata types.Metadata, pkg ftypes.Package) (Packa namespace, name = parseComposer(name) case packageurl.TypeGolang: namespace, name = parseGolang(name) + if (name == "") { + return PackageURL{PackageURL: *packageurl.NewPackageURL("", "", "", "", nil, "")}, nil + } case packageurl.TypeNPM: namespace, name = parseNpm(name) case packageurl.TypeSwift: @@ -310,6 +313,9 @@ func parseMaven(pkgName string) (string, string) { // ref. https://github.com/package-url/purl-spec/blob/a748c36ad415c8aeffe2b8a4a5d8a50d16d6d85f/PURL-TYPES.rst#golang func parseGolang(pkgName string) (string, string) { + if strings.HasPrefix(pkgName, "./") || strings.HasPrefix(pkgName, "../") { + return "", "" + } name := strings.ToLower(pkgName) return parsePkgName(name) } diff --git a/pkg/purl/purl_test.go b/pkg/purl/purl_test.go index c71c71af58a4..5e62c300b984 100644 --- a/pkg/purl/purl_test.go +++ b/pkg/purl/purl_test.go @@ -190,10 +190,10 @@ func TestNewPackageURL(t *testing.T) { }, want: purl.PackageURL{ PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "./private_repos/cnrm.googlesource.com", - Name: "cnrm", - Version: "(devel)", + Type: "", + Namespace: "", + Name: "", + Version: "", }, }, }, diff --git a/pkg/sbom/spdx/marshal.go b/pkg/sbom/spdx/marshal.go index 1444d228f69a..fafac8feab11 100644 --- a/pkg/sbom/spdx/marshal.go +++ b/pkg/sbom/spdx/marshal.go @@ -321,11 +321,13 @@ func (m *Marshaler) pkgToSpdxPackage(t, pkgDownloadLocation string, class types. pkgSrcInfo = fmt.Sprintf("%s: %s %s", SourcePackagePrefix, pkg.SrcName, utils.FormatSrcVersion(pkg)) } + var pkgExtRefs []*spdx.PackageExternalReference packageURL, err := purl.NewPackageURL(t, metadata, pkg) if err != nil { return spdx.Package{}, xerrors.Errorf("failed to parse purl (%s): %w", pkg.Name, err) + } else if packageURL.Type != "" { + pkgExtRefs = []*spdx.PackageExternalReference{purlExternalReference(packageURL.String())} } - pkgExtRefs := []*spdx.PackageExternalReference{purlExternalReference(packageURL.String())} var attrTexts []string attrTexts = appendAttributionText(attrTexts, PropertyPkgID, pkg.ID) diff --git a/pkg/sbom/spdx/marshal_test.go b/pkg/sbom/spdx/marshal_test.go index a0e9e0c21ded..2efdde8cfa65 100644 --- a/pkg/sbom/spdx/marshal_test.go +++ b/pkg/sbom/spdx/marshal_test.go @@ -836,6 +836,118 @@ func TestMarshaler_Marshal(t *testing.T) { }, }, }, + { + name: "go library local", + inputReport: types.Report{ + SchemaVersion: report.SchemaVersion, + ArtifactName: "go-artifact", + ArtifactType: ftypes.ArtifactFilesystem, + Results: types.Results{ + { + Target: "artifact", + Class: types.ClassLangPkg, + Type: ftypes.GoBinary, + Packages: []ftypes.Package{ + { + Name: "./private_repos/cnrm.googlesource.com/cnrm/", + Version: "(devel)", + }, + { + Name: "golang.org/x/crypto", + Version: "v0.0.1", + }, + }, + }, + }, + }, + wantSBOM: &spdx.Document{ + SPDXVersion: spdx.Version, + DataLicense: spdx.DataLicense, + SPDXIdentifier: "DOCUMENT", + DocumentName: "go-artifact", + DocumentNamespace: "http://aquasecurity.github.io/trivy/filesystem/go-artifact-3ff14136-e09f-4df9-80ea-000000000001", + CreationInfo: &spdx.CreationInfo{ + Creators: []common.Creator{ + { + Creator: "aquasecurity", + CreatorType: "Organization", + }, + { + Creator: "trivy-0.38.1", + CreatorType: "Tool", + }, + }, + Created: "2021-08-25T12:20:30Z", + }, + Packages: []*spdx.Package{ + { + PackageSPDXIdentifier: spdx.ElementID("Package-9164ae38c5cdf815"), + PackageDownloadLocation: "NONE", + PackageName: "./private_repos/cnrm.googlesource.com/cnrm/", + PackageVersion: "(devel)", + PackageLicenseConcluded: "NONE", + PackageLicenseDeclared: "NONE", + PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, + PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, + }, + { + PackageName: "go-artifact", + PackageSPDXIdentifier: "Filesystem-e340f27468b382be", + PackageDownloadLocation: "NONE", + PackageAttributionTexts: []string{ + "SchemaVersion: 2", + }, + PrimaryPackagePurpose: tspdx.PackagePurposeSource, + }, + { + PackageSPDXIdentifier: spdx.ElementID("Application-6666b83a5d554671"), + PackageDownloadLocation: "NONE", + PackageName: "gobinary", + PackageSourceInfo: "artifact", + PrimaryPackagePurpose: tspdx.PackagePurposeApplication, + }, + { + PackageSPDXIdentifier: spdx.ElementID("Package-8451f2bc8e1f45aa"), + PackageDownloadLocation: "NONE", + PackageName: "golang.org/x/crypto", + PackageVersion: "v0.0.1", + PackageLicenseConcluded: "NONE", + PackageLicenseDeclared: "NONE", + PackageExternalReferences: []*spdx.PackageExternalReference{ + { + Category: tspdx.CategoryPackageManager, + RefType: tspdx.RefTypePurl, + Locator: "pkg:golang/golang.org/x/crypto@v0.0.1", + }, + }, + PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, + PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, + }, + }, + Relationships: []*spdx.Relationship{ + { + RefA: spdx.DocElementID{ElementRefID: "DOCUMENT"}, + RefB: spdx.DocElementID{ElementRefID: "Filesystem-e340f27468b382be"}, + Relationship: "DESCRIBES", + }, + { + RefA: spdx.DocElementID{ElementRefID: "Filesystem-e340f27468b382be"}, + RefB: spdx.DocElementID{ElementRefID: "Application-6666b83a5d554671"}, + Relationship: "CONTAINS", + }, + { + RefA: spdx.DocElementID{ElementRefID: "Application-6666b83a5d554671"}, + RefB: spdx.DocElementID{ElementRefID: "Package-9164ae38c5cdf815"}, + Relationship: "CONTAINS", + }, + { + RefA: spdx.DocElementID{ElementRefID: "Application-6666b83a5d554671"}, + RefB: spdx.DocElementID{ElementRefID: "Package-8451f2bc8e1f45aa"}, + Relationship: "CONTAINS", + }, + }, + }, + }, } for _, tc := range testCases { From 775c59676481cb039183d9086f572b44c50dd5bb Mon Sep 17 00:00:00 2001 From: javierfreire Date: Tue, 19 Sep 2023 18:55:43 +0200 Subject: [PATCH 3/3] fix(purl): a few improvements --- pkg/purl/purl.go | 6 ++---- pkg/sbom/spdx/marshal.go | 6 ++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/purl/purl.go b/pkg/purl/purl.go index 2ed01b848985..9a4617b3b5a2 100644 --- a/pkg/purl/purl.go +++ b/pkg/purl/purl.go @@ -194,7 +194,7 @@ func NewPackageURL(t string, metadata types.Metadata, pkg ftypes.Package) (Packa namespace, name = parseComposer(name) case packageurl.TypeGolang: namespace, name = parseGolang(name) - if (name == "") { + if name == "" { return PackageURL{PackageURL: *packageurl.NewPackageURL("", "", "", "", nil, "")}, nil } case packageurl.TypeNPM: @@ -313,6 +313,7 @@ func parseMaven(pkgName string) (string, string) { // ref. https://github.com/package-url/purl-spec/blob/a748c36ad415c8aeffe2b8a4a5d8a50d16d6d85f/PURL-TYPES.rst#golang func parseGolang(pkgName string) (string, string) { + // The PURL will be skipped when the package name is a local path, since it can't identify a software package. if strings.HasPrefix(pkgName, "./") || strings.HasPrefix(pkgName, "../") { return "", "" } @@ -414,9 +415,6 @@ func parseQualifier(pkg ftypes.Package) packageurl.Qualifiers { func parsePkgName(name string) (string, string) { var namespace string - if len(name) > 0 && name[len(name)-1] == '/' { - name = name[:len(name)-1] - } index := strings.LastIndex(name, "/") if index != -1 { namespace = name[:index] diff --git a/pkg/sbom/spdx/marshal.go b/pkg/sbom/spdx/marshal.go index fafac8feab11..e9793c80663b 100644 --- a/pkg/sbom/spdx/marshal.go +++ b/pkg/sbom/spdx/marshal.go @@ -321,11 +321,13 @@ func (m *Marshaler) pkgToSpdxPackage(t, pkgDownloadLocation string, class types. pkgSrcInfo = fmt.Sprintf("%s: %s %s", SourcePackagePrefix, pkg.SrcName, utils.FormatSrcVersion(pkg)) } - var pkgExtRefs []*spdx.PackageExternalReference packageURL, err := purl.NewPackageURL(t, metadata, pkg) if err != nil { return spdx.Package{}, xerrors.Errorf("failed to parse purl (%s): %w", pkg.Name, err) - } else if packageURL.Type != "" { + } + + var pkgExtRefs []*spdx.PackageExternalReference + if packageURL.Type != "" { pkgExtRefs = []*spdx.PackageExternalReference{purlExternalReference(packageURL.String())} }