From c4cbe211a31dd96cd9da627a0aae99d49ffebf51 Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Fri, 3 Mar 2023 17:26:46 +0000 Subject: [PATCH] feat: disable cpe vendor wildcards to reduce false positives (#1647) * improved parsing of vendor from github url Signed-off-by: Weston Steimel * stop generating wildcard vendors Add logic for parsing javascript and ruby package vendor candidates from url and author fields and stop generating wildcard vendor candidates Signed-off-by: Weston Steimel --------- Signed-off-by: Weston Steimel --- syft/pkg/cataloger/common/cpe/generate.go | 14 ++++---- .../pkg/cataloger/common/cpe/generate_test.go | 21 +++++++----- syft/pkg/cataloger/common/cpe/javascript.go | 32 +++++++++++++++++++ syft/pkg/cataloger/common/cpe/ruby.go | 5 +++ .../cataloger/common/cpe/vendors_from_url.go | 2 +- .../common/cpe/vendors_from_url_test.go | 10 ++++++ 6 files changed, 68 insertions(+), 16 deletions(-) create mode 100644 syft/pkg/cataloger/common/cpe/javascript.go diff --git a/syft/pkg/cataloger/common/cpe/generate.go b/syft/pkg/cataloger/common/cpe/generate.go index 5ac019cd3a2..ba17df0dd06 100644 --- a/syft/pkg/cataloger/common/cpe/generate.go +++ b/syft/pkg/cataloger/common/cpe/generate.go @@ -93,13 +93,6 @@ func candidateVendors(p pkg.Package) []string { } } - // some ecosystems do not have enough metadata to determine the vendor accurately, in which case we selectively - // allow * as a candidate. Note: do NOT allow Java packages to have * vendors. - switch p.Language { - case pkg.Ruby, pkg.JavaScript: - vendors.addValue(wfn.Any) - } - switch p.MetadataType { case pkg.RpmMetadataType: vendors.union(candidateVendorsForRPM(p)) @@ -111,8 +104,15 @@ func candidateVendors(p pkg.Package) []string { vendors.union(candidateVendorsForJava(p)) case pkg.ApkMetadataType: vendors.union(candidateVendorsForAPK(p)) + case pkg.NpmPackageJSONMetadataType: + vendors.union(candidateVendorsForJavascript(p)) } + // We should no longer be generating vendor candidates with these values ["" and "*"] + // (since CPEs will match any other value) + vendors.removeByValue("") + vendors.removeByValue("*") + // try swapping hyphens for underscores, vice versa, and removing separators altogether addDelimiterVariations(vendors) diff --git a/syft/pkg/cataloger/common/cpe/generate_test.go b/syft/pkg/cataloger/common/cpe/generate_test.go index 8841e9edc86..046e9596fb6 100644 --- a/syft/pkg/cataloger/common/cpe/generate_test.go +++ b/syft/pkg/cataloger/common/cpe/generate_test.go @@ -117,15 +117,20 @@ func TestGeneratePackageCPEs(t *testing.T) { { name: "javascript language", p: pkg.Package{ - Name: "name", - Version: "3.2", - FoundBy: "some-analyzer", - Language: pkg.JavaScript, - Type: pkg.DebPkg, + Name: "name", + Version: "3.2", + FoundBy: "some-analyzer", + Language: pkg.JavaScript, + MetadataType: pkg.NpmPackageJSONMetadataType, + Metadata: pkg.NpmPackageJSONMetadata{ + Author: "jon", + URL: "https://github.com/bob/npm-name", + }, }, expected: []string{ "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", - "cpe:2.3:a:*:name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:jon:name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:bob:name:3.2:*:*:*:*:*:*:*", }, }, { @@ -142,10 +147,10 @@ func TestGeneratePackageCPEs(t *testing.T) { "someones name", "someones.elses.name@gmail.com", }, + Homepage: "https://github.com/tom/ruby-name", }, }, expected: []string{ - "cpe:2.3:a:*:name:3.2:*:*:*:*:*:*:*", "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", "cpe:2.3:a:ruby-lang:name:3.2:*:*:*:*:*:*:*", "cpe:2.3:a:ruby:name:3.2:*:*:*:*:*:*:*", @@ -154,6 +159,7 @@ func TestGeneratePackageCPEs(t *testing.T) { "cpe:2.3:a:someones-name:name:3.2:*:*:*:*:*:*:*", "cpe:2.3:a:someones_elses_name:name:3.2:*:*:*:*:*:*:*", "cpe:2.3:a:someones_name:name:3.2:*:*:*:*:*:*:*", + "cpe:2.3:a:tom:name:3.2:*:*:*:*:*:*:*", }, }, { @@ -641,7 +647,6 @@ func TestGeneratePackageCPEs(t *testing.T) { }, }, expected: []string{ - "cpe:2.3:a:*:bundler:2.1.4:*:*:*:*:*:*:*", "cpe:2.3:a:bundler:bundler:2.1.4:*:*:*:*:*:*:*", "cpe:2.3:a:ruby-lang:bundler:2.1.4:*:*:*:*:*:*:*", "cpe:2.3:a:ruby:bundler:2.1.4:*:*:*:*:*:*:*", diff --git a/syft/pkg/cataloger/common/cpe/javascript.go b/syft/pkg/cataloger/common/cpe/javascript.go new file mode 100644 index 00000000000..4f89173c770 --- /dev/null +++ b/syft/pkg/cataloger/common/cpe/javascript.go @@ -0,0 +1,32 @@ +package cpe + +import "github.com/anchore/syft/syft/pkg" + +func candidateVendorsForJavascript(p pkg.Package) fieldCandidateSet { + if p.MetadataType != pkg.NpmPackageJSONMetadataType { + return nil + } + + vendors := newFieldCandidateSet() + metadata, ok := p.Metadata.(pkg.NpmPackageJSONMetadata) + if !ok { + return nil + } + + if metadata.Author != "" { + vendors.add(fieldCandidate{ + value: normalizePersonName(stripEmailSuffix(metadata.Author)), + disallowSubSelections: true, + }) + } + + if metadata.URL != "" { + vendors.union(candidateVendorsFromURL(metadata.URL)) + } + + if metadata.Homepage != "" { + vendors.union(candidateVendorsFromURL(metadata.Homepage)) + } + + return vendors +} diff --git a/syft/pkg/cataloger/common/cpe/ruby.go b/syft/pkg/cataloger/common/cpe/ruby.go index b89e2f6da89..266a858e06a 100644 --- a/syft/pkg/cataloger/common/cpe/ruby.go +++ b/syft/pkg/cataloger/common/cpe/ruby.go @@ -17,5 +17,10 @@ func candidateVendorsForRuby(p pkg.Package) fieldCandidateSet { disallowSubSelections: true, }) } + + if metadata.Homepage != "" { + vendors.union(candidateVendorsFromURL(metadata.Homepage)) + } + return vendors } diff --git a/syft/pkg/cataloger/common/cpe/vendors_from_url.go b/syft/pkg/cataloger/common/cpe/vendors_from_url.go index 9a981914248..596d6d1d473 100644 --- a/syft/pkg/cataloger/common/cpe/vendors_from_url.go +++ b/syft/pkg/cataloger/common/cpe/vendors_from_url.go @@ -21,7 +21,7 @@ var ( } vendorExtractionPatterns = []*regexp.Regexp{ - regexp.MustCompile(`^https://(?:github|gitlab)\.com/(?P[\w\-]*?)/.*$`), + regexp.MustCompile(`^(?:https|http|git)://(?:github|gitlab)\.com/(?P[\w\-]*?)/.*$`), } ) diff --git a/syft/pkg/cataloger/common/cpe/vendors_from_url_test.go b/syft/pkg/cataloger/common/cpe/vendors_from_url_test.go index 19348bd4af8..35c686a25cd 100644 --- a/syft/pkg/cataloger/common/cpe/vendors_from_url_test.go +++ b/syft/pkg/cataloger/common/cpe/vendors_from_url_test.go @@ -52,6 +52,16 @@ func Test_candidateVendorsFromURL(t *testing.T) { url: "https://github.com/armadillo/abcxyz-12345/a/b/c/d/e/f/g", expected: []string{"armadillo"}, }, + { + name: "github username from git://", + url: "git://github.com/abc/xyz.git", + expected: []string{"abc"}, + }, + { + name: "github username from http://", + url: "http://github.com/abc/xyz.git", + expected: []string{"abc"}, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) {