github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/cpe/by_specificity.go (about)

     1  package cpe
     2  
     3  import (
     4  	"sort"
     5  )
     6  
     7  var _ sort.Interface = (*BySpecificity)(nil)
     8  
     9  type BySpecificity []Attributes
    10  
    11  func (c BySpecificity) Len() int { return len(c) }
    12  
    13  func (c BySpecificity) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
    14  
    15  func (c BySpecificity) Less(i, j int) bool {
    16  	return isMoreSpecific(c[i], c[j])
    17  }
    18  
    19  // Returns true if i is more specific than j, with some
    20  // tie breaking mechanisms to make sorting equally-specific cpe Attributes
    21  // deterministic.
    22  func isMoreSpecific(i, j Attributes) bool {
    23  	iScore := weightedCountForSpecifiedFields(i)
    24  	jScore := weightedCountForSpecifiedFields(j)
    25  
    26  	// check weighted sort first
    27  	if iScore != jScore {
    28  		return iScore > jScore
    29  	}
    30  
    31  	// sort longer fields to top
    32  	if countFieldLength(i) != countFieldLength(j) {
    33  		return countFieldLength(i) > countFieldLength(j)
    34  	}
    35  
    36  	// if score and length are equal then text sort
    37  	// note that we are not using String from the syft pkg
    38  	// as we are not encoding/decoding this Attributes string so we don't
    39  	// need the proper quoted version of the Attributes.
    40  	return i.BindToFmtString() < j.BindToFmtString()
    41  }
    42  
    43  func countFieldLength(cpe Attributes) int {
    44  	return len(cpe.Part + cpe.Vendor + cpe.Product + cpe.Version + cpe.TargetSW)
    45  }
    46  
    47  func weightedCountForSpecifiedFields(cpe Attributes) int {
    48  	checksForSpecifiedField := []func(cpe Attributes) (bool, int){
    49  		func(cpe Attributes) (bool, int) { return cpe.Part != "", 2 },
    50  		func(cpe Attributes) (bool, int) { return cpe.Vendor != "", 3 },
    51  		func(cpe Attributes) (bool, int) { return cpe.Product != "", 4 },
    52  		func(cpe Attributes) (bool, int) { return cpe.Version != "", 1 },
    53  		func(cpe Attributes) (bool, int) { return cpe.TargetSW != "", 1 },
    54  	}
    55  
    56  	weightedCount := 0
    57  	for _, fieldIsSpecified := range checksForSpecifiedField {
    58  		isSpecified, weight := fieldIsSpecified(cpe)
    59  		if isSpecified {
    60  			weightedCount += weight
    61  		}
    62  	}
    63  
    64  	return weightedCount
    65  }