diff --git a/integration/testdata/conda-spdx.json.golden b/integration/testdata/conda-spdx.json.golden index a201662a7736..be1146b285c4 100644 --- a/integration/testdata/conda-spdx.json.golden +++ b/integration/testdata/conda-spdx.json.golden @@ -22,7 +22,7 @@ }, { "name": "openssl", - "SPDXID": "SPDXRef-Package-38e5db7a21fc70a8", + "SPDXID": "SPDXRef-Package-20b95c21bfbf9fc4", "versionInfo": "1.1.1q", "supplier": "NOASSERTION", "downloadLocation": "NONE", @@ -43,7 +43,7 @@ }, { "name": "pip", - "SPDXID": "SPDXRef-Package-f9844c873ead5dbe", + "SPDXID": "SPDXRef-Package-11a429ec3bd01d80", "versionInfo": "22.2.2", "supplier": "NOASSERTION", "downloadLocation": "NONE", @@ -110,21 +110,21 @@ }, { "spdxElementId": "SPDXRef-Application-ee5ef1aa4ac89125", - "relatedSpdxElement": "SPDXRef-Package-38e5db7a21fc70a8", + "relatedSpdxElement": "SPDXRef-Package-20b95c21bfbf9fc4", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-38e5db7a21fc70a8", + "spdxElementId": "SPDXRef-Package-20b95c21bfbf9fc4", "relatedSpdxElement": "SPDXRef-File-600e5e0110a84891", "relationshipType": "CONTAINS" }, { "spdxElementId": "SPDXRef-Application-ee5ef1aa4ac89125", - "relatedSpdxElement": "SPDXRef-Package-f9844c873ead5dbe", + "relatedSpdxElement": "SPDXRef-Package-11a429ec3bd01d80", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-f9844c873ead5dbe", + "spdxElementId": "SPDXRef-Package-11a429ec3bd01d80", "relatedSpdxElement": "SPDXRef-File-7eb62e2a3edddc0a", "relationshipType": "CONTAINS" } diff --git a/pkg/fanal/analyzer/sbom/sbom.go b/pkg/fanal/analyzer/sbom/sbom.go index 51b5178c781c..efb9829a1593 100644 --- a/pkg/fanal/analyzer/sbom/sbom.go +++ b/pkg/fanal/analyzer/sbom/sbom.go @@ -88,9 +88,6 @@ func handleBitnamiImages(componentPath string, bom types.SBOM) { // If the file path is empty, the file path will be set to the component dir path. filePath := path.Join(componentPath, pkg.FilePath) bom.Applications[i].Libraries[j].FilePath = filePath - if pkg.Identifier.PURL != nil && pkg.Identifier.PURL.FilePath != "" { - bom.Applications[i].Libraries[j].Identifier.PURL.FilePath = filePath - } } } } diff --git a/pkg/fanal/analyzer/sbom/sbom_test.go b/pkg/fanal/analyzer/sbom/sbom_test.go index 4e37834d04d8..096d0ececec5 100644 --- a/pkg/fanal/analyzer/sbom/sbom_test.go +++ b/pkg/fanal/analyzer/sbom/sbom_test.go @@ -35,13 +35,11 @@ func Test_sbomAnalyzer_Analyze(t *testing.T) { Version: "1.36.0", FilePath: "opt/bitnami/elasticsearch", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "co.elastic.apm", - Name: "apm-agent", - Version: "1.36.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "co.elastic.apm", + Name: "apm-agent", + Version: "1.36.0", }, }, }, @@ -50,13 +48,11 @@ func Test_sbomAnalyzer_Analyze(t *testing.T) { Version: "1.36.0", FilePath: "opt/bitnami/elasticsearch", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "co.elastic.apm", - Name: "apm-agent-cached-lookup-key", - Version: "1.36.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "co.elastic.apm", + Name: "apm-agent-cached-lookup-key", + Version: "1.36.0", }, }, }, @@ -65,13 +61,11 @@ func Test_sbomAnalyzer_Analyze(t *testing.T) { Version: "1.36.0", FilePath: "opt/bitnami/elasticsearch", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "co.elastic.apm", - Name: "apm-agent-common", - Version: "1.36.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "co.elastic.apm", + Name: "apm-agent-common", + Version: "1.36.0", }, }, }, @@ -80,13 +74,11 @@ func Test_sbomAnalyzer_Analyze(t *testing.T) { Version: "1.36.0", FilePath: "opt/bitnami/elasticsearch", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "co.elastic.apm", - Name: "apm-agent-core", - Version: "1.36.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "co.elastic.apm", + Name: "apm-agent-core", + Version: "1.36.0", }, }, }, @@ -102,16 +94,14 @@ func Test_sbomAnalyzer_Analyze(t *testing.T) { Arch: "arm64", Licenses: []string{"Elastic-2.0"}, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeBitnami, - Name: "elasticsearch", - Version: "8.9.1", - Qualifiers: packageurl.Qualifiers{ - { - Key: "arch", - Value: "arm64", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeBitnami, + Name: "elasticsearch", + Version: "8.9.1", + Qualifiers: packageurl.Qualifiers{ + { + Key: "arch", + Value: "arm64", }, }, }, @@ -137,14 +127,11 @@ func Test_sbomAnalyzer_Analyze(t *testing.T) { Name: "co.elastic.apm:apm-agent", Version: "1.36.0", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "co.elastic.apm", - Name: "apm-agent", - Version: "1.36.0", - }, - FilePath: "opt/bitnami/elasticsearch/modules/apm/elastic-apm-agent-1.36.0.jar", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "co.elastic.apm", + Name: "apm-agent", + Version: "1.36.0", }, BOMRef: "pkg:maven/co.elastic.apm/apm-agent@1.36.0", }, @@ -154,14 +141,11 @@ func Test_sbomAnalyzer_Analyze(t *testing.T) { Name: "co.elastic.apm:apm-agent-cached-lookup-key", Version: "1.36.0", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "co.elastic.apm", - Name: "apm-agent-cached-lookup-key", - Version: "1.36.0", - }, - FilePath: "opt/bitnami/elasticsearch/modules/apm/elastic-apm-agent-1.36.0.jar", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "co.elastic.apm", + Name: "apm-agent-cached-lookup-key", + Version: "1.36.0", }, BOMRef: "pkg:maven/co.elastic.apm/apm-agent-cached-lookup-key@1.36.0", }, @@ -187,12 +171,10 @@ func Test_sbomAnalyzer_Analyze(t *testing.T) { Version: "3.7.1", Licenses: []string{"MIT"}, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeBitnami, - Name: "gdal", - Version: "3.7.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeBitnami, + Name: "gdal", + Version: "3.7.1", }, }, }, @@ -201,12 +183,10 @@ func Test_sbomAnalyzer_Analyze(t *testing.T) { Version: "3.8.3", Licenses: []string{"LGPL-2.1-only"}, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeBitnami, - Name: "geos", - Version: "3.8.3", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeBitnami, + Name: "geos", + Version: "3.8.3", }, }, }, @@ -215,12 +195,10 @@ func Test_sbomAnalyzer_Analyze(t *testing.T) { Version: "15.3.0", Licenses: []string{"PostgreSQL"}, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeBitnami, - Name: "postgresql", - Version: "15.3.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeBitnami, + Name: "postgresql", + Version: "15.3.0", }, }, }, @@ -229,12 +207,10 @@ func Test_sbomAnalyzer_Analyze(t *testing.T) { Version: "6.3.2", Licenses: []string{"MIT"}, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeBitnami, - Name: "proj", - Version: "6.3.2", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeBitnami, + Name: "proj", + Version: "6.3.2", }, }, }, diff --git a/pkg/fanal/applier/applier_test.go b/pkg/fanal/applier/applier_test.go index 0994d46bfc39..8037e0bee922 100644 --- a/pkg/fanal/applier/applier_test.go +++ b/pkg/fanal/applier/applier_test.go @@ -151,17 +151,15 @@ func TestApplier_ApplyLayers(t *testing.T) { SrcName: "glibc", SrcVersion: "2.24-11+deb9u4", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeDebian, - Namespace: "debian", - Name: "libc6", - Version: "2.24-11+deb9u4", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "debian-9.9", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeDebian, + Namespace: "debian", + Name: "libc6", + Version: "2.24-11+deb9u4", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "debian-9.9", }, }, }, @@ -177,17 +175,15 @@ func TestApplier_ApplyLayers(t *testing.T) { SrcName: "tzdata", SrcVersion: "2019a-0+deb9u1", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeDebian, - Namespace: "debian", - Name: "tzdata", - Version: "2019a-0+deb9u1", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "debian-9.9", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeDebian, + Namespace: "debian", + Name: "tzdata", + Version: "2019a-0+deb9u1", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "debian-9.9", }, }, }, @@ -211,13 +207,11 @@ func TestApplier_ApplyLayers(t *testing.T) { DiffID: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "guzzlehttp", - Name: "guzzle", - Version: "6.2.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "guzzlehttp", + Name: "guzzle", + Version: "6.2.0", }, }, }, @@ -229,13 +223,11 @@ func TestApplier_ApplyLayers(t *testing.T) { DiffID: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "symfony", - Name: "process", - Version: "v4.2.7", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "symfony", + Name: "process", + Version: "v4.2.7", }, }, }, @@ -353,17 +345,15 @@ func TestApplier_ApplyLayers(t *testing.T) { Name: "busybox", Version: "1.30.1-r3", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "busybox", - Version: "1.30.1-r3", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.10.4", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeApk, + Namespace: "alpine", + Name: "busybox", + Version: "1.30.1-r3", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "3.10.4", }, }, }, @@ -377,17 +367,15 @@ func TestApplier_ApplyLayers(t *testing.T) { Name: "libcrypto1.1", Version: "1.1.1d-r2", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "libcrypto1.1", - Version: "1.1.1d-r2", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.10.4", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeApk, + Namespace: "alpine", + Name: "libcrypto1.1", + Version: "1.1.1d-r2", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "3.10.4", }, }, }, @@ -401,17 +389,15 @@ func TestApplier_ApplyLayers(t *testing.T) { Name: "libssl1.1", Version: "1.1.1d-r2", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "libssl1.1", - Version: "1.1.1d-r2", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.10.4", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeApk, + Namespace: "alpine", + Name: "libssl1.1", + Version: "1.1.1d-r2", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "3.10.4", }, }, }, @@ -425,17 +411,15 @@ func TestApplier_ApplyLayers(t *testing.T) { Name: "musl", Version: "1.1.22-r3", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "musl", - Version: "1.1.22-r3", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.10.4", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeApk, + Namespace: "alpine", + Name: "musl", + Version: "1.1.22-r3", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "3.10.4", }, }, }, @@ -450,17 +434,15 @@ func TestApplier_ApplyLayers(t *testing.T) { Version: "1.1.1d-r2", Identifier: types.PkgIdentifier{ //PURL: "pkg:apk/alpine/openssl@1.1.1d-r2?distro=3.10.4", - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "openssl", - Version: "1.1.1d-r2", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.10.4", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeApk, + Namespace: "alpine", + Name: "openssl", + Version: "1.1.1d-r2", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "3.10.4", }, }, }, @@ -684,13 +666,11 @@ func TestApplier_ApplyLayers(t *testing.T) { DiffID: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "guzzlehttp", - Name: "guzzle", - Version: "6.2.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "guzzlehttp", + Name: "guzzle", + Version: "6.2.0", }, }, }, @@ -702,13 +682,11 @@ func TestApplier_ApplyLayers(t *testing.T) { DiffID: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "symfony", - Name: "process", - Version: "v4.2.7", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "symfony", + Name: "process", + Version: "v4.2.7", }, }, }, @@ -896,13 +874,11 @@ func TestApplier_ApplyLayers(t *testing.T) { DiffID: "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "guzzlehttp", - Name: "guzzle", - Version: "6.2.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "guzzlehttp", + Name: "guzzle", + Version: "6.2.0", }, }, }, @@ -914,13 +890,11 @@ func TestApplier_ApplyLayers(t *testing.T) { DiffID: "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "symfony", - Name: "process", - Version: "v4.2.7", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "symfony", + Name: "process", + Version: "v4.2.7", }, }, }, diff --git a/pkg/fanal/applier/docker.go b/pkg/fanal/applier/docker.go index 68de804c89b1..730737e8a370 100644 --- a/pkg/fanal/applier/docker.go +++ b/pkg/fanal/applier/docker.go @@ -6,6 +6,7 @@ import ( "time" "github.com/knqyf263/nested" + "github.com/package-url/packageurl-go" "github.com/samber/lo" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" @@ -249,12 +250,13 @@ func ApplyLayers(layers []ftypes.BlobInfo) ftypes.ArtifactDetail { return mergedLayer } -func newPURL(pkgType ftypes.TargetType, metadata types.Metadata, pkg ftypes.Package) *ftypes.PackageURL { +func newPURL(pkgType ftypes.TargetType, metadata types.Metadata, pkg ftypes.Package) *packageurl.PackageURL { p, err := purl.New(pkgType, metadata, pkg) if err != nil { log.Logger.Errorf("Failed to create PackageURL: %s", err) + return nil } - return p + return p.Unwrap() } // aggregate merges all packages installed by pip/gem/npm/jar/conda into each application diff --git a/pkg/fanal/applier/docker_test.go b/pkg/fanal/applier/docker_test.go index a8076f3bb4e5..425930b9ba2e 100644 --- a/pkg/fanal/applier/docker_test.go +++ b/pkg/fanal/applier/docker_test.go @@ -1,10 +1,10 @@ package applier_test import ( - "github.com/package-url/packageurl-go" "sort" "testing" + "github.com/package-url/packageurl-go" "github.com/stretchr/testify/assert" "github.com/aquasecurity/trivy/pkg/fanal/applier" @@ -145,17 +145,15 @@ func TestApplyLayers(t *testing.T) { Version: "1.2.4", Release: "4.5.8", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "musl", - Version: "1.2.4-4.5.8", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.10", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeApk, + Namespace: "alpine", + Name: "musl", + Version: "1.2.4-4.5.8", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "3.10", }, }, }, @@ -170,17 +168,15 @@ func TestApplyLayers(t *testing.T) { Version: "1.2.3", Release: "4.5.6", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "openssl", - Version: "1.2.3-4.5.6", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.10", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeApk, + Namespace: "alpine", + Name: "openssl", + Version: "1.2.3-4.5.6", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "3.10", }, }, }, @@ -204,13 +200,10 @@ func TestApplyLayers(t *testing.T) { DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "activesupport", - Version: "6.0.2.1", - }, - FilePath: "var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "activesupport", + Version: "6.0.2.1", }, }, }, @@ -223,13 +216,10 @@ func TestApplyLayers(t *testing.T) { DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "gon", - Version: "6.3.2", - }, - FilePath: "usr/local/bundle/specifications/gon-6.3.2.gemspec", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "gon", + Version: "6.3.2", }, }, }, @@ -247,12 +237,10 @@ func TestApplyLayers(t *testing.T) { DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "gemlibrary1", - Version: "1.2.3", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "gemlibrary1", + Version: "1.2.3", }, }, }, @@ -474,12 +462,10 @@ func TestApplyLayers(t *testing.T) { DiffID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "rack", - Version: "4.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "rack", + Version: "4.0.0", }, }, }, @@ -491,12 +477,10 @@ func TestApplyLayers(t *testing.T) { DiffID: "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "rails", - Version: "6.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "rails", + Version: "6.0.0", }, }, }, @@ -514,12 +498,10 @@ func TestApplyLayers(t *testing.T) { DiffID: "sha256:aad63a9339440e7c3e1fff2b988991b9bfb81280042fa7f39a5e327023056819", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Name: "phplibrary1", - Version: "6.6.6", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Name: "phplibrary1", + Version: "6.6.6", }, }, }, @@ -779,17 +761,15 @@ func TestApplyLayers(t *testing.T) { Release: "4.5.7", Licenses: []string{"GPL-2"}, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeDebian, - Namespace: "debian", - Name: "libc", - Version: "1.2.4-4.5.7", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "debian-8", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeDebian, + Namespace: "debian", + Name: "libc", + Version: "1.2.4-4.5.7", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "debian-8", }, }, }, @@ -805,17 +785,15 @@ func TestApplyLayers(t *testing.T) { Release: "4.5.6", Licenses: []string{"OpenSSL"}, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeDebian, - Namespace: "debian", - Name: "openssl", - Version: "1.2.3-4.5.6", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "debian-8", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeDebian, + Namespace: "debian", + Name: "openssl", + Version: "1.2.3-4.5.6", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "debian-8", }, }, }, @@ -957,17 +935,15 @@ func TestApplyLayers(t *testing.T) { Version: "5.6.7", Release: "8", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeRPM, - Namespace: "redhat", - Name: "bash", - Version: "5.6.7-8", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "redhat-8", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Namespace: "redhat", + Name: "bash", + Version: "5.6.7-8", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "redhat-8", }, }, }, @@ -986,17 +962,15 @@ func TestApplyLayers(t *testing.T) { Version: "1.2.4", Release: "5", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeRPM, - Namespace: "redhat", - Name: "libc", - Version: "1.2.4-5", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "redhat-8", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Namespace: "redhat", + Name: "libc", + Version: "1.2.4-5", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "redhat-8", }, }, }, @@ -1017,17 +991,15 @@ func TestApplyLayers(t *testing.T) { Version: "1.2.3", Release: "4", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeRPM, - Namespace: "redhat", - Name: "openssl", - Version: "1.2.3-4", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "redhat-8", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Namespace: "redhat", + Name: "openssl", + Version: "1.2.3-4", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "redhat-8", }, }, }, diff --git a/pkg/fanal/artifact/image/remote_sbom_test.go b/pkg/fanal/artifact/image/remote_sbom_test.go index 6b21e2200233..3e445fc0b9ee 100644 --- a/pkg/fanal/artifact/image/remote_sbom_test.go +++ b/pkg/fanal/artifact/image/remote_sbom_test.go @@ -84,17 +84,15 @@ func TestArtifact_InspectRekorAttestation(t *testing.T) { Name: "musl", Version: "1.2.3-r0", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "musl", - Version: "1.2.3-r0", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.16.2", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeApk, + Namespace: "alpine", + Name: "musl", + Version: "1.2.3-r0", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "3.16.2", }, }, }, @@ -235,13 +233,11 @@ func TestArtifact_inspectOCIReferrerSBOM(t *testing.T) { Name: "github.com/opencontainers/go-digest", Version: "v1.0.0", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "github.com/opencontainers", - Name: "go-digest", - Version: "v1.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Namespace: "github.com/opencontainers", + Name: "go-digest", + Version: "v1.0.0", }, BOMRef: "pkg:golang/github.com/opencontainers/go-digest@v1.0.0", }, @@ -250,13 +246,11 @@ func TestArtifact_inspectOCIReferrerSBOM(t *testing.T) { Name: "golang.org/x/sync", Version: "v0.1.0", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "golang.org/x", - Name: "sync", - Version: "v0.1.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Namespace: "golang.org/x", + Name: "sync", + Version: "v0.1.0", }, BOMRef: "pkg:golang/golang.org/x/sync@v0.1.0", }, diff --git a/pkg/fanal/artifact/sbom/sbom_test.go b/pkg/fanal/artifact/sbom/sbom_test.go index e58437141ea8..f0286d4c3198 100644 --- a/pkg/fanal/artifact/sbom/sbom_test.go +++ b/pkg/fanal/artifact/sbom/sbom_test.go @@ -50,17 +50,15 @@ func TestArtifact_Inspect(t *testing.T) { DiffID: "sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "musl", - Version: "1.2.3-r0", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.16.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeApk, + Namespace: "alpine", + Name: "musl", + Version: "1.2.3-r0", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "3.16.0", }, }, }, @@ -82,13 +80,11 @@ func TestArtifact_Inspect(t *testing.T) { DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "log", - Version: "1.13.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "log", + Version: "1.13.1", }, BOMRef: "pkg:composer/pear/log@1.13.1", }, @@ -101,13 +97,11 @@ func TestArtifact_Inspect(t *testing.T) { DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "pear_exception", - Version: "v1.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "pear_exception", + Version: "v1.0.0", }, BOMRef: "pkg:composer/pear/pear_exception@v1.0.0", }, @@ -125,13 +119,11 @@ func TestArtifact_Inspect(t *testing.T) { DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1", }, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "github.com/package-url", - Name: "packageurl-go", - Version: "v0.1.1-0.20220203205134-d70459300c8a", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Namespace: "github.com/package-url", + Name: "packageurl-go", + Version: "v0.1.1-0.20220203205134-d70459300c8a", }, BOMRef: "pkg:golang/github.com/package-url/packageurl-go@v0.1.1-0.20220203205134-d70459300c8a", }, @@ -150,14 +142,11 @@ func TestArtifact_Inspect(t *testing.T) { }, FilePath: "app/maven/target/child-project-1.0.jar", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.codehaus.mojo", - Name: "child-project", - Version: "1.0", - }, - FilePath: "app/maven/target/child-project-1.0.jar", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.codehaus.mojo", + Name: "child-project", + Version: "1.0", }, BOMRef: "pkg:maven/org.codehaus.mojo/child-project@1.0?file_path=app%2Fmaven%2Ftarget%2Fchild-project-1.0.jar", }, @@ -177,13 +166,10 @@ func TestArtifact_Inspect(t *testing.T) { }, FilePath: "app/app/package.json", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeNPM, - Name: "bootstrap", - Version: "5.0.2", - }, - FilePath: "app/app/package.json", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeNPM, + Name: "bootstrap", + Version: "5.0.2", }, BOMRef: "pkg:npm/bootstrap@5.0.2?file_path=app%2Fapp%2Fpackage.json", }, @@ -226,17 +212,15 @@ func TestArtifact_Inspect(t *testing.T) { SrcVersion: "1.2.3-r0", Licenses: []string{"MIT"}, Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "musl", - Version: "1.2.3-r0", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.16.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeApk, + Namespace: "alpine", + Name: "musl", + Version: "1.2.3-r0", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "3.16.0", }, }, }, @@ -258,13 +242,11 @@ func TestArtifact_Inspect(t *testing.T) { Name: "pear/log", Version: "1.13.1", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "log", - Version: "1.13.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "log", + Version: "1.13.1", }, BOMRef: "pkg:composer/pear/log@1.13.1", }, @@ -277,13 +259,11 @@ func TestArtifact_Inspect(t *testing.T) { Name: "pear/pear_exception", Version: "v1.0.0", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "pear_exception", - Version: "v1.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "pear_exception", + Version: "v1.0.0", }, BOMRef: "pkg:composer/pear/pear_exception@v1.0.0", }, @@ -301,13 +281,11 @@ func TestArtifact_Inspect(t *testing.T) { Name: "github.com/package-url/packageurl-go", Version: "v0.1.1-0.20220203205134-d70459300c8a", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "github.com/package-url", - Name: "packageurl-go", - Version: "v0.1.1-0.20220203205134-d70459300c8a", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Namespace: "github.com/package-url", + Name: "packageurl-go", + Version: "v0.1.1-0.20220203205134-d70459300c8a", }, BOMRef: "pkg:golang/github.com/package-url/packageurl-go@v0.1.1-0.20220203205134-d70459300c8a", }, @@ -325,14 +303,11 @@ func TestArtifact_Inspect(t *testing.T) { Name: "org.codehaus.mojo:child-project", Version: "1.0", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.codehaus.mojo", - Name: "child-project", - Version: "1.0", - }, - FilePath: "app/maven/target/child-project-1.0.jar", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.codehaus.mojo", + Name: "child-project", + Version: "1.0", }, BOMRef: "pkg:maven/org.codehaus.mojo/child-project@1.0?file_path=app%2Fmaven%2Ftarget%2Fchild-project-1.0.jar", }, @@ -351,13 +326,10 @@ func TestArtifact_Inspect(t *testing.T) { Name: "bootstrap", Version: "5.0.2", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeNPM, - Name: "bootstrap", - Version: "5.0.2", - }, - FilePath: "app/app/package.json", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeNPM, + Name: "bootstrap", + Version: "5.0.2", }, BOMRef: "pkg:npm/bootstrap@5.0.2?file_path=app%2Fapp%2Fpackage.json", }, diff --git a/pkg/fanal/handler/unpackaged/unpackaged_test.go b/pkg/fanal/handler/unpackaged/unpackaged_test.go index 40c3cff54fdd..a37748101144 100644 --- a/pkg/fanal/handler/unpackaged/unpackaged_test.go +++ b/pkg/fanal/handler/unpackaged/unpackaged_test.go @@ -46,13 +46,11 @@ func Test_unpackagedHook_Handle(t *testing.T) { Name: "github.com/spf13/cobra", Version: "1.5.0", Identifier: types.PkgIdentifier{ - PURL: &types.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "github.com/spf13", - Name: "cobra", - Version: "1.5.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Namespace: "github.com/spf13", + Name: "cobra", + Version: "1.5.0", }, BOMRef: "pkg:golang/github.com/spf13/cobra@1.5.0", }, diff --git a/pkg/fanal/types/artifact.go b/pkg/fanal/types/artifact.go index b715cb7eeb77..2d8e491fc72c 100644 --- a/pkg/fanal/types/artifact.go +++ b/pkg/fanal/types/artifact.go @@ -1,9 +1,11 @@ package types import ( + "encoding/json" "time" v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/package-url/packageurl-go" "github.com/samber/lo" "github.com/aquasecurity/trivy/pkg/digest" @@ -102,8 +104,51 @@ type Package struct { // PkgIdentifier represents a software identifiers in one of more of the supported formats. type PkgIdentifier struct { - PURL *PackageURL `json:",omitempty"` - BOMRef string `json:",omitempty"` // For CycloneDX + PURL *packageurl.PackageURL `json:"-"` + BOMRef string `json:",omitempty"` // For CycloneDX +} + +// MarshalJSON customizes the JSON encoding of PkgIdentifier. +func (id *PkgIdentifier) MarshalJSON() ([]byte, error) { + var p string + if id.PURL != nil { + p = id.PURL.String() + } + + type Alias PkgIdentifier + return json.Marshal(&struct { + PURL string `json:",omitempty"` + *Alias + }{ + PURL: p, + Alias: (*Alias)(id), + }) +} + +// UnmarshalJSON customizes the JSON decoding of PkgIdentifier. +func (id *PkgIdentifier) UnmarshalJSON(data []byte) error { + type Alias PkgIdentifier + aux := &struct { + PURL string `json:",omitempty"` + *Alias + }{ + Alias: (*Alias)(id), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + if aux.PURL != "" { + p, err := packageurl.FromString(aux.PURL) + if err != nil { + return err + } else if len(p.Qualifiers) == 0 { + p.Qualifiers = nil + } + id.PURL = &p + } + + return nil } func (id *PkgIdentifier) Empty() bool { diff --git a/pkg/fanal/types/purl.go b/pkg/fanal/types/purl.go deleted file mode 100644 index 53ad1cbdc0ec..000000000000 --- a/pkg/fanal/types/purl.go +++ /dev/null @@ -1,78 +0,0 @@ -package types - -import ( - "encoding/json" - - "github.com/package-url/packageurl-go" - "golang.org/x/xerrors" -) - -type PackageURL struct { - packageurl.PackageURL - FilePath string -} - -func (p *PackageURL) BOMRef() string { - // 'bom-ref' must be unique within BOM, but PURLs may conflict - // when the same packages are installed in an artifact. - // In that case, we prefer to make PURLs unique by adding file paths, - // rather than using UUIDs, even if it is not PURL technically. - // ref. https://cyclonedx.org/use-cases/#dependency-graph - purl := p.PackageURL // so that it will not override the qualifiers below - if p.FilePath != "" { - purl.Qualifiers = append(purl.Qualifiers, - packageurl.Qualifier{ - Key: "file_path", - Value: p.FilePath, - }, - ) - } - return purl.String() -} - -func (p *PackageURL) MarshalJSON() ([]byte, error) { - if p == nil { - return nil, nil - } - return json.Marshal(p.String()) -} - -func (p *PackageURL) UnmarshalJSON(b []byte) error { - var s string - if err := json.Unmarshal(b, &s); err != nil { - return err - } - purl, err := NewPackageURL(s) - if err != nil { - return xerrors.Errorf("failed to parse purl(%s): %w", string(b), err) - } - *p = *purl - return nil -} - -func NewPackageURL(s string) (*PackageURL, error) { - p, err := packageurl.FromString(s) - if err != nil { - return nil, xerrors.Errorf("failed to parse purl(%s): %w", s, err) - } - - // Take out and delete the file path from qualifiers - var filePath string - for i, q := range p.Qualifiers { - if q.Key != "file_path" { - continue - } - filePath = q.Value - p.Qualifiers = append(p.Qualifiers[:i], p.Qualifiers[i+1:]...) - break - } - - if len(p.Qualifiers) == 0 { - p.Qualifiers = nil - } - - return &PackageURL{ - PackageURL: p, - FilePath: filePath, - }, nil -} diff --git a/pkg/k8s/scanner/scanner.go b/pkg/k8s/scanner/scanner.go index bbc7913ae745..ca08f8a86bd1 100644 --- a/pkg/k8s/scanner/scanner.go +++ b/pkg/k8s/scanner/scanner.go @@ -577,7 +577,7 @@ func nodeComponent(nf bom.NodeInfo) *core.Component { Namespace: k8sCoreComponentNamespace, }, }, - PackageURL: &ftypes.PackageURL{ + PackageURL: &purl.PackageURL{ PackageURL: *packageurl.NewPackageURL(golang, "", runtimeName, runtimeVersion, packageurl.Qualifiers{}, ""), }, }, @@ -601,7 +601,7 @@ func toProperties(props map[string]string, namespace string) []core.Property { return properties } -func generatePURL(name, ver, nodeName string) *ftypes.PackageURL { +func generatePURL(name, ver, nodeName string) *purl.PackageURL { var namespace string // Identify k8s distribution. An empty namespace means upstream. @@ -611,7 +611,7 @@ func generatePURL(name, ver, nodeName string) *ftypes.PackageURL { namespace = "" } - return &ftypes.PackageURL{ + return &purl.PackageURL{ PackageURL: *packageurl.NewPackageURL(purl.TypeK8s, namespace, name, ver, nil, ""), } } diff --git a/pkg/k8s/scanner/scanner_test.go b/pkg/k8s/scanner/scanner_test.go index 5f70facd3cf6..ec6d78f73c05 100644 --- a/pkg/k8s/scanner/scanner_test.go +++ b/pkg/k8s/scanner/scanner_test.go @@ -2,7 +2,6 @@ package scanner import ( "context" - ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "sort" "testing" @@ -98,7 +97,7 @@ func TestScanner_Scan(t *testing.T) { Namespace: k8sCoreComponentNamespace, }, }, - PackageURL: &ftypes.PackageURL{ + PackageURL: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: purl.TypeK8s, Name: "k8s.io/kubernetes", @@ -110,7 +109,7 @@ func TestScanner_Scan(t *testing.T) { Type: cdx.ComponentTypeApplication, Name: "k8s.io/apiserver", Version: "1.21.1", - PackageURL: &ftypes.PackageURL{ + PackageURL: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: purl.TypeK8s, Name: "k8s.io/apiserver", @@ -123,7 +122,7 @@ func TestScanner_Scan(t *testing.T) { Type: cdx.ComponentTypeContainer, Name: "k8s.gcr.io/kube-apiserver", Version: "sha256:18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f", - PackageURL: &ftypes.PackageURL{ + PackageURL: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: "oci", Name: "kube-apiserver", @@ -234,7 +233,7 @@ func TestScanner_Scan(t *testing.T) { Namespace: k8sCoreComponentNamespace, }, }, - PackageURL: &ftypes.PackageURL{ + PackageURL: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: "k8s", Name: "k8s.io/kubelet", @@ -258,7 +257,7 @@ func TestScanner_Scan(t *testing.T) { Namespace: k8sCoreComponentNamespace, }, }, - PackageURL: &ftypes.PackageURL{ + PackageURL: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: "golang", Name: "github.com/containerd/containerd", diff --git a/pkg/module/serialize/types_easyjson.go b/pkg/module/serialize/types_easyjson.go index 3ccb7e194271..cd2826c716ea 100644 --- a/pkg/module/serialize/types_easyjson.go +++ b/pkg/module/serialize/types_easyjson.go @@ -1617,7 +1617,9 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes(in *jlexer.Lexer, case "PkgPath": out.PkgPath = string(in.String()) case "PkgIdentifier": - easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes9(in, &out.PkgIdentifier) + if data := in.Raw(); in.Ok() { + in.AddError((out.PkgIdentifier).UnmarshalJSON(data)) + } case "InstalledVersion": out.InstalledVersion = string(in.String()) case "FixedVersion": @@ -1843,7 +1845,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes(out *jwriter.Write } else { out.RawString(prefix) } - easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes9(out, in.PkgIdentifier) + out.Raw((in.PkgIdentifier).MarshalJSON()) } if in.InstalledVersion != "" { const prefix string = ",\"InstalledVersion\":" @@ -2215,71 +2217,6 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyDbPkgTypes(out *jwriter.Wri } out.RawByte('}') } -func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes9(in *jlexer.Lexer, out *types1.PkgIdentifier) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeFieldName(false) - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "PURL": - if in.IsNull() { - in.Skip() - out.PURL = nil - } else { - if out.PURL == nil { - out.PURL = new(types1.PackageURL) - } - if data := in.Raw(); in.Ok() { - in.AddError((*out.PURL).UnmarshalJSON(data)) - } - } - case "BOMRef": - out.BOMRef = string(in.String()) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes9(out *jwriter.Writer, in types1.PkgIdentifier) { - out.RawByte('{') - first := true - _ = first - if in.PURL != nil { - const prefix string = ",\"PURL\":" - first = false - out.RawString(prefix[1:]) - out.Raw((*in.PURL).MarshalJSON()) - } - if in.BOMRef != "" { - const prefix string = ",\"BOMRef\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.String(string(in.BOMRef)) - } - out.RawByte('}') -} func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes(in *jlexer.Lexer, out *types1.Package) { isTopLevel := in.IsStart() if in.IsNull() { @@ -2304,7 +2241,9 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes(in *jlexer.Le case "Name": out.Name = string(in.String()) case "Identifier": - easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes9(in, &out.Identifier) + if data := in.Raw(); in.Ok() { + in.AddError((out.Identifier).UnmarshalJSON(data)) + } case "Version": out.Version = string(in.String()) case "Release": @@ -2358,7 +2297,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes(in *jlexer.Le if out.BuildInfo == nil { out.BuildInfo = new(types1.BuildInfo) } - easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes10(in, out.BuildInfo) + easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes9(in, out.BuildInfo) } case "Indirect": out.Indirect = bool(in.Bool()) @@ -2475,7 +2414,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes(out *jwriter. } else { out.RawString(prefix) } - easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes9(out, in.Identifier) + out.Raw((in.Identifier).MarshalJSON()) } if in.Version != "" { const prefix string = ",\"Version\":" @@ -2614,7 +2553,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes(out *jwriter. } else { out.RawString(prefix) } - easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes10(out, *in.BuildInfo) + easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes9(out, *in.BuildInfo) } if in.Indirect { const prefix string = ",\"Indirect\":" @@ -2715,7 +2654,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes(out *jwriter. } out.RawByte('}') } -func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes10(in *jlexer.Lexer, out *types1.BuildInfo) { +func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes9(in *jlexer.Lexer, out *types1.BuildInfo) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -2771,7 +2710,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes10(in *jlexer. in.Consumed() } } -func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes10(out *jwriter.Writer, in types1.BuildInfo) { +func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes9(out *jwriter.Writer, in types1.BuildInfo) { out.RawByte('{') first := true _ = first diff --git a/pkg/purl/purl.go b/pkg/purl/purl.go index c63c4fbb849f..bd62ce57cef8 100644 --- a/pkg/purl/purl.go +++ b/pkg/purl/purl.go @@ -43,13 +43,135 @@ const ( TypeUnknown = "unknown" ) -func FromString(s string) (*ftypes.PackageURL, error) { - return ftypes.NewPackageURL(s) +type PackageURL struct { + packageurl.PackageURL + FilePath string +} + +func FromString(s string) (*PackageURL, error) { + p, err := packageurl.FromString(s) + if err != nil { + return nil, xerrors.Errorf("failed to parse purl(%s): %w", s, err) + } + + // Take out and delete the file path from qualifiers + var filePath string + for i, q := range p.Qualifiers { + if q.Key != "file_path" { + continue + } + filePath = q.Value + p.Qualifiers = append(p.Qualifiers[:i], p.Qualifiers[i+1:]...) + break + } + + if len(p.Qualifiers) == 0 { + p.Qualifiers = nil + } + + return &PackageURL{ + PackageURL: p, + FilePath: filePath, + }, nil +} + +// nolint: gocyclo +func New(t ftypes.TargetType, metadata types.Metadata, pkg ftypes.Package) (*PackageURL, error) { + qualifiers := parseQualifier(pkg) + pkg.Epoch = 0 // we moved Epoch to qualifiers so we don't need it in version + + ptype := purlType(t) + name := pkg.Name + ver := utils.FormatVersion(pkg) + namespace := "" + subpath := "" + + switch ptype { + case packageurl.TypeRPM: + ns, qs := parseRPM(metadata.OS, pkg.Modularitylabel) + namespace = string(ns) + qualifiers = append(qualifiers, qs...) + case packageurl.TypeDebian: + qualifiers = append(qualifiers, parseDeb(metadata.OS)...) + if metadata.OS != nil { + namespace = string(metadata.OS.Family) + } + case packageurl.TypeApk: + var qs packageurl.Qualifiers + name, namespace, qs = parseApk(name, metadata.OS) + qualifiers = append(qualifiers, qs...) + case packageurl.TypeMaven, string(ftypes.Gradle): // TODO: replace with packageurl.TypeGradle once they add it. + namespace, name = parseMaven(name) + case packageurl.TypePyPi: + name = parsePyPI(name) + case packageurl.TypeComposer: + namespace, name = parseComposer(name) + case packageurl.TypeGolang: + namespace, name = parseGolang(name) + if name == "" { + return nil, nil + } + case packageurl.TypeNPM: + namespace, name = parseNpm(name) + case packageurl.TypeSwift: + namespace, name = parseSwift(name) + case packageurl.TypeCocoapods: + name, subpath = parseCocoapods(name) + case packageurl.TypeOCI: + purl, err := parseOCI(metadata) + if err != nil { + return nil, err + } else if purl.Type == "" { + return nil, nil + } + return &PackageURL{PackageURL: purl}, nil + } + + return &PackageURL{ + PackageURL: *packageurl.NewPackageURL(ptype, namespace, name, ver, qualifiers, subpath), + FilePath: pkg.FilePath, + }, nil +} + +// WithPath wraps packageurl.PackageURL with the given file path +func WithPath(purl *packageurl.PackageURL, filePath string) *PackageURL { + if purl == nil { + return nil + } + return &PackageURL{ + PackageURL: *purl, + FilePath: filePath, + } +} + +func (p *PackageURL) BOMRef() string { + // 'bom-ref' must be unique within BOM, but PURLs may conflict + // when the same packages are installed in an artifact. + // In that case, we prefer to make PURLs unique by adding file paths, + // rather than using UUIDs, even if it is not PURL technically. + // ref. https://cyclonedx.org/use-cases/#dependency-graph + purl := p.PackageURL // so that it will not override the qualifiers below + if p.FilePath != "" { + purl.Qualifiers = append(purl.Qualifiers, + packageurl.Qualifier{ + Key: "file_path", + Value: p.FilePath, + }, + ) + } + return purl.String() +} + +func (p *PackageURL) Unwrap() *packageurl.PackageURL { + if p == nil { + return nil + } + return &p.PackageURL } // LangType returns an application type in Trivy // nolint: gocyclo -func LangType(p *ftypes.PackageURL) ftypes.LangType { +func (p *PackageURL) LangType() ftypes.LangType { switch p.Type { case packageurl.TypeComposer: return ftypes.Composer @@ -102,13 +224,13 @@ func LangType(p *ftypes.PackageURL) ftypes.LangType { } } -func Class(p *ftypes.PackageURL) types.ResultClass { +func (p *PackageURL) Class() types.ResultClass { switch p.Type { case packageurl.TypeApk, packageurl.TypeDebian, packageurl.TypeRPM: // OS packages return types.ClassOSPkg default: - if LangType(p) == TypeUnknown { + if p.LangType() == TypeUnknown { return types.ClassUnknown } // Language-specific packages @@ -116,10 +238,13 @@ func Class(p *ftypes.PackageURL) types.ResultClass { } } -func ToPackage(p *ftypes.PackageURL) *ftypes.Package { +func (p *PackageURL) Package() *ftypes.Package { pkg := &ftypes.Package{ Name: p.Name, Version: p.Version, + Identifier: ftypes.PkgIdentifier{ + PURL: p.Unwrap(), + }, } for _, q := range p.Qualifiers { switch q.Key { @@ -151,11 +276,10 @@ func ToPackage(p *ftypes.PackageURL) *ftypes.Package { // Return packages without namespace. // OS packages are not supposed to have namespace. - if p.Namespace == "" || Class(p) == types.ClassOSPkg { + if p.Namespace == "" || p.Class() == types.ClassOSPkg { return pkg } - // TODO: replace with packageurl.TypeGradle once they add it. if p.Type == packageurl.TypeMaven || p.Type == packageurl.TypeGradle { // Maven and Gradle packages separate ":" // e.g. org.springframework:spring-core @@ -167,63 +291,34 @@ func ToPackage(p *ftypes.PackageURL) *ftypes.Package { return pkg } -// nolint: gocyclo -func New(t ftypes.TargetType, metadata types.Metadata, pkg ftypes.Package) (*ftypes.PackageURL, error) { - qualifiers := parseQualifier(pkg) - pkg.Epoch = 0 // we moved Epoch to qualifiers so we don't need it in version - - ptype := purlType(t) - name := pkg.Name - ver := utils.FormatVersion(pkg) - namespace := "" - subpath := "" +// Match returns true if the given PURL "target" satisfies the constraint PURL "p". +// - If the constraint does not have a version, it will match any version in the target. +// - If the constraint has qualifiers, the target must have the same set of qualifiers to match. +func (p *PackageURL) Match(target *packageurl.PackageURL) bool { + if target == nil { + return false + } + switch { + case p.Type != target.Type: + return false + case p.Namespace != target.Namespace: + return false + case p.Name != target.Name: + return false + case p.Version != "" && p.Version != target.Version: + return false + case p.Subpath != "" && p.Subpath != target.Subpath: + return false + } - switch ptype { - case packageurl.TypeRPM: - ns, qs := parseRPM(metadata.OS, pkg.Modularitylabel) - namespace = string(ns) - qualifiers = append(qualifiers, qs...) - case packageurl.TypeDebian: - qualifiers = append(qualifiers, parseDeb(metadata.OS)...) - if metadata.OS != nil { - namespace = string(metadata.OS.Family) + // All qualifiers in the constraint must be in the target to match + q := target.Qualifiers.Map() + for k, v1 := range p.Qualifiers.Map() { + if v2, ok := q[k]; !ok || v1 != v2 { + return false } - case packageurl.TypeApk: - var qs packageurl.Qualifiers - name, namespace, qs = parseApk(name, metadata.OS) - qualifiers = append(qualifiers, qs...) - case packageurl.TypeMaven, string(ftypes.Gradle): // TODO: replace with packageurl.TypeGradle once they add it. - namespace, name = parseMaven(name) - case packageurl.TypePyPi: - name = parsePyPI(name) - case packageurl.TypeComposer: - namespace, name = parseComposer(name) - case packageurl.TypeGolang: - namespace, name = parseGolang(name) - if name == "" { - return nil, nil - } - case packageurl.TypeNPM: - namespace, name = parseNpm(name) - case packageurl.TypeSwift: - namespace, name = parseSwift(name) - case packageurl.TypeCocoapods: - name, subpath = parseCocoapods(name) - case packageurl.TypeOCI: - purl, err := parseOCI(metadata) - if err != nil { - return nil, err - } - if purl.Type == "" { - return nil, nil - } - return &ftypes.PackageURL{PackageURL: purl}, nil } - - return &ftypes.PackageURL{ - PackageURL: *packageurl.NewPackageURL(ptype, namespace, name, ver, qualifiers, subpath), - FilePath: pkg.FilePath, - }, nil + return true } // ref. https://github.com/package-url/purl-spec/blob/a748c36ad415c8aeffe2b8a4a5d8a50d16d6d85f/PURL-TYPES.rst#oci diff --git a/pkg/purl/purl_test.go b/pkg/purl/purl_test.go index 16c32cdf056c..876911ca521a 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 *ftypes.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: &ftypes.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: &ftypes.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: &ftypes.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: &ftypes.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: &ftypes.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: &ftypes.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: &ftypes.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: &ftypes.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: &ftypes.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: &ftypes.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeGolang, Namespace: "github.com/go-sql-driver", @@ -202,7 +202,7 @@ func TestNewPackageURL(t *testing.T) { }, }, }, - want: &ftypes.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeHex, Name: "bunt", @@ -217,7 +217,7 @@ func TestNewPackageURL(t *testing.T) { Name: "http", Version: "0.13.2", }, - want: &ftypes.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypePub, Name: "http", @@ -233,7 +233,7 @@ func TestNewPackageURL(t *testing.T) { Name: "github.com/apple/swift-atomics", Version: "1.1.0", }, - want: &ftypes.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeSwift, Namespace: "github.com/apple", @@ -250,7 +250,7 @@ func TestNewPackageURL(t *testing.T) { Name: "GoogleUtilities/NSData+zlib", Version: "7.5.2", }, - want: &ftypes.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeCocoapods, Name: "GoogleUtilities", @@ -267,7 +267,7 @@ func TestNewPackageURL(t *testing.T) { Name: "abomination", Version: "0.7.3", }, - want: &ftypes.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeCargo, Name: "abomination", @@ -283,7 +283,7 @@ func TestNewPackageURL(t *testing.T) { Name: "Newtonsoft.Json", Version: "9.0.1", }, - want: &ftypes.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeNuget, Name: "Newtonsoft.Json", @@ -313,7 +313,7 @@ func TestNewPackageURL(t *testing.T) { Name: "8", }, }, - want: &ftypes.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeRPM, Namespace: "redhat", @@ -351,7 +351,7 @@ func TestNewPackageURL(t *testing.T) { Architecture: "amd64", }, }, - want: &ftypes.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeOCI, Namespace: "", @@ -399,7 +399,7 @@ func TestNewPackageURL(t *testing.T) { Architecture: "amd64", }, }, - want: &ftypes.PackageURL{ + want: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeOCI, Namespace: "", @@ -451,13 +451,13 @@ func TestFromString(t *testing.T) { testCases := []struct { name string purl string - want ftypes.PackageURL + want purl.PackageURL wantErr string }{ { name: "happy path for maven", purl: "pkg:maven/org.springframework/spring-core@5.0.4.RELEASE", - want: ftypes.PackageURL{ + want: purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeMaven, Namespace: "org.springframework", @@ -470,7 +470,7 @@ func TestFromString(t *testing.T) { { name: "happy path for npm", purl: "pkg:npm/bootstrap@5.0.2?file_path=app%2Fapp%2Fpackage.json", - want: ftypes.PackageURL{ + want: purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeNPM, Name: "bootstrap", @@ -482,7 +482,7 @@ func TestFromString(t *testing.T) { { name: "happy path for coocapods", purl: "pkg:cocoapods/GoogleUtilities@7.5.2#NSData+zlib", - want: ftypes.PackageURL{ + want: purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeCocoapods, Name: "GoogleUtilities", @@ -494,7 +494,7 @@ func TestFromString(t *testing.T) { { name: "happy path for hex", purl: "pkg:hex/plug@1.14.0", - want: ftypes.PackageURL{ + want: purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeHex, Name: "plug", @@ -505,7 +505,7 @@ func TestFromString(t *testing.T) { { name: "happy path for dart", purl: "pkg:pub/http@0.13.2", - want: ftypes.PackageURL{ + want: purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypePub, Name: "http", @@ -516,7 +516,7 @@ func TestFromString(t *testing.T) { { name: "happy path for apk", purl: "pkg:apk/alpine/alpine-baselayout@3.2.0-r16?distro=3.14.2&epoch=1", - want: ftypes.PackageURL{ + want: purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: string(analyzer.TypeApk), Namespace: "alpine", @@ -538,7 +538,7 @@ func TestFromString(t *testing.T) { { name: "happy path for rpm", purl: "pkg:rpm/redhat/containers-common@0.1.14", - want: ftypes.PackageURL{ + want: purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeRPM, Namespace: "redhat", @@ -550,7 +550,7 @@ func TestFromString(t *testing.T) { { name: "happy path for conda", purl: "pkg:conda/absl-py@0.4.1", - want: ftypes.PackageURL{ + want: purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeConda, Name: "absl-py", @@ -561,7 +561,7 @@ func TestFromString(t *testing.T) { { name: "bad rpm", purl: "pkg:rpm/redhat/a--@1.0.0", - want: ftypes.PackageURL{ + want: purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeRPM, Namespace: "redhat", @@ -585,15 +585,15 @@ func TestFromString(t *testing.T) { } } -func TestToPackage(t *testing.T) { +func TestPackageURL_Package(t *testing.T) { tests := []struct { name string - pkgURL *ftypes.PackageURL + pkgURL *purl.PackageURL wantPkg *ftypes.Package }{ { name: "rpm + Qualifiers", - pkgURL: &ftypes.PackageURL{ + pkgURL: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeRPM, Namespace: "redhat", @@ -626,26 +626,60 @@ func TestToPackage(t *testing.T) { Arch: "x86_64", Epoch: 1, Modularitylabel: "nodejs:10:8020020200707141642:6a468ee4", + Identifier: ftypes.PkgIdentifier{ + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Namespace: "redhat", + Name: "nodejs-full-i18n", + Version: "10.21.0-3.module_el8.2.0+391+8da3adc6", + Qualifiers: packageurl.Qualifiers{ + { + Key: "arch", + Value: "x86_64", + }, + { + Key: "epoch", + Value: "1", + }, + { + Key: "modularitylabel", + Value: "nodejs:10:8020020200707141642:6a468ee4", + }, + { + Key: "distro", + Value: "redhat-8", + }, + }, + }, + }, }, }, { name: "composer with namespace", - pkgURL: &ftypes.PackageURL{ + pkgURL: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeComposer, Namespace: "symfony", Name: "contracts", - Version: "v1.0.2", + Version: "1.0.2", }, }, wantPkg: &ftypes.Package{ Name: "symfony/contracts", - Version: "v1.0.2", + Version: "1.0.2", + Identifier: ftypes.PkgIdentifier{ + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "symfony", + Name: "contracts", + Version: "1.0.2", + }, + }, }, }, { name: "maven with namespace", - pkgURL: &ftypes.PackageURL{ + pkgURL: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeMaven, Namespace: "org.springframework", @@ -656,11 +690,19 @@ func TestToPackage(t *testing.T) { wantPkg: &ftypes.Package{ Name: "org.springframework:spring-core", Version: "5.0.4.RELEASE", + Identifier: ftypes.PkgIdentifier{ + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.springframework", + Name: "spring-core", + Version: "5.0.4.RELEASE", + }, + }, }, }, { name: "cocoapods with subpath", - pkgURL: &ftypes.PackageURL{ + pkgURL: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeCocoapods, Version: "4.2.0", @@ -671,11 +713,19 @@ func TestToPackage(t *testing.T) { wantPkg: &ftypes.Package{ Name: "AppCenter/Analytics", Version: "4.2.0", + Identifier: ftypes.PkgIdentifier{ + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeCocoapods, + Version: "4.2.0", + Name: "AppCenter", + Subpath: "Analytics", + }, + }, }, }, { name: "wrong epoch", - pkgURL: &ftypes.PackageURL{ + pkgURL: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: packageurl.TypeRPM, Namespace: "redhat", @@ -693,19 +743,33 @@ func TestToPackage(t *testing.T) { Name: "acl", Version: "2.2.53", Release: "1.el8", + Identifier: ftypes.PkgIdentifier{ + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Namespace: "redhat", + Name: "acl", + Version: "2.2.53-1.el8", + Qualifiers: packageurl.Qualifiers{ + { + Key: "epoch", + Value: "wrong", + }, + }, + }, + }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := purl.ToPackage(tt.pkgURL) + got := tt.pkgURL.Package() assert.Equal(t, tt.wantPkg, got) }) } } -func TestLangType(t *testing.T) { +func TestPackageURL_LangType(t *testing.T) { tests := []struct { name string purl packageurl.PackageURL @@ -743,8 +807,77 @@ func TestLangType(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - p := &ftypes.PackageURL{PackageURL: tt.purl} - assert.Equalf(t, tt.want, purl.LangType(p), "LangType()") + p := &purl.PackageURL{PackageURL: tt.purl} + assert.Equalf(t, tt.want, p.LangType(), "LangType()") + }) + } +} + +func TestPackageURL_Match(t *testing.T) { + tests := []struct { + name string + constraint string + target string + want bool + }{ + { + name: "same purl", + constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", + target: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", + want: true, + }, + { + name: "different type", + constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", + target: "pkg:maven/github.com/aquasecurity/trivy@0.49.0", + want: false, + }, + { + name: "different namespace", + constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", + target: "pkg:golang/github.com/aquasecurity2/trivy@0.49.0", + want: false, + }, + { + name: "different name", + constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", + target: "pkg:golang/github.com/aquasecurity/tracee@0.49.0", + want: false, + }, + { + name: "different version", + constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", + target: "pkg:golang/github.com/aquasecurity/trivy@0.49.1", + want: false, + }, + { + name: "version wildcard", + constraint: "pkg:golang/github.com/aquasecurity/trivy", + target: "pkg:golang/github.com/aquasecurity/trivy@0.50.0", + want: true, + }, + { + name: "different qualifier", + constraint: "pkg:bitnami/wordpress@6.2.0?arch=arm64&distro=debian-12", + target: "pkg:bitnami/wordpress@6.2.0?arch=arm64&distro=debian-13", + want: false, + }, + { + name: "target more qualifiers", + constraint: "pkg:bitnami/wordpress@6.2.0?arch=arm64", + target: "pkg:bitnami/wordpress@6.2.0?arch=arm64&distro=debian-13", + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c, err := purl.FromString(tt.constraint) + require.NoError(t, err) + + p, err := purl.FromString(tt.target) + require.NoError(t, err) + + assert.Equalf(t, tt.want, c.Match(p.Unwrap()), "Match()") }) } } diff --git a/pkg/result/filter_test.go b/pkg/result/filter_test.go index db9770d886d1..4edd86348c9b 100644 --- a/pkg/result/filter_test.go +++ b/pkg/result/filter_test.go @@ -154,13 +154,11 @@ func TestFilter(t *testing.T) { VulnerabilityID: "CVE-2019-0001", PkgName: "foo", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "github.com/aquasecurity", - Name: "foo", - Version: "1.2.3", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Namespace: "github.com/aquasecurity", + Name: "foo", + Version: "1.2.3", }, }, InstalledVersion: "1.2.3", @@ -173,13 +171,11 @@ func TestFilter(t *testing.T) { VulnerabilityID: "CVE-2019-0001", PkgName: "bar", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "github.com/aquasecurity", - Name: "bar", - Version: "4.5.6", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Namespace: "github.com/aquasecurity", + Name: "bar", + Version: "4.5.6", }, }, InstalledVersion: "4.5.6", @@ -209,13 +205,11 @@ func TestFilter(t *testing.T) { VulnerabilityID: "CVE-2019-0001", PkgName: "bar", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "github.com/aquasecurity", - Name: "bar", - Version: "4.5.6", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Namespace: "github.com/aquasecurity", + Name: "bar", + Version: "4.5.6", }, }, InstalledVersion: "4.5.6", diff --git a/pkg/rpc/convert.go b/pkg/rpc/convert.go index a8a04d0dcbd6..7e2a09f2c7da 100644 --- a/pkg/rpc/convert.go +++ b/pkg/rpc/convert.go @@ -3,6 +3,7 @@ package rpc import ( "time" + "github.com/package-url/packageurl-go" "github.com/samber/lo" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" @@ -11,7 +12,6 @@ import ( "github.com/aquasecurity/trivy/pkg/digest" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" - "github.com/aquasecurity/trivy/pkg/purl" "github.com/aquasecurity/trivy/pkg/types" "github.com/aquasecurity/trivy/rpc/cache" "github.com/aquasecurity/trivy/rpc/common" @@ -82,7 +82,7 @@ func ConvertToRPCPkgIdentifier(pkg ftypes.PkgIdentifier) *common.PkgIdentifier { var p string if pkg.PURL != nil { - p = pkg.PURL.BOMRef() // Use BOMRef() instead of String() so that we won't lose file_path + p = pkg.PURL.String() } return &common.PkgIdentifier{ Purl: p, @@ -227,11 +227,11 @@ func ConvertFromRPCPkgIdentifier(pkg *common.PkgIdentifier) ftypes.PkgIdentifier } if pkg.Purl != "" { - pu, err := purl.FromString(pkg.Purl) + pu, err := packageurl.FromString(pkg.Purl) if err != nil { log.Logger.Error("Failed to parse PURL (%s): %s", pkg.Purl, err) } - pkgID.PURL = pu + pkgID.PURL = &pu } return pkgID diff --git a/pkg/sbom/cyclonedx/core/cyclonedx.go b/pkg/sbom/cyclonedx/core/cyclonedx.go index 2c5945b35416..7477a559262c 100644 --- a/pkg/sbom/cyclonedx/core/cyclonedx.go +++ b/pkg/sbom/cyclonedx/core/cyclonedx.go @@ -15,8 +15,8 @@ import ( "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" "github.com/aquasecurity/trivy/pkg/clock" "github.com/aquasecurity/trivy/pkg/digest" - ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/purl" "github.com/aquasecurity/trivy/pkg/types" "github.com/aquasecurity/trivy/pkg/uuid" ) @@ -39,7 +39,7 @@ type Component struct { Name string Group string Version string - PackageURL *ftypes.PackageURL + PackageURL *purl.PackageURL Licenses []string Hashes []digest.Digest Supplier string @@ -232,7 +232,7 @@ func (c *CycloneDX) Vulnerabilities(uniq map[string]*cdx.Vulnerability) *[]cdx.V return &vulns } -func (c *CycloneDX) PackageURL(p *ftypes.PackageURL) string { +func (c *CycloneDX) PackageURL(p *purl.PackageURL) string { if p == nil { return "" } diff --git a/pkg/sbom/cyclonedx/core/cyclonedx_test.go b/pkg/sbom/cyclonedx/core/cyclonedx_test.go index d3b37308140f..60ad12864520 100644 --- a/pkg/sbom/cyclonedx/core/cyclonedx_test.go +++ b/pkg/sbom/cyclonedx/core/cyclonedx_test.go @@ -2,6 +2,7 @@ package core_test import ( "context" + "github.com/aquasecurity/trivy/pkg/purl" "testing" "time" @@ -11,7 +12,6 @@ import ( "github.com/aquasecurity/trivy/pkg/clock" "github.com/aquasecurity/trivy/pkg/digest" - ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/sbom/cyclonedx/core" "github.com/aquasecurity/trivy/pkg/uuid" ) @@ -43,7 +43,7 @@ func TestMarshaler_CoreComponent(t *testing.T) { Type: cdx.ComponentTypeContainer, Name: "k8s.gcr.io/kube-apiserver", Version: "sha256:18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f", - PackageURL: &ftypes.PackageURL{ + PackageURL: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: "oci", Name: "kube-apiserver", @@ -138,7 +138,7 @@ func TestMarshaler_CoreComponent(t *testing.T) { Value: "golang", }, }, - PackageURL: &ftypes.PackageURL{ + PackageURL: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: "golang", Name: "kubelet", @@ -157,7 +157,7 @@ func TestMarshaler_CoreComponent(t *testing.T) { Value: "golang", }, }, - PackageURL: &ftypes.PackageURL{ + PackageURL: &purl.PackageURL{ PackageURL: packageurl.PackageURL{ Type: "golang", Name: "containerd", diff --git a/pkg/sbom/cyclonedx/marshal.go b/pkg/sbom/cyclonedx/marshal.go index 16cb37c2d2fc..59564dddd6f8 100644 --- a/pkg/sbom/cyclonedx/marshal.go +++ b/pkg/sbom/cyclonedx/marshal.go @@ -378,7 +378,7 @@ func pkgComponent(pkg Package) (*core.Component, error) { Name: name, Group: group, Version: version, - PackageURL: pkg.Identifier.PURL, + PackageURL: purl.WithPath(pkg.Identifier.PURL, pkg.FilePath), 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 8169baf040bb..5a3022c91b0c 100644 --- a/pkg/sbom/cyclonedx/marshal_test.go +++ b/pkg/sbom/cyclonedx/marshal_test.go @@ -63,21 +63,19 @@ func TestMarshaler_Marshal(t *testing.T) { Epoch: 0, Arch: "aarch64", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeRPM, - Namespace: "centos", - Name: "binutils", - Version: "2.30-93.el8", - Qualifiers: packageurl.Qualifiers{ - { - Key: "arch", - Value: "aarch64", - }, - { - Key: "distro", - Value: "centos-8.3.2011", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Namespace: "centos", + Name: "binutils", + Version: "2.30-93.el8", + Qualifiers: packageurl.Qualifiers{ + { + Key: "arch", + Value: "aarch64", + }, + { + Key: "distro", + Value: "centos-8.3.2011", }, }, }, @@ -145,12 +143,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "actionpack", Version: "7.0.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actionpack", - Version: "7.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actionpack", + Version: "7.0.0", }, }, Indirect: false, @@ -160,12 +156,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "actioncontroller", Version: "7.0.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actioncontroller", - Version: "7.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actioncontroller", + Version: "7.0.0", }, }, Indirect: false, @@ -185,12 +179,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "actionpack", Version: "7.0.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actionpack", - Version: "7.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actionpack", + Version: "7.0.0", }, }, }, @@ -206,12 +198,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "Newtonsoft.Json", Version: "9.0.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeNuget, - Name: "Newtonsoft.Json", - Version: "9.0.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeNuget, + Name: "Newtonsoft.Json", + Version: "9.0.1", }, }, }, @@ -226,13 +216,11 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "golang.org/x/crypto", Version: "v0.0.0-20210421170649-83a5a9bb288b", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "golang.org/x", - Name: "crypto", - Version: "v0.0.0-20210421170649-83a5a9bb288b", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Namespace: "golang.org/x", + Name: "crypto", + Version: "v0.0.0-20210421170649-83a5a9bb288b", }, }, }, @@ -667,25 +655,23 @@ func TestMarshaler_Marshal(t *testing.T) { Epoch: 1, Arch: "aarch64", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeRPM, - Namespace: "centos", - Name: "acl", - Version: "2.2.53-1.el8", - Qualifiers: packageurl.Qualifiers{ - { - Key: "arch", - Value: "aarch64", - }, - { - Key: "distro", - Value: "centos-8.3.2011", - }, - { - Key: "epoch", - Value: "1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Namespace: "centos", + Name: "acl", + Version: "2.2.53-1.el8", + Qualifiers: packageurl.Qualifiers{ + { + Key: "arch", + Value: "aarch64", + }, + { + Key: "distro", + Value: "centos-8.3.2011", + }, + { + Key: "epoch", + Value: "1", }, }, }, @@ -709,21 +695,19 @@ func TestMarshaler_Marshal(t *testing.T) { Epoch: 0, Arch: "aarch64", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeRPM, - Namespace: "centos", - Name: "glibc", - Version: "2.28-151.el8", - Qualifiers: packageurl.Qualifiers{ - { - Key: "arch", - Value: "aarch64", - }, - { - Key: "distro", - Value: "centos-8.3.2011", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Namespace: "centos", + Name: "glibc", + Version: "2.28-151.el8", + Qualifiers: packageurl.Qualifiers{ + { + Key: "arch", + Value: "aarch64", + }, + { + Key: "distro", + Value: "centos-8.3.2011", }, }, }, @@ -748,13 +732,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "actionpack", Version: "7.0.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actionpack", - Version: "7.0.0", - }, - FilePath: "tools/project-john/specifications/actionpack.gemspec", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actionpack", + Version: "7.0.0", }, }, Layer: ftypes.Layer{ @@ -767,13 +748,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "actionpack", Version: "7.0.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actionpack", - Version: "7.0.1", - }, - FilePath: "tools/project-doe/specifications/actionpack.gemspec", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actionpack", + Version: "7.0.1", }, }, Layer: ftypes.Layer{ @@ -789,12 +767,10 @@ func TestMarshaler_Marshal(t *testing.T) { PkgName: "actionpack", PkgPath: "tools/project-john/specifications/actionpack.gemspec", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actionpack", - Version: "7.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actionpack", + Version: "7.0.0", }, }, InstalledVersion: "7.0.0", @@ -841,12 +817,10 @@ func TestMarshaler_Marshal(t *testing.T) { PkgName: "actionpack", PkgPath: "tools/project-doe/specifications/actionpack.gemspec", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actionpack", - Version: "7.0.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actionpack", + Version: "7.0.1", }, }, InstalledVersion: "7.0.1", @@ -1221,12 +1195,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "actioncable", Version: "6.1.4.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actioncable", - Version: "6.1.4.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actioncable", + Version: "6.1.4.1", }, }, }, @@ -1241,14 +1213,11 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "org.springframework:spring-web", Version: "5.3.22", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.springframework", - Name: "spring-web", - Version: "5.3.22", - }, - FilePath: "spring-web-5.3.22.jar", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.springframework", + Name: "spring-web", + Version: "5.3.22", }, }, FilePath: "spring-web-5.3.22.jar", @@ -1375,14 +1344,11 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "org.apache.nifi:nifi-dbcp-base", Version: "1.20.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.apache.nifi", - Name: "nifi-dbcp-base", - Version: "1.20.0", - }, - FilePath: "nifi-dbcp-base-1.20.0.jar", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.apache.nifi", + Name: "nifi-dbcp-base", + Version: "1.20.0", }, }, FilePath: "nifi-dbcp-base-1.20.0.jar", @@ -1391,14 +1357,11 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "org.apache.nifi:nifi-hikari-dbcp-service", Version: "1.20.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.apache.nifi", - Name: "nifi-hikari-dbcp-service", - Version: "1.20.0", - }, - FilePath: "nifi-hikari-dbcp-service-1.20.0.jar", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.apache.nifi", + Name: "nifi-hikari-dbcp-service", + Version: "1.20.0", }, }, FilePath: "nifi-hikari-dbcp-service-1.20.0.jar", @@ -1410,13 +1373,11 @@ func TestMarshaler_Marshal(t *testing.T) { PkgName: "org.apache.nifi:nifi-dbcp-base", PkgPath: "nifi-dbcp-base-1.20.0.jar", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.apache.nifi", - Name: "nifi-dbcp-base", - Version: "1.20.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.apache.nifi", + Name: "nifi-dbcp-base", + Version: "1.20.0", }, }, InstalledVersion: "1.20.0", @@ -1462,13 +1423,11 @@ func TestMarshaler_Marshal(t *testing.T) { PkgName: "org.apache.nifi:nifi-hikari-dbcp-service", PkgPath: "nifi-hikari-dbcp-service-1.20.0.jar", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.apache.nifi", - Name: "nifi-hikari-dbcp-service", - Version: "1.20.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.apache.nifi", + Name: "nifi-hikari-dbcp-service", + Version: "1.20.0", }, }, InstalledVersion: "1.20.0", @@ -1680,13 +1639,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "ruby-typeprof", Version: "0.20.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeNPM, - Name: "ruby-typeprof", - Version: "0.20.1", - }, - FilePath: "usr/local/lib/ruby/gems/3.1.0/gems/typeprof-0.21.1/vscode/package.json", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeNPM, + Name: "ruby-typeprof", + Version: "0.20.1", }, }, Licenses: []string{"MIT"}, diff --git a/pkg/sbom/cyclonedx/unmarshal.go b/pkg/sbom/cyclonedx/unmarshal.go index 77babfda26aa..96fa09364640 100644 --- a/pkg/sbom/cyclonedx/unmarshal.go +++ b/pkg/sbom/cyclonedx/unmarshal.go @@ -202,7 +202,7 @@ func parsePkgs(components []cdx.Component, seen map[string]struct{}) ([]ftypes.P } // Skip unsupported package types - if purl.Class(pkgURL) == types.ClassUnknown { + if pkgURL.Class() == types.ClassUnknown { continue } pkgs = append(pkgs, *pkg) @@ -290,11 +290,11 @@ func aggregatePkgs(libs []cdx.Component) ([]ftypes.PackageInfo, []ftypes.Applica return nil, nil, xerrors.Errorf("failed to parse the component: %w", err) } - switch purl.Class(pkgURL) { + switch pkgURL.Class() { case types.ClassOSPkg: osPkgMap[pkgURL.Type] = append(osPkgMap[pkgURL.Type], *pkg) case types.ClassLangPkg: - langType := purl.LangType(pkgURL) + langType := pkgURL.LangType() langPkgMap[langType] = append(langPkgMap[langType], *pkg) } } @@ -337,7 +337,7 @@ func toApplication(component cdx.Component) *ftypes.Application { } } -func toPackage(component cdx.Component) (*ftypes.PackageURL, *ftypes.Package, error) { +func toPackage(component cdx.Component) (*purl.PackageURL, *ftypes.Package, error) { if component.PackageURL == "" { log.Logger.Warnf("Skip the component (BOM-Ref: %s) as the PURL is empty", component.BOMRef) return nil, nil, ErrPURLEmpty @@ -347,7 +347,7 @@ func toPackage(component cdx.Component) (*ftypes.PackageURL, *ftypes.Package, er return nil, nil, xerrors.Errorf("failed to parse purl: %w", err) } - pkg := purl.ToPackage(p) + pkg := p.Package() // Trivy's marshall loses case-sensitivity in PURL used in SBOM for packages (Go, Npm, PyPI), // so we have to use an original package name pkg.Name = packageName(p.Type, pkg.Name, component) @@ -382,12 +382,9 @@ func toPackage(component cdx.Component) (*ftypes.PackageURL, *ftypes.Package, er if pkg.FilePath != "" { p.FilePath = pkg.FilePath } - pkg.Identifier = ftypes.PkgIdentifier{ - PURL: p, - BOMRef: component.BOMRef, - } + pkg.Identifier.BOMRef = component.BOMRef - if purl.Class(p) == types.ClassOSPkg { + if p.Class() == types.ClassOSPkg { fillSrcPkg(pkg) } diff --git a/pkg/sbom/cyclonedx/unmarshal_test.go b/pkg/sbom/cyclonedx/unmarshal_test.go index a61bbf8d0d30..f62677a9ac70 100644 --- a/pkg/sbom/cyclonedx/unmarshal_test.go +++ b/pkg/sbom/cyclonedx/unmarshal_test.go @@ -40,17 +40,15 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { SrcVersion: "1.2.3-r0", Licenses: []string{"MIT"}, Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "musl", - Version: "1.2.3-r0", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.16.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeApk, + Namespace: "alpine", + Name: "musl", + Version: "1.2.3-r0", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "3.16.0", }, }, }, @@ -72,13 +70,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/log", Version: "1.13.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "log", - Version: "1.13.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "log", + Version: "1.13.1", }, BOMRef: "pkg:composer/pear/log@1.13.1", }, @@ -91,13 +87,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/pear_exception", Version: "v1.0.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "pear_exception", - Version: "v1.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "pear_exception", + Version: "v1.0.0", }, BOMRef: "pkg:composer/pear/pear_exception@v1.0.0", }, @@ -115,13 +109,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "github.com/package-url/packageurl-go", Version: "v0.1.1-0.20220203205134-d70459300c8a", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "github.com/package-url", - Name: "packageurl-go", - Version: "v0.1.1-0.20220203205134-d70459300c8a", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Namespace: "github.com/package-url", + Name: "packageurl-go", + Version: "v0.1.1-0.20220203205134-d70459300c8a", }, BOMRef: "pkg:golang/github.com/package-url/packageurl-go@v0.1.1-0.20220203205134-d70459300c8a", }, @@ -138,13 +130,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { { Name: "com.example:example", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "com.example", - Name: "example", - Version: "0.0.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "com.example", + Name: "example", + Version: "0.0.1", }, BOMRef: "pkg:maven/com.example/example@0.0.1", }, @@ -161,14 +151,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { { Name: "org.codehaus.mojo:child-project", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.codehaus.mojo", - Name: "child-project", - Version: "1.0", - }, - FilePath: "app/maven/target/child-project-1.0.jar", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.codehaus.mojo", + Name: "child-project", + Version: "1.0", }, BOMRef: "pkg:maven/org.codehaus.mojo/child-project@1.0?file_path=app%2Fmaven%2Ftarget%2Fchild-project-1.0.jar", }, @@ -188,13 +175,10 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "bootstrap", Version: "5.0.2", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeNPM, - Name: "bootstrap", - Version: "5.0.2", - }, - FilePath: "app/app/package.json", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeNPM, + Name: "bootstrap", + Version: "5.0.2", }, BOMRef: "pkg:npm/bootstrap@5.0.2?file_path=app%2Fapp%2Fpackage.json", }, @@ -230,12 +214,10 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "docker", Version: "24.0.4", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Name: "docker", - Version: "24.0.4", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Name: "docker", + Version: "24.0.4", }, BOMRef: "pkg:golang/docker@24.0.4", }, @@ -253,12 +235,10 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "k8s.io/apiserver", Version: "1.27.4", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: purl.TypeK8s, - Name: "k8s.io/apiserver", - Version: "1.27.4", - }, + PURL: &packageurl.PackageURL{ + Type: purl.TypeK8s, + Name: "k8s.io/apiserver", + Version: "1.27.4", }, BOMRef: "pkg:k8s/k8s.io%2Fapiserver@1.27.4", }, @@ -267,12 +247,10 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "k8s.io/controller-manager", Version: "1.27.4", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: purl.TypeK8s, - Name: "k8s.io/controller-manager", - Version: "1.27.4", - }, + PURL: &packageurl.PackageURL{ + Type: purl.TypeK8s, + Name: "k8s.io/controller-manager", + Version: "1.27.4", }, BOMRef: "pkg:k8s/k8s.io%2Fcontroller-manager@1.27.4", }, @@ -281,12 +259,10 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "k8s.io/kube-proxy", Version: "1.27.4", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: purl.TypeK8s, - Name: "k8s.io/kube-proxy", - Version: "1.27.4", - }, + PURL: &packageurl.PackageURL{ + Type: purl.TypeK8s, + Name: "k8s.io/kube-proxy", + Version: "1.27.4", }, BOMRef: "pkg:k8s/k8s.io%2Fkube-proxy@1.27.4", }, @@ -295,12 +271,10 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "k8s.io/kube-scheduler", Version: "1.27.4", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: purl.TypeK8s, - Name: "k8s.io/kube-scheduler", - Version: "1.27.4", - }, + PURL: &packageurl.PackageURL{ + Type: purl.TypeK8s, + Name: "k8s.io/kube-scheduler", + Version: "1.27.4", }, BOMRef: "pkg:k8s/k8s.io%2Fkube-scheduler@1.27.4", }, @@ -309,12 +283,10 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "k8s.io/kubelet", Version: "1.27.4", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: purl.TypeK8s, - Name: "k8s.io/kubelet", - Version: "1.27.4", - }, + PURL: &packageurl.PackageURL{ + Type: purl.TypeK8s, + Name: "k8s.io/kubelet", + Version: "1.27.4", }, BOMRef: "pkg:k8s/k8s.io%2Fkubelet@1.27.4", }, @@ -323,12 +295,10 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "k8s.io/kubernetes", Version: "1.27.4", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: purl.TypeK8s, - Name: "k8s.io/kubernetes", - Version: "1.27.4", - }, + PURL: &packageurl.PackageURL{ + Type: purl.TypeK8s, + Name: "k8s.io/kubernetes", + Version: "1.27.4", }, BOMRef: "pkg:k8s/k8s.io%2Fkubernetes@1.27.4", }, @@ -362,17 +332,15 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { "GFDL-1.3", }, Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeDebian, - Namespace: "ubuntu", - Name: "libc6", - Version: "2.35-0ubuntu3.1", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "ubuntu-22.04", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeDebian, + Namespace: "ubuntu", + Name: "libc6", + Version: "2.35-0ubuntu3.1", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "ubuntu-22.04", }, }, }, @@ -393,21 +361,19 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { SrcRelease: "1", SrcEpoch: 1, Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeDebian, - Namespace: "ubuntu", - Name: "libcrypt1", - Version: "4.4.27-1", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "ubuntu-22.04", - }, - { - Key: "epoch", - Value: "1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeDebian, + Namespace: "ubuntu", + Name: "libcrypt1", + Version: "4.4.27-1", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "ubuntu-22.04", + }, + { + Key: "epoch", + Value: "1", }, }, }, @@ -441,17 +407,15 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { SrcVersion: "1.2.3-r0", Licenses: []string{"MIT"}, Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "musl", - Version: "1.2.3-r0", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.16.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeApk, + Namespace: "alpine", + Name: "musl", + Version: "1.2.3-r0", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "3.16.0", }, }, }, @@ -470,13 +434,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/log", Version: "1.13.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "log", - Version: "1.13.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "log", + Version: "1.13.1", }, BOMRef: "pkg:composer/pear/log@1.13.1", }, @@ -487,13 +449,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/pear_exception", Version: "v1.0.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "pear_exception", - Version: "v1.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "pear_exception", + Version: "v1.0.0", }, BOMRef: "pkg:composer/pear/pear_exception@v1.0.0", }, @@ -516,13 +476,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/log", Version: "1.13.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "log", - Version: "1.13.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "log", + Version: "1.13.1", }, BOMRef: "pkg:composer/pear/log@1.13.1", }, @@ -545,13 +503,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/log", Version: "1.13.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "log", - Version: "1.13.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "log", + Version: "1.13.1", }, BOMRef: "pkg:composer/pear/log@1.13.1", }, @@ -561,13 +517,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/pear_exception", Version: "v1.0.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "pear_exception", - Version: "v1.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "pear_exception", + Version: "v1.0.0", }, BOMRef: "pkg:composer/pear/pear_exception@v1.0.0", }, @@ -590,13 +544,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/core", Version: "1.13.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "core", - Version: "1.13.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "core", + Version: "1.13.1", }, BOMRef: "pkg:composer/pear/core@1.13.1", }, @@ -605,13 +557,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/log", Version: "1.13.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "log", - Version: "1.13.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "log", + Version: "1.13.1", }, BOMRef: "pkg:composer/pear/log@1.13.1", }, @@ -621,13 +571,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/pear_exception", Version: "v1.0.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "pear_exception", - Version: "v1.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "pear_exception", + Version: "v1.0.0", }, BOMRef: "pkg:composer/pear/pear_exception@v1.0.0", }, @@ -649,14 +597,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "org.springframework:spring-web", Version: "5.3.22", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.springframework", - Name: "spring-web", - Version: "5.3.22", - }, - FilePath: "spring-web-5.3.22.jar", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.springframework", + Name: "spring-web", + Version: "5.3.22", }, BOMRef: "pkg:maven/org.springframework/spring-web@5.3.22?file_path=spring-web-5.3.22.jar", }, diff --git a/pkg/sbom/spdx/marshal_test.go b/pkg/sbom/spdx/marshal_test.go index abd356d6ec7e..616108886264 100644 --- a/pkg/sbom/spdx/marshal_test.go +++ b/pkg/sbom/spdx/marshal_test.go @@ -62,21 +62,19 @@ func TestMarshaler_Marshal(t *testing.T) { Epoch: 0, Arch: "aarch64", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeRPM, - Namespace: "centos", - Name: "binutils", - Version: "2.30-93.el8", - Qualifiers: packageurl.Qualifiers{ - { - Key: "arch", - Value: "aarch64", - }, - { - Key: "distro", - Value: "centos-8.3.2011", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Namespace: "centos", + Name: "binutils", + Version: "2.30-93.el8", + Qualifiers: packageurl.Qualifiers{ + { + Key: "arch", + Value: "aarch64", + }, + { + Key: "distro", + Value: "centos-8.3.2011", }, }, }, @@ -101,12 +99,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "actionpack", Version: "7.0.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actionpack", - Version: "7.0.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actionpack", + Version: "7.0.1", }, }, }, @@ -114,12 +110,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "actioncontroller", Version: "7.0.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actioncontroller", - Version: "7.0.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actioncontroller", + Version: "7.0.1", }, }, }, @@ -134,12 +128,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "actionpack", Version: "7.0.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actionpack", - Version: "7.0.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actionpack", + Version: "7.0.1", }, }, }, @@ -351,25 +343,23 @@ func TestMarshaler_Marshal(t *testing.T) { Epoch: 1, Arch: "aarch64", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeRPM, - Namespace: "centos", - Name: "acl", - Version: "2.2.53-1.el8", - Qualifiers: packageurl.Qualifiers{ - { - Key: "arch", - Value: "aarch64", - }, - { - Key: "distro", - Value: "centos-8.3.2011", - }, - { - Key: "epoch", - Value: "1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Namespace: "centos", + Name: "acl", + Version: "2.2.53-1.el8", + Qualifiers: packageurl.Qualifiers{ + { + Key: "arch", + Value: "aarch64", + }, + { + Key: "distro", + Value: "centos-8.3.2011", + }, + { + Key: "epoch", + Value: "1", }, }, }, @@ -393,12 +383,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "actionpack", Version: "7.0.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actionpack", - Version: "7.0.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actionpack", + Version: "7.0.1", }, }, Layer: ftypes.Layer{ @@ -411,12 +399,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "actionpack", Version: "7.0.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actionpack", - Version: "7.0.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actionpack", + Version: "7.0.1", }, }, Layer: ftypes.Layer{ @@ -634,12 +620,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "actioncable", Version: "6.1.4.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGem, - Name: "actioncable", - Version: "6.1.4.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGem, + Name: "actioncable", + Version: "6.1.4.1", }, }, }, @@ -736,12 +720,10 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "ruby-typeprof", Version: "0.20.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeNPM, - Name: "ruby-typeprof", - Version: "0.20.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeNPM, + Name: "ruby-typeprof", + Version: "0.20.1", }, }, Licenses: []string{"MIT"}, @@ -976,13 +958,11 @@ func TestMarshaler_Marshal(t *testing.T) { Name: "golang.org/x/crypto", Version: "v0.0.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "golang.org/x", - Name: "crypto", - Version: "v0.0.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Namespace: "golang.org/x", + Name: "crypto", + Version: "v0.0.1", }, }, }, diff --git a/pkg/sbom/spdx/unmarshal.go b/pkg/sbom/spdx/unmarshal.go index 5ce4237ac20a..1723476a323d 100644 --- a/pkg/sbom/spdx/unmarshal.go +++ b/pkg/sbom/spdx/unmarshal.go @@ -177,12 +177,12 @@ func (s *SPDX) parsePackages(pkgs map[common.ElementID]*spdx.Package) error { } else if err != nil { return xerrors.Errorf("failed to parse package: %w", err) } - switch purl.Class(pkgURL) { + switch pkgURL.Class() { case types.ClassOSPkg: osPkgs = append(osPkgs, *pkg) case types.ClassLangPkg: // Language-specific packages - pkgType := purl.LangType(pkgURL) + pkgType := pkgURL.LangType() app, ok := apps[pkgType] if !ok { app.Type = pkgType @@ -246,12 +246,13 @@ func parseOS(pkg spdx.Package) ftypes.OS { } } -func parsePkg(spdxPkg spdx.Package, packageFilePaths map[string]string) (*ftypes.Package, *ftypes.PackageURL, error) { - pkg, pkgURL, err := parseExternalReferences(spdxPkg.PackageExternalReferences) +func parsePkg(spdxPkg spdx.Package, packageFilePaths map[string]string) (*ftypes.Package, *purl.PackageURL, error) { + pkgURL, err := parseExternalReferences(spdxPkg.PackageExternalReferences) if err != nil { return nil, nil, xerrors.Errorf("external references error: %w", err) } + pkg := pkgURL.Package() if spdxPkg.PackageLicenseDeclared != "NONE" { pkg.Licenses = strings.Split(spdxPkg.PackageLicenseDeclared, ",") } @@ -278,7 +279,7 @@ func parsePkg(spdxPkg spdx.Package, packageFilePaths map[string]string) (*ftypes return pkg, pkgURL, nil } -func parseExternalReferences(refs []*spdx.PackageExternalReference) (*ftypes.Package, *ftypes.PackageURL, error) { +func parseExternalReferences(refs []*spdx.PackageExternalReference) (*purl.PackageURL, error) { for _, ref := range refs { // Extract the package information from PURL if ref.RefType != RefTypePurl || ref.Category != CategoryPackageManager { @@ -287,15 +288,11 @@ func parseExternalReferences(refs []*spdx.PackageExternalReference) (*ftypes.Pac packageURL, err := purl.FromString(ref.Locator) if err != nil { - return nil, nil, xerrors.Errorf("failed to parse purl from string: %w", err) + return nil, xerrors.Errorf("failed to parse purl from string: %w", err) } - pkg := purl.ToPackage(packageURL) - pkg.Identifier = ftypes.PkgIdentifier{ - PURL: packageURL, - } - return pkg, packageURL, nil + return packageURL, nil } - return nil, nil, errUnknownPackageFormat + return nil, errUnknownPackageFormat } func lookupAttributionTexts(attributionTexts []string, key string) string { diff --git a/pkg/sbom/spdx/unmarshal_test.go b/pkg/sbom/spdx/unmarshal_test.go index dfa09a0c86fa..2d6ee258c378 100644 --- a/pkg/sbom/spdx/unmarshal_test.go +++ b/pkg/sbom/spdx/unmarshal_test.go @@ -40,17 +40,15 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { SrcVersion: "1.2.3-r0", Licenses: []string{"MIT"}, Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeApk, - Namespace: "alpine", - Name: "musl", - Version: "1.2.3-r0", - Qualifiers: packageurl.Qualifiers{ - { - Key: "distro", - Value: "3.16.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeApk, + Namespace: "alpine", + Name: "musl", + Version: "1.2.3-r0", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "3.16.0", }, }, }, @@ -71,13 +69,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/log", Version: "1.13.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "log", - Version: "1.13.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "log", + Version: "1.13.1", }, }, Layer: ftypes.Layer{ @@ -89,13 +85,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/pear_exception", Version: "v1.0.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "pear_exception", - Version: "v1.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "pear_exception", + Version: "v1.0.0", }, }, Layer: ftypes.Layer{ @@ -112,13 +106,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "github.com/package-url/packageurl-go", Version: "v0.1.1-0.20220203205134-d70459300c8a", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeGolang, - Namespace: "github.com/package-url", - Name: "packageurl-go", - Version: "v0.1.1-0.20220203205134-d70459300c8a", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeGolang, + Namespace: "github.com/package-url", + Name: "packageurl-go", + Version: "v0.1.1-0.20220203205134-d70459300c8a", }, }, Layer: ftypes.Layer{ @@ -133,13 +125,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { { Name: "org.codehaus.mojo:child-project", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.codehaus.mojo", - Name: "child-project", - Version: "1.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.codehaus.mojo", + Name: "child-project", + Version: "1.0", }, }, Version: "1.0", @@ -156,12 +146,10 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "bootstrap", Version: "5.0.2", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeNPM, - Name: "bootstrap", - Version: "5.0.2", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeNPM, + Name: "bootstrap", + Version: "5.0.2", }, }, Licenses: []string{"MIT"}, @@ -188,12 +176,10 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Version: "21.1.1", Licenses: []string{"ISC"}, Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeNPM, - Name: "yargs-parser", - Version: "21.1.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeNPM, + Name: "yargs-parser", + Version: "21.1.1", }, }, FilePath: "node_modules/yargs-parser/package.json", @@ -217,12 +203,10 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Version: "21.1.1", Licenses: []string{"ISC"}, Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeNPM, - Name: "yargs-parser", - Version: "21.1.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeNPM, + Name: "yargs-parser", + Version: "21.1.1", }, }, FilePath: "node_modules/yargs-parser/package.json", @@ -245,13 +229,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/log", Version: "1.13.1", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "log", - Version: "1.13.1", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "log", + Version: "1.13.1", }, }, }, @@ -260,13 +242,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "pear/pear_exception", Version: "v1.0.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeComposer, - Namespace: "pear", - Name: "pear_exception", - Version: "v1.0.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeComposer, + Namespace: "pear", + Name: "pear_exception", + Version: "v1.0.0", }, }, }, @@ -288,13 +268,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "co.elastic.apm:apm-agent", Version: "1.36.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "co.elastic.apm", - Name: "apm-agent", - Version: "1.36.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "co.elastic.apm", + Name: "apm-agent", + Version: "1.36.0", }, }, }, @@ -303,13 +281,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) { Name: "co.elastic.apm:apm-agent-cached-lookup-key", Version: "1.36.0", Identifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "co.elastic.apm", - Name: "apm-agent-cached-lookup-key", - Version: "1.36.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "co.elastic.apm", + Name: "apm-agent-cached-lookup-key", + Version: "1.36.0", }, }, }, diff --git a/pkg/vex/csaf.go b/pkg/vex/csaf.go index efed27b83f22..87684064b9d4 100644 --- a/pkg/vex/csaf.go +++ b/pkg/vex/csaf.go @@ -2,12 +2,12 @@ package vex import ( csaf "github.com/csaf-poc/csaf_distribution/v3/csaf" + "github.com/package-url/packageurl-go" "github.com/samber/lo" "go.uber.org/zap" - "golang.org/x/exp/slices" - ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/purl" "github.com/aquasecurity/trivy/pkg/types" ) @@ -36,16 +36,16 @@ func (v *CSAF) Filter(vulns []types.DetectedVulnerability) []types.DetectedVulne }) } -func (v *CSAF) affected(vuln *csaf.Vulnerability, purl *ftypes.PackageURL) bool { - if purl == nil || vuln.ProductStatus == nil { +func (v *CSAF) affected(vuln *csaf.Vulnerability, pkgURL *packageurl.PackageURL) bool { + if pkgURL == nil || vuln.ProductStatus == nil { return true } var status Status switch { - case v.matchPURL(purl, vuln.ProductStatus.KnownNotAffected): + case v.matchPURL(vuln.ProductStatus.KnownNotAffected, pkgURL): status = StatusNotAffected - case v.matchPURL(purl, vuln.ProductStatus.Fixed): + case v.matchPURL(vuln.ProductStatus.Fixed, pkgURL): status = StatusFixed } @@ -60,17 +60,24 @@ func (v *CSAF) affected(vuln *csaf.Vulnerability, purl *ftypes.PackageURL) bool } // matchPURL returns true if the given PackageURL is found in the ProductTree. -func (v *CSAF) matchPURL(purl *ftypes.PackageURL, products *csaf.Products) bool { +func (v *CSAF) matchPURL(products *csaf.Products, pkgURL *packageurl.PackageURL) bool { for _, product := range lo.FromPtr(products) { helpers := v.advisory.ProductTree.CollectProductIdentificationHelpers(lo.FromPtr(product)) - purls := lo.FilterMap(helpers, func(helper *csaf.ProductIdentificationHelper, _ int) (string, bool) { + purls := lo.FilterMap(helpers, func(helper *csaf.ProductIdentificationHelper, _ int) (*purl.PackageURL, bool) { if helper == nil || helper.PURL == nil { - return "", false + return nil, false } - return string(*helper.PURL), true + p, err := purl.FromString(string(*helper.PURL)) + if err != nil { + v.logger.Errorw("Invalid PURL", zap.String("purl", string(*helper.PURL)), zap.Error(err)) + return nil, false + } + return p, true }) - if slices.Contains(purls, purl.String()) { - return true + for _, p := range purls { + if p.Match(pkgURL) { + return true + } } } diff --git a/pkg/vex/vex_test.go b/pkg/vex/vex_test.go index 1feb37a7c41d..004a0d14d842 100644 --- a/pkg/vex/vex_test.go +++ b/pkg/vex/vex_test.go @@ -47,13 +47,11 @@ func TestVEX_Filter(t *testing.T) { PkgName: "spring-boot", InstalledVersion: "2.6.0", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.springframework.boot", - Name: "spring-boot", - Version: "2.6.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.springframework.boot", + Name: "spring-boot", + Version: "2.6.0", }, }, }, @@ -73,13 +71,11 @@ func TestVEX_Filter(t *testing.T) { PkgName: "spring-boot", InstalledVersion: "2.6.0", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.springframework.boot", - Name: "spring-boot", - Version: "2.6.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.springframework.boot", + Name: "spring-boot", + Version: "2.6.0", }, }, }, @@ -88,13 +84,11 @@ func TestVEX_Filter(t *testing.T) { PkgName: "spring-boot", InstalledVersion: "2.6.0", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.springframework.boot", - Name: "spring-boot", - Version: "2.6.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.springframework.boot", + Name: "spring-boot", + Version: "2.6.0", }, }, }, @@ -106,13 +100,11 @@ func TestVEX_Filter(t *testing.T) { PkgName: "spring-boot", InstalledVersion: "2.6.0", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.springframework.boot", - Name: "spring-boot", - Version: "2.6.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.springframework.boot", + Name: "spring-boot", + Version: "2.6.0", }, }, }, @@ -136,13 +128,11 @@ func TestVEX_Filter(t *testing.T) { PkgName: "jackson-databind", InstalledVersion: "2.8.0", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "com.fasterxml.jackson.core", - Name: "jackson-databind", - Version: "2.8.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "com.fasterxml.jackson.core", + Name: "jackson-databind", + Version: "2.8.0", }, }, }, @@ -151,13 +141,11 @@ func TestVEX_Filter(t *testing.T) { PkgName: "jackson-databind", InstalledVersion: "2.8.0", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "com.fasterxml.jackson.core", - Name: "jackson-databind", - Version: "2.8.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "com.fasterxml.jackson.core", + Name: "jackson-databind", + Version: "2.8.0", }, }, }, @@ -169,13 +157,11 @@ func TestVEX_Filter(t *testing.T) { PkgName: "jackson-databind", InstalledVersion: "2.8.0", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "com.fasterxml.jackson.core", - Name: "jackson-databind", - Version: "2.8.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "com.fasterxml.jackson.core", + Name: "jackson-databind", + Version: "2.8.0", }, }, }, @@ -199,13 +185,11 @@ func TestVEX_Filter(t *testing.T) { PkgName: "jackson-databind", InstalledVersion: "2.8.0", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "com.fasterxml.jackson.core", - Name: "jackson-databind", - Version: "2.8.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "com.fasterxml.jackson.core", + Name: "jackson-databind", + Version: "2.8.0", }, }, }, @@ -217,13 +201,11 @@ func TestVEX_Filter(t *testing.T) { PkgName: "jackson-databind", InstalledVersion: "2.8.0", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "com.fasterxml.jackson.core", - Name: "jackson-databind", - Version: "2.8.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "com.fasterxml.jackson.core", + Name: "jackson-databind", + Version: "2.8.0", }, }, }, @@ -241,13 +223,11 @@ func TestVEX_Filter(t *testing.T) { PkgName: "spring-boot", InstalledVersion: "2.6.0", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.springframework.boot", - Name: "spring-boot", - Version: "2.6.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.springframework.boot", + Name: "spring-boot", + Version: "2.6.0", }, }, }, @@ -267,13 +247,11 @@ func TestVEX_Filter(t *testing.T) { PkgName: "def", InstalledVersion: "1.0", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.example.company", - Name: "def", - Version: "1.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.example.company", + Name: "def", + Version: "1.0", }, }, }, @@ -285,13 +263,11 @@ func TestVEX_Filter(t *testing.T) { PkgName: "def", InstalledVersion: "1.0", PkgIdentifier: ftypes.PkgIdentifier{ - PURL: &ftypes.PackageURL{ - PackageURL: packageurl.PackageURL{ - Type: packageurl.TypeMaven, - Namespace: "org.example.company", - Name: "def", - Version: "1.0", - }, + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.example.company", + Name: "def", + Version: "1.0", }, }, },