feat: Record where CPEs come from (#2552)

Syft can get CPEs from several source, including generating them based on
package data, finding them in the NVD CPE dictionary, or finding them declared
in a manifest or existing SBOM. Record where Syft got CPEs so that consumers of
SBOMs can reason about how trustworthy they are.

Signed-off-by: Will Murphy <will.murphy@anchore.com>
This commit is contained in:
William Murphy
2024-02-02 16:17:52 +00:00
committed by GitHub
parent 4fe50f4169
commit b7a6d5e946
47 changed files with 2674 additions and 287 deletions
+23 -16
View File
@@ -6,15 +6,22 @@ import (
var _ sort.Interface = (*BySpecificity)(nil)
type BySpecificity []CPE
type BySpecificity []Attributes
func (c BySpecificity) Len() int { return len(c) }
func (c BySpecificity) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c BySpecificity) Less(i, j int) bool {
iScore := weightedCountForSpecifiedFields(c[i])
jScore := weightedCountForSpecifiedFields(c[j])
return isMoreSpecific(c[i], c[j])
}
// Returns true if i is more specific than j, with some
// tie breaking mechanisms to make sorting equally-specific cpe Attributes
// deterministic.
func isMoreSpecific(i, j Attributes) bool {
iScore := weightedCountForSpecifiedFields(i)
jScore := weightedCountForSpecifiedFields(j)
// check weighted sort first
if iScore != jScore {
@@ -22,28 +29,28 @@ func (c BySpecificity) Less(i, j int) bool {
}
// sort longer fields to top
if countFieldLength(c[i]) != countFieldLength(c[j]) {
return countFieldLength(c[i]) > countFieldLength(c[j])
if countFieldLength(i) != countFieldLength(j) {
return countFieldLength(i) > countFieldLength(j)
}
// if score and length are equal then text sort
// note that we are not using String from the syft pkg
// as we are not encoding/decoding this CPE string so we don't
// need the proper quoted version of the CPE.
return c[i].BindToFmtString() < c[j].BindToFmtString()
// as we are not encoding/decoding this Attributes string so we don't
// need the proper quoted version of the Attributes.
return i.BindToFmtString() < j.BindToFmtString()
}
func countFieldLength(cpe CPE) int {
func countFieldLength(cpe Attributes) int {
return len(cpe.Part + cpe.Vendor + cpe.Product + cpe.Version + cpe.TargetSW)
}
func weightedCountForSpecifiedFields(cpe CPE) int {
checksForSpecifiedField := []func(cpe CPE) (bool, int){
func(cpe CPE) (bool, int) { return cpe.Part != "", 2 },
func(cpe CPE) (bool, int) { return cpe.Vendor != "", 3 },
func(cpe CPE) (bool, int) { return cpe.Product != "", 4 },
func(cpe CPE) (bool, int) { return cpe.Version != "", 1 },
func(cpe CPE) (bool, int) { return cpe.TargetSW != "", 1 },
func weightedCountForSpecifiedFields(cpe Attributes) int {
checksForSpecifiedField := []func(cpe Attributes) (bool, int){
func(cpe Attributes) (bool, int) { return cpe.Part != "", 2 },
func(cpe Attributes) (bool, int) { return cpe.Vendor != "", 3 },
func(cpe Attributes) (bool, int) { return cpe.Product != "", 4 },
func(cpe Attributes) (bool, int) { return cpe.Version != "", 1 },
func(cpe Attributes) (bool, int) { return cpe.TargetSW != "", 1 },
}
weightedCount := 0