// getVulnerabilitiesFromLayerPackagesNodes returns the list of vulnerabilities // affecting the provided package nodes, filtered by Priority. func getVulnerabilitiesFromLayerPackagesNodes(packagesNodes []string, minimumPriority types.Priority, selectedFields []string) ([]*database.Vulnerability, error) { if len(packagesNodes) == 0 { return []*database.Vulnerability{}, nil } // Get successors of the packages. packagesNextVersions, err := getSuccessorsFromPackagesNodes(packagesNodes) if err != nil { return []*database.Vulnerability{}, err } if len(packagesNextVersions) == 0 { return []*database.Vulnerability{}, nil } // Find vulnerabilities fixed in these successors. vulnerabilities, err := database.FindAllVulnerabilitiesByFixedIn(packagesNextVersions, selectedFields) if err != nil { return []*database.Vulnerability{}, err } // Filter vulnerabilities depending on their priority and remove duplicates. filteredVulnerabilities := []*database.Vulnerability{} seen := map[string]struct{}{} for _, v := range vulnerabilities { if minimumPriority.Compare(v.Priority) <= 0 { if _, alreadySeen := seen[v.ID]; !alreadySeen { filteredVulnerabilities = append(filteredVulnerabilities, v) seen[v.ID] = struct{}{} } } } return filteredVulnerabilities, nil }
func AnalyzeLocalImage(imageName string, minSeverity types.Priority, endpoint, myAddress, tmpPath string) error { // Save image. log.Printf("Saving %s to local disk (this may take some time)", imageName) err := save(imageName, tmpPath) if err != nil { return fmt.Errorf("Could not save image: %s", err) } // Retrieve history. log.Println("Retrieving image history") layerIDs, err := historyFromManifest(tmpPath) if err != nil { layerIDs, err = historyFromCommand(imageName) } if err != nil || len(layerIDs) == 0 { return fmt.Errorf("Could not get image's history: %s", err) } // Setup a simple HTTP server if Clair is not local. if !strings.Contains(endpoint, "127.0.0.1") && !strings.Contains(endpoint, "localhost") { allowedHost := strings.TrimPrefix(endpoint, "http://") portIndex := strings.Index(allowedHost, ":") if portIndex >= 0 { allowedHost = allowedHost[:portIndex] } log.Printf("Setting up HTTP server (allowing: %s)\n", allowedHost) ch := make(chan error) go listenHTTP(tmpPath, allowedHost, ch) select { case err := <-ch: return fmt.Errorf("An error occured when starting HTTP server: %s", err) case <-time.After(100 * time.Millisecond): break } tmpPath = "http://" + myAddress + ":" + strconv.Itoa(httpPort) } // Analyze layers. log.Printf("Analyzing %d layers... \n", len(layerIDs)) for i := 0; i < len(layerIDs); i++ { log.Printf("Analyzing %s\n", layerIDs[i]) if i > 0 { err = analyzeLayer(endpoint, tmpPath+"/"+layerIDs[i]+"/layer.tar", layerIDs[i], layerIDs[i-1]) } else { err = analyzeLayer(endpoint, tmpPath+"/"+layerIDs[i]+"/layer.tar", layerIDs[i], "") } if err != nil { return fmt.Errorf("Could not analyze layer: %s", err) } } // Get vulnerabilities. log.Println("Retrieving image's vulnerabilities") layer, err := getLayer(endpoint, layerIDs[len(layerIDs)-1]) if err != nil { return fmt.Errorf("Could not get layer information: %s", err) } // Print report. fmt.Printf("Clair report for image %s (%s)\n", imageName, time.Now().UTC()) if len(layer.Features) == 0 { fmt.Printf("%s No features have been detected in the image. This usually means that the image isn't supported by Clair.\n", color.YellowString("NOTE:")) return nil } isSafe := true hasVisibleVulnerabilities := false var vulnerabilities = make([]vulnerabilityInfo, 0) for _, feature := range layer.Features { if len(feature.Vulnerabilities) > 0 { for _, vulnerability := range feature.Vulnerabilities { severity := types.Priority(vulnerability.Severity) isSafe = false if minSeverity.Compare(severity) > 0 { continue } hasVisibleVulnerabilities = true vulnerabilities = append(vulnerabilities, vulnerabilityInfo{vulnerability, feature, severity}) } } } // Sort vulnerabilitiy by severity. priority := func(v1, v2 vulnerabilityInfo) bool { return v1.severity.Compare(v2.severity) >= 0 } By(priority).Sort(vulnerabilities) for _, vulnerabilityInfo := range vulnerabilities { vulnerability := vulnerabilityInfo.vulnerability feature := vulnerabilityInfo.feature severity := vulnerabilityInfo.severity fmt.Printf("%s (%s)\n", vulnerability.Name, coloredSeverity(severity)) if vulnerability.Description != "" { fmt.Printf("%s\n\n", text.Indent(text.Wrap(vulnerability.Description, 80), "\t")) } fmt.Printf("\tPackage: %s @ %s\n", feature.Name, feature.Version) if vulnerability.FixedBy != "" { fmt.Printf("\tFixed version: %s\n", vulnerability.FixedBy) } if vulnerability.Link != "" { fmt.Printf("\tLink: %s\n", vulnerability.Link) } fmt.Printf("\tLayer: %s\n", feature.AddedBy) fmt.Println("") } if isSafe { fmt.Printf("%s No vulnerabilities were detected in your image\n", color.GreenString("Success!")) } else if !hasVisibleVulnerabilities { fmt.Printf("%s No vulnerabilities matching the minimum severity level were detected in your image\n", color.YellowString("NOTE:")) } return nil }