Skip to content

Commit

Permalink
fix: add manual vendor/product removal to fix false flags (#1070)
Browse files Browse the repository at this point in the history
  • Loading branch information
cpendery authored Dec 8, 2022
1 parent f1a1242 commit 668f102
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 0 deletions.
66 changes: 66 additions & 0 deletions syft/pkg/cataloger/common/cpe/candidate_by_package_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
}
78 changes: 78 additions & 0 deletions syft/pkg/cataloger/common/cpe/candidate_by_package_type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
})
}
}
6 changes: 6 additions & 0 deletions syft/pkg/cataloger/common/cpe/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

Expand Down Expand Up @@ -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()
}

Expand Down
21 changes: 21 additions & 0 deletions syft/pkg/cataloger/common/cpe/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 668f102

Please sign in to comment.