chore: refactor basic CPE functionality to its own package (#1436)

This commit is contained in:
Keith Zantow
2023-01-04 11:26:28 -05:00
committed by GitHub
parent e3d6ffd30e
commit 64be0a1072
28 changed files with 330 additions and 281 deletions
+12 -11
View File
@@ -5,13 +5,14 @@ import (
"github.com/facebookincubator/nvdtools/wfn"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg"
)
const jenkinsName = "jenkins"
// filterFn instances should return true if the given CPE should be removed from a collection for the given package
type filterFn func(cpe pkg.CPE, p pkg.Package) bool
type filterFn func(cpe cpe.CPE, p pkg.Package) bool
var cpeFilters = []filterFn{
disallowJiraClientServerMismatch,
@@ -20,23 +21,23 @@ var cpeFilters = []filterFn{
disallowNonParseableCPEs,
}
func filter(cpes []pkg.CPE, p pkg.Package, filters ...filterFn) (result []pkg.CPE) {
func filter(cpes []cpe.CPE, p pkg.Package, filters ...filterFn) (result []cpe.CPE) {
cpeLoop:
for _, cpe := range cpes {
for _, c := range cpes {
for _, fn := range filters {
if fn(cpe, p) {
if fn(c, p) {
continue cpeLoop
}
}
// all filter functions passed on filtering this CPE
result = append(result, cpe)
result = append(result, c)
}
return result
}
func disallowNonParseableCPEs(cpe pkg.CPE, _ pkg.Package) bool {
v := pkg.CPEString(cpe)
_, err := pkg.NewCPE(v)
func disallowNonParseableCPEs(c cpe.CPE, _ pkg.Package) bool {
v := cpe.String(c)
_, err := cpe.New(v)
cannotParse := err != nil
@@ -44,7 +45,7 @@ func disallowNonParseableCPEs(cpe pkg.CPE, _ pkg.Package) bool {
}
// jenkins plugins should not match against jenkins
func disallowJenkinsServerCPEForPluginPackage(cpe pkg.CPE, p pkg.Package) bool {
func disallowJenkinsServerCPEForPluginPackage(cpe cpe.CPE, p pkg.Package) bool {
if p.Type == pkg.JenkinsPluginPkg && cpe.Product == jenkinsName {
return true
}
@@ -52,7 +53,7 @@ func disallowJenkinsServerCPEForPluginPackage(cpe pkg.CPE, p pkg.Package) bool {
}
// filter to account that packages that are not for jenkins but have a CPE generated that will match against jenkins
func disallowJenkinsCPEsNotAssociatedWithJenkins(cpe pkg.CPE, p pkg.Package) bool {
func disallowJenkinsCPEsNotAssociatedWithJenkins(cpe cpe.CPE, p pkg.Package) bool {
// jenkins server should only match against a product with the name jenkins
if cpe.Product == jenkinsName && !strings.Contains(strings.ToLower(p.Name), jenkinsName) {
if cpe.Vendor == wfn.Any || cpe.Vendor == jenkinsName || cpe.Vendor == "cloudbees" {
@@ -63,7 +64,7 @@ func disallowJenkinsCPEsNotAssociatedWithJenkins(cpe pkg.CPE, p pkg.Package) boo
}
// filter to account for packages which are jira client packages but have a CPE that will match against jira
func disallowJiraClientServerMismatch(cpe pkg.CPE, p pkg.Package) bool {
func disallowJiraClientServerMismatch(cpe cpe.CPE, p pkg.Package) bool {
// jira / atlassian should not apply to clients
if cpe.Product == "jira" && strings.Contains(strings.ToLower(p.Name), "client") {
if cpe.Vendor == wfn.Any || cpe.Vendor == "jira" || cpe.Vendor == "atlassian" {
+17 -16
View File
@@ -5,19 +5,20 @@ import (
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg"
)
func Test_disallowJenkinsServerCPEForPluginPackage(t *testing.T) {
tests := []struct {
name string
cpe pkg.CPE
cpe cpe.CPE
pkg pkg.Package
expected bool
}{
{
name: "go case (filter out)",
cpe: pkg.MustCPE("cpe:2.3:a:name:jenkins:3.2:*:*:*:*:*:*:*"),
cpe: cpe.Must("cpe:2.3:a:name:jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Type: pkg.JenkinsPluginPkg,
},
@@ -25,7 +26,7 @@ func Test_disallowJenkinsServerCPEForPluginPackage(t *testing.T) {
},
{
name: "ignore jenkins plugins with unique name",
cpe: pkg.MustCPE("cpe:2.3:a:name:ci-jenkins:3.2:*:*:*:*:*:*:*"),
cpe: cpe.Must("cpe:2.3:a:name:ci-jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Type: pkg.JenkinsPluginPkg,
},
@@ -33,7 +34,7 @@ func Test_disallowJenkinsServerCPEForPluginPackage(t *testing.T) {
},
{
name: "ignore java packages",
cpe: pkg.MustCPE("cpe:2.3:a:name:jenkins:3.2:*:*:*:*:*:*:*"),
cpe: cpe.Must("cpe:2.3:a:name:jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Type: pkg.JavaPkg,
},
@@ -50,13 +51,13 @@ func Test_disallowJenkinsServerCPEForPluginPackage(t *testing.T) {
func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
tests := []struct {
name string
cpe pkg.CPE
cpe cpe.CPE
pkg pkg.Package
expected bool
}{
{
name: "filter out mismatched name (cloudbees vendor)",
cpe: pkg.MustCPE("cpe:2.3:a:cloudbees:jenkins:3.2:*:*:*:*:*:*:*"),
cpe: cpe.Must("cpe:2.3:a:cloudbees:jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "not-j*nkins",
Type: pkg.JavaPkg,
@@ -65,7 +66,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
},
{
name: "filter out mismatched name (jenkins vendor)",
cpe: pkg.MustCPE("cpe:2.3:a:jenkins:jenkins:3.2:*:*:*:*:*:*:*"),
cpe: cpe.Must("cpe:2.3:a:jenkins:jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "not-j*nkins",
Type: pkg.JavaPkg,
@@ -74,7 +75,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
},
{
name: "filter out mismatched name (any vendor)",
cpe: pkg.MustCPE("cpe:2.3:a:*:jenkins:3.2:*:*:*:*:*:*:*"),
cpe: cpe.Must("cpe:2.3:a:*:jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "not-j*nkins",
Type: pkg.JavaPkg,
@@ -83,7 +84,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
},
{
name: "ignore packages with the name jenkins",
cpe: pkg.MustCPE("cpe:2.3:a:*:jenkins:3.2:*:*:*:*:*:*:*"),
cpe: cpe.Must("cpe:2.3:a:*:jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "jenkins-thing",
Type: pkg.JavaPkg,
@@ -92,7 +93,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
},
{
name: "ignore product names that are not exactly 'jenkins'",
cpe: pkg.MustCPE("cpe:2.3:a:*:jenkins-something-else:3.2:*:*:*:*:*:*:*"),
cpe: cpe.Must("cpe:2.3:a:*:jenkins-something-else:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "not-j*nkins",
Type: pkg.JavaPkg,
@@ -110,13 +111,13 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
func Test_disallowJiraClientServerMismatch(t *testing.T) {
tests := []struct {
name string
cpe pkg.CPE
cpe cpe.CPE
pkg pkg.Package
expected bool
}{
{
name: "filter out mismatched name (atlassian vendor)",
cpe: pkg.MustCPE("cpe:2.3:a:atlassian:jira:3.2:*:*:*:*:*:*:*"),
cpe: cpe.Must("cpe:2.3:a:atlassian:jira:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "something-client",
Type: pkg.JavaPkg,
@@ -125,7 +126,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
},
{
name: "filter out mismatched name (jira vendor)",
cpe: pkg.MustCPE("cpe:2.3:a:jira:jira:3.2:*:*:*:*:*:*:*"),
cpe: cpe.Must("cpe:2.3:a:jira:jira:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "something-client",
Type: pkg.JavaPkg,
@@ -134,7 +135,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
},
{
name: "filter out mismatched name (any vendor)",
cpe: pkg.MustCPE("cpe:2.3:a:*:jira:3.2:*:*:*:*:*:*:*"),
cpe: cpe.Must("cpe:2.3:a:*:jira:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "something-client",
Type: pkg.JavaPkg,
@@ -143,7 +144,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
},
{
name: "ignore package names that do not have 'client'",
cpe: pkg.MustCPE("cpe:2.3:a:*:jira:3.2:*:*:*:*:*:*:*"),
cpe: cpe.Must("cpe:2.3:a:*:jira:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "jira-thing",
Type: pkg.JavaPkg,
@@ -152,7 +153,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
},
{
name: "ignore product names that are not exactly 'jira'",
cpe: pkg.MustCPE("cpe:2.3:a:*:jira-something-else:3.2:*:*:*:*:*:*:*"),
cpe: cpe.Must("cpe:2.3:a:*:jira-something-else:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "not-j*ra",
Type: pkg.JavaPkg,
+14 -13
View File
@@ -10,26 +10,27 @@ import (
"github.com/facebookincubator/nvdtools/wfn"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg"
)
func newCPE(product, vendor, version, targetSW string) *wfn.Attributes {
cpe := *(wfn.NewAttributesWithAny())
cpe.Part = "a"
cpe.Product = product
cpe.Vendor = vendor
cpe.Version = version
cpe.TargetSW = targetSW
if pkg.ValidateCPEString(pkg.CPEString(cpe)) != nil {
c := *(wfn.NewAttributesWithAny())
c.Part = "a"
c.Product = product
c.Vendor = vendor
c.Version = version
c.TargetSW = targetSW
if cpe.ValidateString(cpe.String(c)) != nil {
return nil
}
return &cpe
return &c
}
// Generate Create a list of CPEs for a given package, trying to guess the vendor, product tuple. We should be trying to
// generate the minimal set of representative CPEs, which implies that optional fields should not be included
// (such as target SW).
func Generate(p pkg.Package) []pkg.CPE {
func Generate(p pkg.Package) []cpe.CPE {
vendors := candidateVendors(p)
products := candidateProducts(p)
if len(products) == 0 {
@@ -37,7 +38,7 @@ func Generate(p pkg.Package) []pkg.CPE {
}
keys := internal.NewStringSet()
cpes := make([]pkg.CPE, 0)
cpes := make([]cpe.CPE, 0)
for _, product := range products {
for _, vendor := range vendors {
// prevent duplicate entries...
@@ -47,8 +48,8 @@ func Generate(p pkg.Package) []pkg.CPE {
}
keys.Add(key)
// add a new entry...
if cpe := newCPE(product, vendor, p.Version, wfn.Any); cpe != nil {
cpes = append(cpes, *cpe)
if c := newCPE(product, vendor, p.Version, wfn.Any); c != nil {
cpes = append(cpes, *c)
}
}
}
@@ -56,7 +57,7 @@ func Generate(p pkg.Package) []pkg.CPE {
// filter out any known combinations that don't accurately represent this package
cpes = filter(cpes, p, cpeFilters...)
sort.Sort(pkg.CPEBySpecificity(cpes))
sort.Sort(cpe.BySpecificity(cpes))
return cpes
}
@@ -10,6 +10,7 @@ import (
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg"
)
@@ -696,7 +697,7 @@ func TestGeneratePackageCPEs(t *testing.T) {
expectedCpeSet := set.NewStringSet(test.expected...)
actualCpeSet := set.NewStringSet()
for _, a := range actual {
actualCpeSet.Add(pkg.CPEString(a))
actualCpeSet.Add(cpe.String(a))
}
extra := strset.Difference(actualCpeSet, expectedCpeSet).List()