diff --git a/pkg/purl/purl.go b/pkg/purl/purl.go index d54d2dffd795..92887bb534e5 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,10 @@ 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 "", "" + } name := strings.ToLower(pkgName) return parsePkgName(name) } diff --git a/pkg/purl/purl_test.go b/pkg/purl/purl_test.go index 4c3688237d7a..dac5edd0decd 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: "", + Namespace: "", + Name: "", + Version: "", + }, + }, + }, { name: "hex package", typ: ftypes.Hex, diff --git a/pkg/sbom/spdx/marshal.go b/pkg/sbom/spdx/marshal.go index 1444d228f69a..e9793c80663b 100644 --- a/pkg/sbom/spdx/marshal.go +++ b/pkg/sbom/spdx/marshal.go @@ -325,7 +325,11 @@ func (m *Marshaler) pkgToSpdxPackage(t, pkgDownloadLocation string, class types. if err != nil { return spdx.Package{}, xerrors.Errorf("failed to parse purl (%s): %w", pkg.Name, err) } - pkgExtRefs := []*spdx.PackageExternalReference{purlExternalReference(packageURL.String())} + + var pkgExtRefs []*spdx.PackageExternalReference + if packageURL.Type != "" { + 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 {