github.com/jenkins-x/jx/v2@v2.1.155/pkg/cve/anchore.go (about) 1 package cve 2 3 import ( 4 "encoding/json" 5 "io/ioutil" 6 "net/http" 7 8 "fmt" 9 10 "github.com/jenkins-x/jx-api/pkg/client/clientset/versioned" 11 "github.com/jenkins-x/jx/v2/pkg/auth" 12 "github.com/jenkins-x/jx/v2/pkg/table" 13 "github.com/jenkins-x/jx/v2/pkg/util" 14 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 "k8s.io/client-go/kubernetes" 16 ) 17 18 const ( 19 getVulnerabilitiesByImageID = "/images/by_id/%s/vuln/%s" 20 getVulnerabilitiesByImageDigest = "/images/%s" 21 vulnerabilityType = "os" 22 GetImages = "/images" 23 ) 24 25 type result interface { 26 } 27 28 type VulnerabilityList struct { 29 ImageDigest string 30 Vulnerabilities []Vulnerability 31 } 32 33 type Vulnerability struct { 34 Fix string 35 Package string 36 Severity string 37 URL string 38 Vuln string 39 } 40 41 type Image struct { 42 AnalysisStatus string `json:"analysis_status,omitempty"` 43 ImageDetails []ImageDetail `json:"image_detail,omitempty"` 44 } 45 46 type ImageDetail struct { 47 Registry string 48 Repo string 49 Tag string 50 ImageId string 51 Fulltag string 52 } 53 54 // AnchoreProvider implements CVEProvider interface for anchore.io 55 type AnchoreProvider struct { 56 Client *http.Client 57 BasicAuth string 58 BaseURL string 59 } 60 61 func NewAnchoreProvider(server *auth.AuthServer, user *auth.UserAuth) (CVEProvider, error) { 62 63 basicAuth := util.BasicAuth(user.Username, user.Password) 64 65 provider := AnchoreProvider{ 66 BaseURL: server.URL, 67 BasicAuth: basicAuth, 68 Client: http.DefaultClient, 69 } 70 71 return &provider, nil 72 } 73 74 func (a AnchoreProvider) GetImageVulnerabilityTable(jxClient versioned.Interface, client kubernetes.Interface, table *table.Table, query CVEQuery) error { 75 76 var err error 77 var vList VulnerabilityList 78 var imageIDs []string 79 80 if query.ImageID != "" { 81 var vList VulnerabilityList 82 subPath := fmt.Sprintf(getVulnerabilitiesByImageID, query.ImageID, vulnerabilityType) 83 84 err = a.AnchoreGet(subPath, &vList) 85 if err != nil { 86 return fmt.Errorf("error getting vulnerabilities for image %s: %v", query.ImageID, err) 87 } 88 89 return a.addVulnerabilitiesTableRows(table, &vList) 90 } 91 92 if query.Environment != "" { 93 94 var vList VulnerabilityList 95 // list pods in the namespace 96 podList, err := client.CoreV1().Pods(query.TargetNamespace).List(meta_v1.ListOptions{}) 97 if err != nil { 98 return err 99 } 100 // if they have the annotation add the value to a list 101 for _, p := range podList.Items { 102 if p.Annotations[AnnotationCVEImageId] != "" { 103 imageIDs = append(imageIDs, p.Annotations[AnnotationCVEImageId]) 104 } 105 } 106 // loop over the list and get the CVEs for each, adding the rows 107 err = a.getCVEsFromImageList(table, &vList, imageIDs) 108 if err != nil { 109 return err 110 } 111 } 112 113 // see if we can match images using an image name and optional version 114 if query.ImageID == "" { 115 // if we have an image name then lets try and match image id(s) 116 if query.ImageName != "" { 117 118 var images []Image 119 subPath := fmt.Sprintf(GetImages) 120 121 err = a.AnchoreGet(subPath, &images) 122 if err != nil { 123 return fmt.Errorf("error getting images %v", err) 124 } 125 126 for _, image := range images { 127 for _, d := range image.ImageDetails { 128 if d.Repo == query.ImageName { 129 // if user has provided a version and it doesn't match lets skip this image 130 if query.Vesion != "" && query.Vesion != d.Tag { 131 continue 132 } 133 imageIDs = append(imageIDs, d.ImageId) 134 } 135 } 136 } 137 if len(imageIDs) > 0 { 138 err = a.getCVEsFromImageList(table, &vList, imageIDs) 139 if err != nil { 140 return err 141 } 142 } else { 143 return fmt.Errorf("no matching images found for ImageName %s and Vesion %s", query.ImageName, query.Vesion) 144 } 145 } 146 } else { 147 return fmt.Errorf("choose an image name, an optinal version or anchore image id to find vulnerabilities") 148 } 149 150 return nil 151 152 } 153 154 // AnchoreGet get command 155 func (a AnchoreProvider) AnchoreGet(subPath string, rs result) error { 156 157 url := fmt.Sprintf("%s%s", a.BaseURL, subPath) 158 req, err := http.NewRequest("GET", url, nil) 159 req.Header.Add("Authorization", "Basic "+a.BasicAuth) 160 161 resp, err := a.Client.Do(req) 162 if err != nil { 163 return fmt.Errorf("error getting vulnerabilities from anchore engine %v", err) 164 } 165 166 if resp.StatusCode != 200 { 167 return fmt.Errorf("error response getting vulnerabilities from anchore engine: %s", resp.Status) 168 } 169 170 data, err := ioutil.ReadAll(resp.Body) 171 err = json.Unmarshal(data, &rs) 172 if err != nil { 173 return fmt.Errorf("error unmarshalling %v", err) 174 } 175 return nil 176 } 177 178 func (a AnchoreProvider) addVulnerabilitiesTableRows(table *table.Table, vList *VulnerabilityList) error { 179 180 var image []Image 181 subPath := fmt.Sprintf(getVulnerabilitiesByImageDigest, vList.ImageDigest) 182 183 err := a.AnchoreGet(subPath, &image) 184 if err != nil { 185 return fmt.Errorf("error getting image for image digest %s: %v", vList.ImageDigest, err) 186 } 187 // TODO sort vList on severity and version? 188 189 for _, v := range vList.Vulnerabilities { 190 var sev string 191 switch v.Severity { 192 case "High": 193 sev = util.ColorError(v.Severity) 194 case "Medium": 195 sev = util.ColorWarning(v.Severity) 196 case "Low": 197 sev = util.ColorStatus(v.Severity) 198 } 199 table.AddRow(image[0].ImageDetails[0].Fulltag, sev, v.Vuln, v.URL, v.Package, v.Fix) 200 } 201 return nil 202 } 203 204 func (a AnchoreProvider) getCVEsFromImageList(table *table.Table, vList *VulnerabilityList, ids []string) error { 205 for _, imageID := range ids { 206 subPath := fmt.Sprintf(getVulnerabilitiesByImageID, imageID, vulnerabilityType) 207 208 err := a.AnchoreGet(subPath, &vList) 209 if err != nil { 210 return fmt.Errorf("error getting vulnerabilities for image %s: %v", imageID, err) 211 } 212 213 err = a.addVulnerabilitiesTableRows(table, vList) 214 if err != nil { 215 return fmt.Errorf("error building vulnerabilities table for image digest %s: %v", vList.ImageDigest, err) 216 } 217 } 218 return nil 219 }