fix: add manual vendor/product removal to fix false flags (#1070)

Closes https://github.com/anchore/syft/issues/1066
Closes https://github.com/anchore/grype/issues/800
Closes https://github.com/anchore/grype/issues/491
This commit is contained in:
Chapman Pendery 2022-12-08 09:57:42 -05:00 committed by GitHub
parent f1a124209a
commit 668f102340
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 171 additions and 0 deletions

View File

@ -11,6 +11,12 @@ type candidateComposite struct {
candidateAddition
}
type candidateRemovalComposite struct {
pkg.Type
candidateKey
candidateRemovals
}
// defaultCandidateAdditions is all of the known cases for product and vendor field values that should be used when
// select package information is discovered
var defaultCandidateAdditions = buildCandidateLookup(
@ -123,6 +129,16 @@ var defaultCandidateAdditions = buildCandidateLookup(
},
})
var defaultCandidateRemovals = buildCandidateRemovalLookup(
[]candidateRemovalComposite{
// Python packages
{
pkg.PythonPkg,
candidateKey{PkgName: "redis"},
candidateRemovals{VendorsToRemove: []string{"redis"}},
},
})
// buildCandidateLookup is a convenience function for creating the defaultCandidateAdditions set
func buildCandidateLookup(cc []candidateComposite) (ca map[pkg.Type]map[candidateKey]candidateAddition) {
ca = make(map[pkg.Type]map[candidateKey]candidateAddition)
@ -136,12 +152,30 @@ func buildCandidateLookup(cc []candidateComposite) (ca map[pkg.Type]map[candidat
return ca
}
// buildCandidateRemovalLookup is a convenience function for creating the defaultCandidateRemovals set
func buildCandidateRemovalLookup(cc []candidateRemovalComposite) (ca map[pkg.Type]map[candidateKey]candidateRemovals) {
ca = make(map[pkg.Type]map[candidateKey]candidateRemovals)
for _, c := range cc {
if _, ok := ca[c.Type]; !ok {
ca[c.Type] = make(map[candidateKey]candidateRemovals)
}
ca[c.Type][c.candidateKey] = c.candidateRemovals
}
return ca
}
// candidateKey represents the set of inputs that should be matched on in order to signal more candidate additions to be used.
type candidateKey struct {
Vendor string
PkgName string
}
// candidateRemovals are the specific removals that should be considered during CPE generation (given a specific candidateKey)
type candidateRemovals struct {
ProductsToRemove []string
VendorsToRemove []string
}
// candidateAddition are the specific additions that should be considered during CPE generation (given a specific candidateKey)
type candidateAddition struct {
AdditionalProducts []string
@ -192,3 +226,35 @@ func findAdditionalProducts(allAdditions map[pkg.Type]map[candidateKey]candidate
return products
}
// findVendorsToRemove searches all possible vendor removals that could be removed during the CPE generation process (given package info + a vendor candidate)
func findVendorsToRemove(allRemovals map[pkg.Type]map[candidateKey]candidateRemovals, ty pkg.Type, pkgName string) (vendors []string) {
removals, ok := allRemovals[ty]
if !ok {
return nil
}
if removal, ok := removals[candidateKey{
PkgName: pkgName,
}]; ok {
vendors = append(vendors, removal.VendorsToRemove...)
}
return vendors
}
// findProductsToRemove searches all possible product removals that could be removed during the CPE generation process (given package info)
func findProductsToRemove(allRemovals map[pkg.Type]map[candidateKey]candidateRemovals, ty pkg.Type, pkgName string) (products []string) {
removals, ok := allRemovals[ty]
if !ok {
return nil
}
if removal, ok := removals[candidateKey{
PkgName: pkgName,
}]; ok {
products = append(products, removal.ProductsToRemove...)
}
return products
}

View File

@ -154,3 +154,81 @@ func Test_additionalVendors(t *testing.T) {
})
}
}
func Test_findVendorsToRemove(t *testing.T) {
//GIVEN
tests := []struct {
name string
ty pkg.Type
pkgName string
expected []string
}{
{
name: "vendor removal match by input package name",
ty: pkg.JavaPkg,
pkgName: "my-package-name",
expected: []string{"awesome-vendor-addition"},
},
{
name: "vendor removal miss by input package name",
ty: pkg.JavaPkg,
pkgName: "my-package-name-1",
},
}
allRemovals := map[pkg.Type]map[candidateKey]candidateRemovals{
pkg.JavaPkg: {
candidateKey{
PkgName: "my-package-name",
}: {
VendorsToRemove: []string{"awesome-vendor-addition"},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
//WHEN + THEN
assert.Equal(t, test.expected, findVendorsToRemove(allRemovals, test.ty, test.pkgName))
})
}
}
func Test_findProductsToRemove(t *testing.T) {
//GIVEN
tests := []struct {
name string
ty pkg.Type
pkgName string
expected []string
}{
{
name: "vendor removal match by input package name",
ty: pkg.JavaPkg,
pkgName: "my-package-name",
expected: []string{"awesome-vendor-addition"},
},
{
name: "vendor removal miss by input package name",
ty: pkg.JavaPkg,
pkgName: "my-package-name-1",
},
}
allRemovals := map[pkg.Type]map[candidateKey]candidateRemovals{
pkg.JavaPkg: {
candidateKey{
PkgName: "my-package-name",
}: {
ProductsToRemove: []string{"awesome-vendor-addition"},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
//WHEN + THEN
assert.Equal(t, test.expected, findProductsToRemove(allRemovals, test.ty, test.pkgName))
})
}
}

View File

@ -116,6 +116,9 @@ func candidateVendors(p pkg.Package) []string {
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, p.Type, p.Name, vendor)...)
}
// remove known mis
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, p.Type, p.Name)...)
return vendors.uniqueValues()
}
@ -148,6 +151,9 @@ func candidateProducts(p pkg.Package) []string {
// add known candidate additions
products.addValue(findAdditionalProducts(defaultCandidateAdditions, p.Type, p.Name)...)
// remove known candidate removals
products.removeByValue(findProductsToRemove(defaultCandidateRemovals, p.Type, p.Name)...)
return products.uniqueValues()
}

View File

@ -666,6 +666,27 @@ func TestGeneratePackageCPEs(t *testing.T) {
"cpe:2.3:a:stephanie_morillo:bundler:2.1.4:*:*:*:*:*:*:*",
},
},
{
name: "regression: python redis shadows normal redis",
p: pkg.Package{
Name: "redis",
Version: "2.1.4",
Type: pkg.PythonPkg,
FoundBy: "some-analyzer",
Language: pkg.Python,
},
expected: []string{
"cpe:2.3:a:python-redis:python-redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python-redis:python_redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python-redis:redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python:python-redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python:python_redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python:redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python_redis:python-redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python_redis:python_redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python_redis:redis:2.1.4:*:*:*:*:*:*:*",
},
},
}
for _, test := range tests {