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  }