github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/pkg/platform/model/vulnerabilities.go (about) 1 package model 2 3 import ( 4 "fmt" 5 "sort" 6 7 "github.com/ActiveState/cli/internal/errs" 8 "github.com/ActiveState/cli/pkg/platform/api/vulnerabilities" 9 "github.com/ActiveState/cli/pkg/platform/api/vulnerabilities/model" 10 "github.com/ActiveState/cli/pkg/platform/api/vulnerabilities/request" 11 "github.com/ActiveState/cli/pkg/platform/authentication" 12 "github.com/thoas/go-funk" 13 ) 14 15 type VulnerabilityIngredient struct { 16 Name string 17 PrimaryNamespace string 18 Version string 19 Vulnerabilities *Vulnerabilities 20 } 21 22 type Vulnerabilities struct { 23 Critical []string 24 High []string 25 Medium []string 26 Low []string 27 } 28 29 func (v Vulnerabilities) Length() int { 30 return len(v.Critical) + len(v.High) + len(v.Medium) + len(v.Low) 31 } 32 33 func (v *Vulnerabilities) Count() map[string]int { 34 return map[string]int{ 35 model.SeverityCritical: len(v.Critical), 36 model.SeverityHigh: len(v.High), 37 model.SeverityMedium: len(v.Medium), 38 model.SeverityLow: len(v.Low), 39 } 40 } 41 42 func FetchVulnerabilitiesForIngredient(auth *authentication.Auth, ingredient *request.Ingredient) (*VulnerabilityIngredient, error) { 43 vulnerabilities, err := FetchVulnerabilitiesForIngredients(auth, []*request.Ingredient{ingredient}) 44 if err != nil { 45 return nil, errs.Wrap(err, "Failed to fetch vulnerabilities") 46 } 47 48 if len(vulnerabilities) == 0 { 49 return nil, nil 50 } 51 52 if len(vulnerabilities) > 1 { 53 return nil, errs.New("Expected 1 vulnerability, got %d", len(vulnerabilities)) 54 } 55 56 return vulnerabilities[0], nil 57 } 58 59 func FetchVulnerabilitiesForIngredients(auth *authentication.Auth, ingredients []*request.Ingredient) ([]*VulnerabilityIngredient, error) { 60 requestIngredients := make([]*request.Ingredient, len(ingredients)) 61 for i, ingredient := range ingredients { 62 requestIngredients[i] = &request.Ingredient{ 63 Namespace: ingredient.Namespace, 64 Name: ingredient.Name, 65 Version: ingredient.Version, 66 } 67 } 68 69 med := vulnerabilities.New(auth) 70 71 req := request.VulnerabilitiesByIngredients(requestIngredients) 72 var resp model.VulnerabilitiesResponse 73 err := med.Run(req, &resp) 74 if err != nil { 75 return nil, errs.Wrap(err, "Failed to run vulnerabilities request") 76 } 77 78 vulnerabilities := make(map[string]*VulnerabilityIngredient) 79 for _, v := range resp.Vulnerabilities { 80 key := fmt.Sprintf("%s/%s/%s", v.PrimaryNamespace, v.Name, v.Version) 81 if _, ok := vulnerabilities[key]; !ok { 82 vulnerabilities[key] = &VulnerabilityIngredient{ 83 Name: v.Name, 84 PrimaryNamespace: v.PrimaryNamespace, 85 Version: v.Version, 86 Vulnerabilities: &Vulnerabilities{ 87 Critical: []string{}, 88 High: []string{}, 89 Medium: []string{}, 90 Low: []string{}, 91 }, 92 } 93 } 94 95 vulns := vulnerabilities[key] 96 switch v.Vulnerability.Severity { 97 case model.SeverityCritical: 98 vulns.Vulnerabilities.Critical = append(vulns.Vulnerabilities.Critical, v.Vulnerability.CVEIdentifier) 99 case model.SeverityHigh: 100 vulns.Vulnerabilities.High = append(vulns.Vulnerabilities.High, v.Vulnerability.CVEIdentifier) 101 case model.SeverityMedium: 102 vulns.Vulnerabilities.Medium = append(vulns.Vulnerabilities.Medium, v.Vulnerability.CVEIdentifier) 103 case model.SeverityLow: 104 vulns.Vulnerabilities.Low = append(vulns.Vulnerabilities.Low, v.Vulnerability.CVEIdentifier) 105 } 106 } 107 108 var result []*VulnerabilityIngredient 109 for _, v := range vulnerabilities { 110 result = append(result, v) 111 } 112 113 sort.Slice(result, func(i, j int) bool { 114 return result[i].Version < result[j].Version 115 }) 116 117 return result, nil 118 } 119 120 type IngredientName string 121 122 type VulnerableIngredientByLevel struct { 123 IngredientName string 124 IngredientVersion string 125 CVEIDs []string 126 } 127 128 type VulnerableIngredientsByLevel struct { 129 Count int 130 CountPrimary int 131 Ingredients map[IngredientName]VulnerableIngredientByLevel 132 } 133 134 type VulnerableIngredientsByLevels struct { 135 Count int 136 CountPrimary int 137 Critical VulnerableIngredientsByLevel 138 High VulnerableIngredientsByLevel 139 Medium VulnerableIngredientsByLevel 140 Low VulnerableIngredientsByLevel 141 } 142 143 func CombineVulnerabilities(ingredients []*VulnerabilityIngredient, primaryIngredients ...string) VulnerableIngredientsByLevels { 144 v := VulnerableIngredientsByLevels{ 145 Critical: VulnerableIngredientsByLevel{Ingredients: map[IngredientName]VulnerableIngredientByLevel{}}, 146 High: VulnerableIngredientsByLevel{Ingredients: map[IngredientName]VulnerableIngredientByLevel{}}, 147 Medium: VulnerableIngredientsByLevel{Ingredients: map[IngredientName]VulnerableIngredientByLevel{}}, 148 Low: VulnerableIngredientsByLevel{Ingredients: map[IngredientName]VulnerableIngredientByLevel{}}, 149 } 150 151 for _, i := range ingredients { 152 iname := IngredientName(i.Name) 153 154 if len(i.Vulnerabilities.Critical) > 0 { 155 v.Count = v.Count + len(i.Vulnerabilities.Critical) 156 v.Critical.Count = v.Critical.Count + len(i.Vulnerabilities.Critical) 157 if funk.Contains(primaryIngredients, i.Name) { 158 v.CountPrimary = v.CountPrimary + len(i.Vulnerabilities.Critical) 159 v.Critical.CountPrimary = v.Critical.CountPrimary + len(i.Vulnerabilities.Critical) 160 } 161 v.Critical.Ingredients[iname] = VulnerableIngredientByLevel{ 162 IngredientName: i.Name, 163 IngredientVersion: i.Version, 164 CVEIDs: i.Vulnerabilities.Critical, 165 } 166 } 167 168 if len(i.Vulnerabilities.High) > 0 { 169 v.Count = v.Count + len(i.Vulnerabilities.High) 170 v.High.Count = v.High.Count + len(i.Vulnerabilities.High) 171 if funk.Contains(primaryIngredients, i.Name) { 172 v.CountPrimary = v.CountPrimary + len(i.Vulnerabilities.High) 173 v.High.CountPrimary = v.High.CountPrimary + len(i.Vulnerabilities.High) 174 } 175 v.High.Ingredients[iname] = VulnerableIngredientByLevel{ 176 IngredientName: i.Name, 177 IngredientVersion: i.Version, 178 CVEIDs: i.Vulnerabilities.High, 179 } 180 } 181 182 if len(i.Vulnerabilities.Medium) > 0 { 183 v.Count = v.Count + len(i.Vulnerabilities.Medium) 184 v.Medium.Count = v.Medium.Count + len(i.Vulnerabilities.Medium) 185 if funk.Contains(primaryIngredients, i.Name) { 186 v.CountPrimary = v.CountPrimary + len(i.Vulnerabilities.Medium) 187 v.Medium.CountPrimary = v.Medium.CountPrimary + len(i.Vulnerabilities.Medium) 188 } 189 v.Medium.Ingredients[iname] = VulnerableIngredientByLevel{ 190 IngredientName: i.Name, 191 IngredientVersion: i.Version, 192 CVEIDs: i.Vulnerabilities.Medium, 193 } 194 } 195 196 if len(i.Vulnerabilities.Low) > 0 { 197 v.Count = v.Count + len(i.Vulnerabilities.Low) 198 v.Low.Count = v.Low.Count + len(i.Vulnerabilities.Low) 199 if funk.Contains(primaryIngredients, i.Name) { 200 v.CountPrimary = v.CountPrimary + len(i.Vulnerabilities.Low) 201 v.Low.CountPrimary = v.Low.CountPrimary + len(i.Vulnerabilities.Low) 202 } 203 v.Low.Ingredients[iname] = VulnerableIngredientByLevel{ 204 IngredientName: i.Name, 205 IngredientVersion: i.Version, 206 CVEIDs: i.Vulnerabilities.Low, 207 } 208 } 209 } 210 211 return v 212 }