github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/pkg/cataloger/common/cpe/field_candidate.go (about)

     1  package cpe
     2  
     3  import (
     4  	"strconv"
     5  
     6  	"github.com/scylladb/go-set/strset"
     7  )
     8  
     9  // fieldCandidate represents a single "guess" for a specific field in a future CPE (vendor, product, target SW, etc).
    10  // When generating these candidates depending on the field the value was sourced from there may be only a subset of
    11  // transforms that should be applied (downstream of extraction). Expressing candidates in this struct allows for this
    12  // flexibility such that downstream transforms can be elected into or skipped over.
    13  type fieldCandidate struct {
    14  	value                       string
    15  	disallowSubSelections       bool
    16  	disallowDelimiterVariations bool
    17  }
    18  
    19  type fieldCandidateSet map[fieldCandidate]struct{}
    20  
    21  func newFieldCandidateSetFromSets(sets ...fieldCandidateSet) fieldCandidateSet {
    22  	s := newFieldCandidateSet()
    23  	for _, set := range sets {
    24  		s.add(set.list()...)
    25  	}
    26  	return s
    27  }
    28  
    29  func newFieldCandidateSet(values ...string) fieldCandidateSet {
    30  	s := make(fieldCandidateSet)
    31  	s.addValue(values...)
    32  	return s
    33  }
    34  
    35  func (s fieldCandidateSet) addValue(values ...string) {
    36  	for _, value := range values {
    37  		// default candidate as an allow-all
    38  		candidate := fieldCandidate{
    39  			value: cleanCandidateField(value),
    40  		}
    41  		s[candidate] = struct{}{}
    42  	}
    43  }
    44  
    45  func (s fieldCandidateSet) add(candidates ...fieldCandidate) {
    46  	for _, candidate := range candidates {
    47  		candidate.value = cleanCandidateField(candidate.value)
    48  		s[candidate] = struct{}{}
    49  	}
    50  }
    51  
    52  func (s fieldCandidateSet) removeByValue(values ...string) {
    53  	for _, value := range values {
    54  		s.removeWhere(valueEquals(value))
    55  	}
    56  }
    57  
    58  // removeWhere removes all entries from the fieldCandidateSet for which the condition function returns true.
    59  func (s fieldCandidateSet) removeWhere(condition fieldCandidateCondition) {
    60  	for candidate := range s {
    61  		if condition(candidate) {
    62  			delete(s, candidate)
    63  		}
    64  	}
    65  }
    66  
    67  func (s fieldCandidateSet) clear() {
    68  	for k := range s {
    69  		delete(s, k)
    70  	}
    71  }
    72  
    73  func (s fieldCandidateSet) union(others ...fieldCandidateSet) {
    74  	for _, other := range others {
    75  		s.add(other.list()...)
    76  	}
    77  }
    78  
    79  func (s fieldCandidateSet) list() (results []fieldCandidate) {
    80  	for c := range s {
    81  		results = append(results, c)
    82  	}
    83  
    84  	return results
    85  }
    86  
    87  func (s fieldCandidateSet) values() (results []string) {
    88  	for _, c := range s.list() {
    89  		results = append(results, c.value)
    90  	}
    91  
    92  	return results
    93  }
    94  
    95  func (s fieldCandidateSet) uniqueValues() []string {
    96  	return strset.New(s.values()...).List()
    97  }
    98  
    99  func (s fieldCandidateSet) copy() fieldCandidateSet {
   100  	newSet := newFieldCandidateSet()
   101  	newSet.add(s.list()...)
   102  
   103  	return newSet
   104  }
   105  
   106  func cleanCandidateField(field string) string {
   107  	cleanedValue, err := strconv.Unquote(field)
   108  	if err != nil {
   109  		return field
   110  	}
   111  	return cleanedValue
   112  }