From 72bd8a88f8501351a81720e8bf9c63ebb71e2538 Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Mon, 13 Feb 2023 11:15:52 +0000 Subject: [PATCH 1/9] fix: improved CPE-generation logic for alpine packages Signed-off-by: Weston Steimel --- syft/pkg/cataloger/common/cpe/apk.go | 53 +++++++++++++ syft/pkg/cataloger/common/cpe/apk_test.go | 91 +++++++++++++++++++++++ syft/pkg/cataloger/common/cpe/generate.go | 8 ++ 3 files changed, 152 insertions(+) create mode 100644 syft/pkg/cataloger/common/cpe/apk.go create mode 100644 syft/pkg/cataloger/common/cpe/apk_test.go diff --git a/syft/pkg/cataloger/common/cpe/apk.go b/syft/pkg/cataloger/common/cpe/apk.go new file mode 100644 index 00000000000..7191eeba665 --- /dev/null +++ b/syft/pkg/cataloger/common/cpe/apk.go @@ -0,0 +1,53 @@ +package cpe + +import ( + "strings" + + "github.com/anchore/syft/syft/pkg" +) + +var ( + prefixes = []string{"py-", "py2-", "py3-", "ruby-"} +) + +func candidateVendorsForAPK(p pkg.Package) fieldCandidateSet { + metadata, ok := p.Metadata.(pkg.ApkMetadata) + if !ok { + return nil + } + + vendors := newFieldCandidateSet() + + for _, p := range prefixes { + if strings.HasPrefix(metadata.Package, p) { + vendors.addValue(strings.TrimPrefix(metadata.Package, p)) + } + + if strings.HasPrefix(metadata.OriginPackage, p) { + vendors.addValue(strings.TrimPrefix(metadata.OriginPackage, p)) + } + } + + return vendors +} + +func candidateProductsForAPK(p pkg.Package) fieldCandidateSet { + metadata, ok := p.Metadata.(pkg.ApkMetadata) + if !ok { + return nil + } + + products := newFieldCandidateSet() + + for _, p := range prefixes { + if strings.HasPrefix(metadata.Package, p) { + products.addValue(strings.TrimPrefix(metadata.Package, p)) + } + + if strings.HasPrefix(metadata.OriginPackage, p) { + products.addValue(strings.TrimPrefix(metadata.OriginPackage, p)) + } + } + + return products +} diff --git a/syft/pkg/cataloger/common/cpe/apk_test.go b/syft/pkg/cataloger/common/cpe/apk_test.go new file mode 100644 index 00000000000..602cfa8f782 --- /dev/null +++ b/syft/pkg/cataloger/common/cpe/apk_test.go @@ -0,0 +1,91 @@ +package cpe + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_candidateVendorsForAPK(t *testing.T) { + tests := []struct { + name string + pkg pkg.Package + expected []string + }{ + { + name: "py3-cryptography Package", + pkg: pkg.Package{ + Metadata: pkg.ApkMetadata{ + Package: "py3-cryptography", + }, + }, + expected: []string{"cryptography"}, + }, + { + name: "py2-pypdf OriginPackage", + pkg: pkg.Package{ + Metadata: pkg.ApkMetadata{ + OriginPackage: "py2-pypdf", + }, + }, + expected: []string{"pypdf"}, + }, + { + name: "ruby-armadillo Package", + pkg: pkg.Package{ + Metadata: pkg.ApkMetadata{ + Package: "ruby-armadillo", + }, + }, + expected: []string{"armadillo"}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual := candidateVendorsForAPK(test.pkg) + assert.ElementsMatch(t, test.expected, actual, "different vendors") + }) + } +} + +func Test_candidateProductsForAPK(t *testing.T) { + tests := []struct { + name string + pkg pkg.Package + expected []string + }{ + { + name: "py3-cryptography Package", + pkg: pkg.Package{ + Metadata: pkg.ApkMetadata{ + Package: "py3-cryptography", + }, + }, + expected: []string{"cryptography"}, + }, + { + name: "py2-pypdf OriginPackage", + pkg: pkg.Package{ + Metadata: pkg.ApkMetadata{ + OriginPackage: "py2-pypdf", + }, + }, + expected: []string{"pypdf"}, + }, + { + name: "ruby-armadillo Package", + pkg: pkg.Package{ + Metadata: pkg.ApkMetadata{ + Package: "ruby-armadillo", + }, + }, + expected: []string{"armadillo"}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual := candidateProductsForAPK(test.pkg) + assert.ElementsMatch(t, test.expected, actual, "different products") + }) + } +} diff --git a/syft/pkg/cataloger/common/cpe/generate.go b/syft/pkg/cataloger/common/cpe/generate.go index ec686cccf80..49ea7a37f89 100644 --- a/syft/pkg/cataloger/common/cpe/generate.go +++ b/syft/pkg/cataloger/common/cpe/generate.go @@ -109,6 +109,8 @@ func candidateVendors(p pkg.Package) []string { vendors.union(candidateVendorsForPython(p)) case pkg.JavaMetadataType: vendors.union(candidateVendorsForJava(p)) + case pkg.ApkMetadataType: + vendors.union(candidateVendorsForAPK(p)) } // try swapping hyphens for underscores, vice versa, and removing separators altogether @@ -156,6 +158,12 @@ func candidateProducts(p pkg.Package) []string { products.addValue(prod) } } + + switch p.MetadataType { + case pkg.ApkMetadataType: + products.union(candidateProductsForAPK(p)) + } + // it is never OK to have candidates with these values ["" and "*"] (since CPEs will match any other value) products.removeByValue("") products.removeByValue("*") From 24e500648cf2d3c3e9a7c5e82cbb677c262de815 Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Mon, 13 Feb 2023 11:43:39 +0000 Subject: [PATCH 2/9] fix: improved alpine upstream name generation Signed-off-by: Weston Steimel --- syft/pkg/cataloger/apkdb/package.go | 20 +++++++++++++++++++- syft/pkg/cataloger/apkdb/package_test.go | 14 ++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/syft/pkg/cataloger/apkdb/package.go b/syft/pkg/cataloger/apkdb/package.go index f60ef82bf1b..f7e3e9b4cf3 100644 --- a/syft/pkg/cataloger/apkdb/package.go +++ b/syft/pkg/cataloger/apkdb/package.go @@ -9,6 +9,10 @@ import ( "github.com/anchore/syft/syft/source" ) +var ( + prefixes = []string{"py-", "py2-", "py3-", "ruby-"} +) + func newPackage(d pkg.ApkMetadata, release *linux.Release, locations ...source.Location) pkg.Package { p := pkg.Package{ Name: d.Package, @@ -26,6 +30,20 @@ func newPackage(d pkg.ApkMetadata, release *linux.Release, locations ...source.L return p } +func generateUpstream(m pkg.ApkMetadata) string { + if m.OriginPackage != "" && m.OriginPackage != m.Package { + return m.OriginPackage + } + + for _, p := range prefixes { + if strings.HasPrefix(m.Package, p) { + return strings.TrimPrefix(m.Package, p) + } + } + + return m.Package +} + // packageURL returns the PURL for the specific Alpine package (see https://github.com/package-url/purl-spec) func packageURL(m pkg.ApkMetadata, distro *linux.Release) string { if distro == nil || distro.ID != "alpine" { @@ -38,7 +56,7 @@ func packageURL(m pkg.ApkMetadata, distro *linux.Release) string { } if m.OriginPackage != "" { - qualifiers[pkg.PURLQualifierUpstream] = m.OriginPackage + qualifiers[pkg.PURLQualifierUpstream] = generateUpstream(m) } return packageurl.NewPackageURL( diff --git a/syft/pkg/cataloger/apkdb/package_test.go b/syft/pkg/cataloger/apkdb/package_test.go index 30a2a396f4d..6bc5195b484 100644 --- a/syft/pkg/cataloger/apkdb/package_test.go +++ b/syft/pkg/cataloger/apkdb/package_test.go @@ -96,6 +96,20 @@ func Test_PackageURL(t *testing.T) { }, expected: "pkg:apk/alpine/p@v?arch=a&upstream=origin&distro=alpine-3.4.6", }, + { + name: "upstream python package information as qualifier", + metadata: pkg.ApkMetadata{ + Package: "py3-potatoes", + Version: "v", + Architecture: "a", + OriginPackage: "py3-potatoes", + }, + distro: linux.Release{ + ID: "alpine", + VersionID: "3.4.6", + }, + expected: "pkg:apk/alpine/p@v?arch=a&upstream=potatoes&distro=alpine-3.4.6", + }, } for _, test := range tests { From f37df976d41f28fdc039f22d88f588e4d0c5fdd8 Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Mon, 13 Feb 2023 11:59:17 +0000 Subject: [PATCH 3/9] fix: improve CPE vendor for alpine Signed-off-by: Weston Steimel --- syft/pkg/cataloger/common/cpe/apk.go | 10 ++++++++-- syft/pkg/cataloger/common/cpe/apk_test.go | 6 +++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/syft/pkg/cataloger/common/cpe/apk.go b/syft/pkg/cataloger/common/cpe/apk.go index 7191eeba665..a4ba4b1e684 100644 --- a/syft/pkg/cataloger/common/cpe/apk.go +++ b/syft/pkg/cataloger/common/cpe/apk.go @@ -20,11 +20,17 @@ func candidateVendorsForAPK(p pkg.Package) fieldCandidateSet { for _, p := range prefixes { if strings.HasPrefix(metadata.Package, p) { - vendors.addValue(strings.TrimPrefix(metadata.Package, p)) + t := strings.TrimPrefix(metadata.Package, p) + vendors.addValue(t) + vendors.addValue(t + "project") + vendors.addValue(t + "_project") } if strings.HasPrefix(metadata.OriginPackage, p) { - vendors.addValue(strings.TrimPrefix(metadata.OriginPackage, p)) + t := strings.TrimPrefix(metadata.OriginPackage, p) + vendors.addValue(t) + vendors.addValue(t + "project") + vendors.addValue(t + "_project") } } diff --git a/syft/pkg/cataloger/common/cpe/apk_test.go b/syft/pkg/cataloger/common/cpe/apk_test.go index 602cfa8f782..87bf2ca5f35 100644 --- a/syft/pkg/cataloger/common/cpe/apk_test.go +++ b/syft/pkg/cataloger/common/cpe/apk_test.go @@ -19,7 +19,7 @@ func Test_candidateVendorsForAPK(t *testing.T) { Package: "py3-cryptography", }, }, - expected: []string{"cryptography"}, + expected: []string{"cryptography", "cryptographyproject", "cryptography_project"}, }, { name: "py2-pypdf OriginPackage", @@ -28,7 +28,7 @@ func Test_candidateVendorsForAPK(t *testing.T) { OriginPackage: "py2-pypdf", }, }, - expected: []string{"pypdf"}, + expected: []string{"pypdf", "pypdfproject", "pypdf_project"}, }, { name: "ruby-armadillo Package", @@ -37,7 +37,7 @@ func Test_candidateVendorsForAPK(t *testing.T) { Package: "ruby-armadillo", }, }, - expected: []string{"armadillo"}, + expected: []string{"armadillo", "armadilloproject", "armadillo_project"}, }, } for _, test := range tests { From f7703daf41aeac4b62ad09a713ed17c5328d25c1 Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Mon, 13 Feb 2023 12:22:18 +0000 Subject: [PATCH 4/9] fix: python vendor CPE gen Signed-off-by: Weston Steimel --- syft/pkg/cataloger/common/cpe/apk.go | 43 ++++++++++++++++--- .../common/cpe/candidate_by_package_type.go | 5 +++ syft/pkg/cataloger/common/cpe/python.go | 36 ++++++++++++++-- 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/syft/pkg/cataloger/common/cpe/apk.go b/syft/pkg/cataloger/common/cpe/apk.go index a4ba4b1e684..98197b80870 100644 --- a/syft/pkg/cataloger/common/cpe/apk.go +++ b/syft/pkg/cataloger/common/cpe/apk.go @@ -7,7 +7,8 @@ import ( ) var ( - prefixes = []string{"py-", "py2-", "py3-", "ruby-"} + pythonPrefixes = []string{"py-", "py2-", "py3-"} + rubyPrefixes = []string{"ruby-"} ) func candidateVendorsForAPK(p pkg.Package) fieldCandidateSet { @@ -18,19 +19,37 @@ func candidateVendorsForAPK(p pkg.Package) fieldCandidateSet { vendors := newFieldCandidateSet() - for _, p := range prefixes { + for _, p := range pythonPrefixes { + if strings.HasPrefix(metadata.Package, p) { + t := strings.TrimPrefix(metadata.Package, p) + vendors.add(fieldCandidate{ + value: t, + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + vendors.union(additionalVendorsForPython(t)) + } + + if strings.HasPrefix(metadata.OriginPackage, p) { + t := strings.TrimPrefix(metadata.OriginPackage, p) + vendors.add(fieldCandidate{ + value: t, + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + vendors.union(additionalVendorsForPython(t)) + } + } + + for _, p := range rubyPrefixes { if strings.HasPrefix(metadata.Package, p) { t := strings.TrimPrefix(metadata.Package, p) vendors.addValue(t) - vendors.addValue(t + "project") - vendors.addValue(t + "_project") } if strings.HasPrefix(metadata.OriginPackage, p) { t := strings.TrimPrefix(metadata.OriginPackage, p) vendors.addValue(t) - vendors.addValue(t + "project") - vendors.addValue(t + "_project") } } @@ -45,7 +64,17 @@ func candidateProductsForAPK(p pkg.Package) fieldCandidateSet { products := newFieldCandidateSet() - for _, p := range prefixes { + for _, p := range pythonPrefixes { + if strings.HasPrefix(metadata.Package, p) { + products.addValue(strings.TrimPrefix(metadata.Package, p)) + } + + if strings.HasPrefix(metadata.OriginPackage, p) { + products.addValue(strings.TrimPrefix(metadata.OriginPackage, p)) + } + } + + for _, p := range rubyPrefixes { if strings.HasPrefix(metadata.Package, p) { products.addValue(strings.TrimPrefix(metadata.Package, p)) } diff --git a/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go b/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go index 154f00a2124..b5c6e6bda73 100644 --- a/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go +++ b/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go @@ -127,6 +127,11 @@ var defaultCandidateAdditions = buildCandidateLookup( candidateKey{PkgName: "python-rrdtool"}, candidateAddition{AdditionalProducts: []string{"rrdtool"}}, }, + { + pkg.PythonPkg, + candidateKey{PkgName: "cryptography"}, + candidateAddition{AdditionalVendors: []string{"pypa"}}, + }, // Alpine packages { pkg.ApkPkg, diff --git a/syft/pkg/cataloger/common/cpe/python.go b/syft/pkg/cataloger/common/cpe/python.go index fca061035d5..15f17c07efc 100644 --- a/syft/pkg/cataloger/common/cpe/python.go +++ b/syft/pkg/cataloger/common/cpe/python.go @@ -1,6 +1,31 @@ package cpe -import "github.com/anchore/syft/syft/pkg" +import ( + "fmt" + "strings" + + "github.com/anchore/syft/syft/pkg" +) + +func additionalVendorsForPython(v string) fieldCandidateSet { + vendors := newFieldCandidateSet() + + if !strings.HasSuffix(v, "project") { + vendors.add(fieldCandidate{ + value: fmt.Sprintf("%sproject", v), + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + + vendors.add(fieldCandidate{ + value: fmt.Sprintf("%s_project", v), + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + } + + return vendors +} func candidateVendorsForPython(p pkg.Package) fieldCandidateSet { metadata, ok := p.Metadata.(pkg.PythonPackageMetadata) @@ -11,18 +36,23 @@ func candidateVendorsForPython(p pkg.Package) fieldCandidateSet { vendors := newFieldCandidateSet() if metadata.Author != "" { + name := normalizePersonName(metadata.Author) vendors.add(fieldCandidate{ - value: normalizePersonName(metadata.Author), + value: name, disallowSubSelections: true, disallowDelimiterVariations: true, }) + + vendors.union(additionalVendorsForPython(name)) } if metadata.AuthorEmail != "" { + name := normalizePersonName(stripEmailSuffix(metadata.AuthorEmail)) vendors.add(fieldCandidate{ - value: normalizePersonName(stripEmailSuffix(metadata.AuthorEmail)), + value: name, disallowSubSelections: true, }) + vendors.union(additionalVendorsForPython(name)) } return vendors From f7b6c279a37675eb9bae233870f63cb6357fabf4 Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Mon, 13 Feb 2023 13:21:48 +0000 Subject: [PATCH 5/9] fix: alpine cpe gen logic Signed-off-by: Weston Steimel --- syft/pkg/cataloger/apkdb/package_test.go | 16 +- syft/pkg/cataloger/common/cpe/apk.go | 171 +++++++++++++----- syft/pkg/cataloger/common/cpe/apk_test.go | 13 +- .../common/cpe/candidate_by_package_type.go | 2 +- .../pkg/cataloger/common/cpe/generate_test.go | 12 ++ syft/pkg/cataloger/common/cpe/python.go | 33 ++-- 6 files changed, 179 insertions(+), 68 deletions(-) diff --git a/syft/pkg/cataloger/apkdb/package_test.go b/syft/pkg/cataloger/apkdb/package_test.go index 6bc5195b484..ce7003bc396 100644 --- a/syft/pkg/cataloger/apkdb/package_test.go +++ b/syft/pkg/cataloger/apkdb/package_test.go @@ -108,7 +108,21 @@ func Test_PackageURL(t *testing.T) { ID: "alpine", VersionID: "3.4.6", }, - expected: "pkg:apk/alpine/p@v?arch=a&upstream=potatoes&distro=alpine-3.4.6", + expected: "pkg:apk/alpine/py3-potatoes@v?arch=a&upstream=potatoes&distro=alpine-3.4.6", + }, + { + name: "python package with origin package as upstream", + metadata: pkg.ApkMetadata{ + Package: "py3-non-existant", + Version: "v", + Architecture: "a", + OriginPackage: "abcdefg", + }, + distro: linux.Release{ + ID: "alpine", + VersionID: "3.4.6", + }, + expected: "pkg:apk/alpine/py3-non-existant@v?arch=a&upstream=abcdefg&distro=alpine-3.4.6", }, } diff --git a/syft/pkg/cataloger/common/cpe/apk.go b/syft/pkg/cataloger/common/cpe/apk.go index 98197b80870..38e3a0b78d2 100644 --- a/syft/pkg/cataloger/common/cpe/apk.go +++ b/syft/pkg/cataloger/common/cpe/apk.go @@ -11,78 +11,163 @@ var ( rubyPrefixes = []string{"ruby-"} ) -func candidateVendorsForAPK(p pkg.Package) fieldCandidateSet { - metadata, ok := p.Metadata.(pkg.ApkMetadata) - if !ok { - return nil +func pythonCandidateVendorsFromName(v string) fieldCandidateSet { + vendors := newFieldCandidateSet() + vendors.add(fieldCandidate{ + value: v, + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + + vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.PythonPkg, v, v)...) + vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.PythonPkg, v)...) + + for _, av := range additionalVendorsForPython(v) { + vendors.add(fieldCandidate{ + value: av, + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.PythonPkg, av, av)...) + vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.PythonPkg, av)...) } + return vendors +} + +func pythonCandidateVendorsFromAPK(m pkg.ApkMetadata) fieldCandidateSet { vendors := newFieldCandidateSet() for _, p := range pythonPrefixes { - if strings.HasPrefix(metadata.Package, p) { - t := strings.TrimPrefix(metadata.Package, p) - vendors.add(fieldCandidate{ - value: t, - disallowSubSelections: true, - disallowDelimiterVariations: true, - }) - vendors.union(additionalVendorsForPython(t)) + if strings.HasPrefix(m.Package, p) { + t := strings.ToLower(strings.TrimPrefix(m.Package, p)) + vendors.union(pythonCandidateVendorsFromName(t)) } - if strings.HasPrefix(metadata.OriginPackage, p) { - t := strings.TrimPrefix(metadata.OriginPackage, p) - vendors.add(fieldCandidate{ - value: t, - disallowSubSelections: true, - disallowDelimiterVariations: true, - }) - vendors.union(additionalVendorsForPython(t)) + if m.OriginPackage != m.Package && strings.HasPrefix(m.OriginPackage, p) { + t := strings.ToLower(strings.TrimPrefix(m.OriginPackage, p)) + vendors.union(pythonCandidateVendorsFromName(t)) } } - for _, p := range rubyPrefixes { - if strings.HasPrefix(metadata.Package, p) { - t := strings.TrimPrefix(metadata.Package, p) - vendors.addValue(t) + return vendors +} + +func pythonCandidateProductsFromName(p string) fieldCandidateSet { + products := newFieldCandidateSet() + products.add(fieldCandidate{ + value: p, + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + + products.addValue(findAdditionalProducts(defaultCandidateAdditions, pkg.PythonPkg, p)...) + products.removeByValue(findProductsToRemove(defaultCandidateRemovals, pkg.PythonPkg, p)...) + return products +} + +func pythonCandidateProductsFromAPK(m pkg.ApkMetadata) fieldCandidateSet { + products := newFieldCandidateSet() + + for _, p := range pythonPrefixes { + if strings.HasPrefix(m.Package, p) { + t := strings.ToLower(strings.TrimPrefix(m.Package, p)) + products.union(pythonCandidateProductsFromName(t)) } - if strings.HasPrefix(metadata.OriginPackage, p) { - t := strings.TrimPrefix(metadata.OriginPackage, p) - vendors.addValue(t) + if m.OriginPackage != m.Package && strings.HasPrefix(m.OriginPackage, p) { + t := strings.ToLower(strings.TrimPrefix(m.OriginPackage, p)) + products.union(pythonCandidateProductsFromName(t)) } } - return vendors + return products } -func candidateProductsForAPK(p pkg.Package) fieldCandidateSet { - metadata, ok := p.Metadata.(pkg.ApkMetadata) - if !ok { - return nil - } +func rubyCandidateVendorsFromName(v string) fieldCandidateSet { + vendors := newFieldCandidateSet() + vendors.add(fieldCandidate{ + value: v, + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + + vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.GemPkg, v, v)...) + vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.GemPkg, v)...) + return vendors +} - products := newFieldCandidateSet() +func rubyCandidateVendorsFromAPK(m pkg.ApkMetadata) fieldCandidateSet { + vendors := newFieldCandidateSet() - for _, p := range pythonPrefixes { - if strings.HasPrefix(metadata.Package, p) { - products.addValue(strings.TrimPrefix(metadata.Package, p)) + for _, p := range rubyPrefixes { + if strings.HasPrefix(m.Package, p) { + t := strings.ToLower(strings.TrimPrefix(m.Package, p)) + vendors.union(rubyCandidateVendorsFromName(t)) } - if strings.HasPrefix(metadata.OriginPackage, p) { - products.addValue(strings.TrimPrefix(metadata.OriginPackage, p)) + if m.OriginPackage != m.Package && strings.HasPrefix(m.OriginPackage, p) { + t := strings.ToLower(strings.TrimPrefix(m.OriginPackage, p)) + vendors.union(rubyCandidateVendorsFromName(t)) } } + return vendors +} + +func rubyCandidateProductsFromName(p string) fieldCandidateSet { + products := newFieldCandidateSet() + products.add(fieldCandidate{ + value: p, + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + + products.addValue(findAdditionalProducts(defaultCandidateAdditions, pkg.GemPkg, p)...) + products.removeByValue(findProductsToRemove(defaultCandidateRemovals, pkg.GemPkg, p)...) + return products +} + +func rubyCandidateProductsFromAPK(m pkg.ApkMetadata) fieldCandidateSet { + products := newFieldCandidateSet() + for _, p := range rubyPrefixes { - if strings.HasPrefix(metadata.Package, p) { - products.addValue(strings.TrimPrefix(metadata.Package, p)) + if strings.HasPrefix(m.Package, p) { + t := strings.ToLower(strings.TrimPrefix(m.Package, p)) + products.union(rubyCandidateProductsFromName(t)) } - if strings.HasPrefix(metadata.OriginPackage, p) { - products.addValue(strings.TrimPrefix(metadata.OriginPackage, p)) + if m.OriginPackage != m.Package && strings.HasPrefix(m.OriginPackage, p) { + t := strings.ToLower(strings.TrimPrefix(m.OriginPackage, p)) + products.union(rubyCandidateProductsFromName(t)) } } return products } + +func candidateVendorsForAPK(p pkg.Package) fieldCandidateSet { + metadata, ok := p.Metadata.(pkg.ApkMetadata) + if !ok { + return nil + } + + vendors := newFieldCandidateSet() + vendors.union(pythonCandidateVendorsFromAPK(metadata)) + vendors.union(rubyCandidateVendorsFromAPK(metadata)) + + return vendors +} + +func candidateProductsForAPK(p pkg.Package) fieldCandidateSet { + metadata, ok := p.Metadata.(pkg.ApkMetadata) + if !ok { + return nil + } + + products := newFieldCandidateSet() + products.union(pythonCandidateProductsFromAPK(metadata)) + products.union(rubyCandidateProductsFromAPK(metadata)) + + return products +} diff --git a/syft/pkg/cataloger/common/cpe/apk_test.go b/syft/pkg/cataloger/common/cpe/apk_test.go index 87bf2ca5f35..ab3297f01e5 100644 --- a/syft/pkg/cataloger/common/cpe/apk_test.go +++ b/syft/pkg/cataloger/common/cpe/apk_test.go @@ -3,6 +3,7 @@ package cpe import ( "testing" + "github.com/anchore/syft/syft/pkg" "github.com/stretchr/testify/assert" ) @@ -19,7 +20,7 @@ func Test_candidateVendorsForAPK(t *testing.T) { Package: "py3-cryptography", }, }, - expected: []string{"cryptography", "cryptographyproject", "cryptography_project"}, + expected: []string{"python-cryptography_project", "cryptography", "cryptographyproject", "cryptography_project"}, }, { name: "py2-pypdf OriginPackage", @@ -37,13 +38,12 @@ func Test_candidateVendorsForAPK(t *testing.T) { Package: "ruby-armadillo", }, }, - expected: []string{"armadillo", "armadilloproject", "armadillo_project"}, + expected: []string{"armadillo"}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - actual := candidateVendorsForAPK(test.pkg) - assert.ElementsMatch(t, test.expected, actual, "different vendors") + assert.ElementsMatch(t, test.expected, candidateVendorsForAPK(test.pkg).values(), "different vendors") }) } } @@ -61,7 +61,7 @@ func Test_candidateProductsForAPK(t *testing.T) { Package: "py3-cryptography", }, }, - expected: []string{"cryptography"}, + expected: []string{"cryptography", "python-cryptography"}, }, { name: "py2-pypdf OriginPackage", @@ -84,8 +84,7 @@ func Test_candidateProductsForAPK(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - actual := candidateProductsForAPK(test.pkg) - assert.ElementsMatch(t, test.expected, actual, "different products") + assert.ElementsMatch(t, test.expected, candidateProductsForAPK(test.pkg).values(), "different products") }) } } diff --git a/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go b/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go index b5c6e6bda73..a48e6f720fa 100644 --- a/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go +++ b/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go @@ -130,7 +130,7 @@ var defaultCandidateAdditions = buildCandidateLookup( { pkg.PythonPkg, candidateKey{PkgName: "cryptography"}, - candidateAddition{AdditionalVendors: []string{"pypa"}}, + candidateAddition{AdditionalProducts: []string{"python-cryptography"}, AdditionalVendors: []string{"python-cryptography_project"}}, }, // Alpine packages { diff --git a/syft/pkg/cataloger/common/cpe/generate_test.go b/syft/pkg/cataloger/common/cpe/generate_test.go index 2b1f581c048..fe966e603c2 100644 --- a/syft/pkg/cataloger/common/cpe/generate_test.go +++ b/syft/pkg/cataloger/common/cpe/generate_test.go @@ -100,6 +100,18 @@ func TestGeneratePackageCPEs(t *testing.T) { "cpe:2.3:a:william_goodman:name:3.2:*:*:*:*:*:*:*", "cpe:2.3:a:william_goodman:python-name:3.2:*:*:*:*:*:*:*", "cpe:2.3:a:william_goodman:python_name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:alex_goodman_project:python_name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:alex_goodman_project:name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:alex_goodman_project:python-name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:alex_goodmanproject:name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:alex_goodmanproject:python-name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:alex_goodmanproject:python_name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:william_goodman_project:name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:william_goodman_project:python-name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:william_goodman_project:python_name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:william_goodmanproject:name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:william_goodmanproject:python-name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:william_goodmanproject:python_name:3.2:*:*:*:*:*:*:*", }, }, { diff --git a/syft/pkg/cataloger/common/cpe/python.go b/syft/pkg/cataloger/common/cpe/python.go index 15f17c07efc..58fd2adb5e8 100644 --- a/syft/pkg/cataloger/common/cpe/python.go +++ b/syft/pkg/cataloger/common/cpe/python.go @@ -7,21 +7,9 @@ import ( "github.com/anchore/syft/syft/pkg" ) -func additionalVendorsForPython(v string) fieldCandidateSet { - vendors := newFieldCandidateSet() - +func additionalVendorsForPython(v string) (vendors []string) { if !strings.HasSuffix(v, "project") { - vendors.add(fieldCandidate{ - value: fmt.Sprintf("%sproject", v), - disallowSubSelections: true, - disallowDelimiterVariations: true, - }) - - vendors.add(fieldCandidate{ - value: fmt.Sprintf("%s_project", v), - disallowSubSelections: true, - disallowDelimiterVariations: true, - }) + vendors = append(vendors, fmt.Sprintf("%sproject", v), fmt.Sprintf("%s_project", v)) } return vendors @@ -43,7 +31,13 @@ func candidateVendorsForPython(p pkg.Package) fieldCandidateSet { disallowDelimiterVariations: true, }) - vendors.union(additionalVendorsForPython(name)) + for _, v := range additionalVendorsForPython(name) { + vendors.add(fieldCandidate{ + value: v, + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + } } if metadata.AuthorEmail != "" { @@ -52,7 +46,14 @@ func candidateVendorsForPython(p pkg.Package) fieldCandidateSet { value: name, disallowSubSelections: true, }) - vendors.union(additionalVendorsForPython(name)) + + for _, v := range additionalVendorsForPython(name) { + vendors.add(fieldCandidate{ + value: v, + disallowSubSelections: true, + disallowDelimiterVariations: true, + }) + } } return vendors From 81e75e0fe83650261f4121a5ba0b9c987c95e23d Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Mon, 13 Feb 2023 14:47:47 +0000 Subject: [PATCH 6/9] fix: apk CPE update for nodejs-current Signed-off-by: Weston Steimel --- syft/pkg/cataloger/common/cpe/candidate_by_package_type.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go b/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go index a48e6f720fa..0a9693f053f 100644 --- a/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go +++ b/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go @@ -148,6 +148,11 @@ var defaultCandidateAdditions = buildCandidateLookup( candidateKey{PkgName: "nodejs"}, candidateAddition{AdditionalProducts: []string{"node.js"}}, }, + { + pkg.ApkPkg, + candidateKey{PkgName: "nodejs-current"}, + candidateAddition{AdditionalProducts: []string{"node.js"}}, + }, // Binary packages { pkg.BinaryPkg, From be0a1989c8f9c8cd2257040bd9204a59b3497b91 Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Mon, 13 Feb 2023 15:35:15 +0000 Subject: [PATCH 7/9] fix: CPE update for python pip Signed-off-by: Weston Steimel --- syft/pkg/cataloger/common/cpe/candidate_by_package_type.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go b/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go index 0a9693f053f..d7de0ce0b56 100644 --- a/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go +++ b/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go @@ -132,6 +132,11 @@ var defaultCandidateAdditions = buildCandidateLookup( candidateKey{PkgName: "cryptography"}, candidateAddition{AdditionalProducts: []string{"python-cryptography"}, AdditionalVendors: []string{"python-cryptography_project"}}, }, + { + pkg.PythonPkg, + candidateKey{PkgName: "pip"}, + candidateAddition{AdditionalVendors: []string{"pypa"}}, + }, // Alpine packages { pkg.ApkPkg, From 97c43e4a37f7a55e4e1d512efff6aaacbd6fee57 Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Mon, 13 Feb 2023 15:50:06 +0000 Subject: [PATCH 8/9] fix: CPE update for some ruby packages Signed-off-by: Weston Steimel --- .../common/cpe/candidate_by_package_type.go | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go b/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go index d7de0ce0b56..4d74cbc176d 100644 --- a/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go +++ b/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go @@ -121,6 +121,46 @@ var defaultCandidateAdditions = buildCandidateLookup( candidateKey{PkgName: "yajl-ruby"}, candidateAddition{AdditionalProducts: []string{"yajl-ruby_gem"}}, }, + { + pkg.GemPkg, + candidateKey{PkgName: "cgi"}, + candidateAddition{AdditionalVendors: []string{"ruby-lang"}}, + }, + { + pkg.GemPkg, + candidateKey{PkgName: "date"}, + candidateAddition{AdditionalVendors: []string{"ruby-lang"}}, + }, + { + pkg.GemPkg, + candidateKey{PkgName: "openssl"}, + candidateAddition{AdditionalVendors: []string{"ruby-lang"}}, + }, + { + pkg.GemPkg, + candidateKey{PkgName: "rake"}, + candidateAddition{AdditionalVendors: []string{"ruby-lang"}}, + }, + { + pkg.GemPkg, + candidateKey{PkgName: "rdoc"}, + candidateAddition{AdditionalVendors: []string{"ruby-lang"}}, + }, + { + pkg.GemPkg, + candidateKey{PkgName: "rexml"}, + candidateAddition{AdditionalVendors: []string{"ruby-lang"}}, + }, + { + pkg.GemPkg, + candidateKey{PkgName: "trunk"}, + candidateAddition{AdditionalVendors: []string{"ruby-lang"}}, + }, + { + pkg.GemPkg, + candidateKey{PkgName: "webrick"}, + candidateAddition{AdditionalVendors: []string{"ruby-lang"}}, + }, // Python packages { pkg.PythonPkg, From cfb785ac2b3f7a5665c6d634c0962caf9956ff19 Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Mon, 13 Feb 2023 16:54:03 +0000 Subject: [PATCH 9/9] fix linting Signed-off-by: Weston Steimel --- syft/pkg/cataloger/common/cpe/apk_test.go | 3 ++- syft/pkg/cataloger/common/cpe/generate.go | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/syft/pkg/cataloger/common/cpe/apk_test.go b/syft/pkg/cataloger/common/cpe/apk_test.go index ab3297f01e5..2335501a0bd 100644 --- a/syft/pkg/cataloger/common/cpe/apk_test.go +++ b/syft/pkg/cataloger/common/cpe/apk_test.go @@ -3,8 +3,9 @@ package cpe import ( "testing" - "github.com/anchore/syft/syft/pkg" "github.com/stretchr/testify/assert" + + "github.com/anchore/syft/syft/pkg" ) func Test_candidateVendorsForAPK(t *testing.T) { diff --git a/syft/pkg/cataloger/common/cpe/generate.go b/syft/pkg/cataloger/common/cpe/generate.go index 49ea7a37f89..5ac019cd3a2 100644 --- a/syft/pkg/cataloger/common/cpe/generate.go +++ b/syft/pkg/cataloger/common/cpe/generate.go @@ -159,8 +159,7 @@ func candidateProducts(p pkg.Package) []string { } } - switch p.MetadataType { - case pkg.ApkMetadataType: + if p.MetadataType == pkg.ApkMetadataType { products.union(candidateProductsForAPK(p)) }