diff --git a/syft/cataloger/cpe.go b/syft/cataloger/cpe.go index 637cc2f86..52fb74c79 100644 --- a/syft/cataloger/cpe.go +++ b/syft/cataloger/cpe.go @@ -2,6 +2,7 @@ package cataloger import ( "fmt" + "sort" "strings" "github.com/anchore/syft/internal" @@ -12,6 +13,17 @@ import ( // this is functionally equivalent to "*" and consistent with no input given (thus easier to test) const any = "" +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 + + return cpe +} + // generatePackageCPEs Create a list of CPEs, trying to guess the vendor, product tuple and setting TargetSoftware if possible func generatePackageCPEs(p pkg.Package) []pkg.CPE { targetSws := candidateTargetSoftwareAttrs(p) @@ -31,18 +43,14 @@ func generatePackageCPEs(p pkg.Package) []pkg.CPE { keys.Add(key) // add a new entry... - candidateCpe := wfn.NewAttributesWithAny() - candidateCpe.Part = "a" - candidateCpe.Product = product - candidateCpe.Vendor = vendor - candidateCpe.Version = p.Version - candidateCpe.TargetSW = targetSw - - cpes = append(cpes, *candidateCpe) + c := newCPE(product, vendor, p.Version, targetSw) + cpes = append(cpes, c) } } } + sort.Sort(ByCPESpecificity(cpes)) + return cpes } diff --git a/syft/cataloger/cpe_specificity.go b/syft/cataloger/cpe_specificity.go new file mode 100644 index 000000000..1e927704b --- /dev/null +++ b/syft/cataloger/cpe_specificity.go @@ -0,0 +1,31 @@ +package cataloger + +import "github.com/facebookincubator/nvdtools/wfn" + +type ByCPESpecificity []wfn.Attributes + +// Implementing sort.Interface +func (c ByCPESpecificity) Len() int { return len(c) } +func (c ByCPESpecificity) Swap(i, j int) { c[i], c[j] = c[j], c[i] } +func (c ByCPESpecificity) Less(i, j int) bool { + return countSpecifiedFields(c[i]) > countSpecifiedFields(c[j]) +} + +func countSpecifiedFields(cpe wfn.Attributes) int { + checksForSpecifiedField := []func(cpe wfn.Attributes) bool{ + func(cpe wfn.Attributes) bool { return cpe.Part != "" }, + func(cpe wfn.Attributes) bool { return cpe.Product != "" }, + func(cpe wfn.Attributes) bool { return cpe.Vendor != "" }, + func(cpe wfn.Attributes) bool { return cpe.Version != "" }, + func(cpe wfn.Attributes) bool { return cpe.TargetSW != "" }, + } + + count := 0 + for _, fieldIsSpecified := range checksForSpecifiedField { + if fieldIsSpecified(cpe) { + count++ + } + } + + return count +}