2017-04-06 06:41:43 -04:00
|
|
|
package clair
|
|
|
|
|
|
|
|
import (
|
2018-06-11 14:34:08 -04:00
|
|
|
"errors"
|
2017-04-06 06:41:43 -04:00
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
"github.com/coreos/clair/api/v3/clairpb"
|
2018-03-11 11:48:04 -04:00
|
|
|
"github.com/genuinetools/reg/registry"
|
2017-04-06 06:41:43 -04:00
|
|
|
)
|
|
|
|
|
2018-06-06 13:22:18 -04:00
|
|
|
// Vulnerabilities scans the given repo and tag.
|
2018-03-06 09:12:29 -05:00
|
|
|
func (c *Clair) Vulnerabilities(r *registry.Registry, repo, tag string) (VulnerabilityReport, error) {
|
2017-04-06 06:41:43 -04:00
|
|
|
report := VulnerabilityReport{
|
|
|
|
RegistryURL: r.Domain,
|
|
|
|
Repo: repo,
|
|
|
|
Tag: tag,
|
|
|
|
Date: time.Now().Local().Format(time.RFC1123),
|
|
|
|
VulnsBySeverity: make(map[string][]Vulnerability),
|
|
|
|
}
|
|
|
|
|
2018-06-06 13:34:09 -04:00
|
|
|
filteredLayers, err := c.getFilteredLayers(r, repo, tag)
|
2018-03-06 09:12:29 -05:00
|
|
|
if err != nil {
|
2018-06-06 13:34:09 -04:00
|
|
|
return report, fmt.Errorf("getting filtered layers failed: %v", err)
|
2018-03-06 09:12:29 -05:00
|
|
|
}
|
|
|
|
|
2018-06-06 13:22:18 -04:00
|
|
|
if len(filteredLayers) == 0 {
|
2017-04-06 06:41:43 -04:00
|
|
|
fmt.Printf("No need to analyse image %s:%s as there is no non-emtpy layer", repo, tag)
|
|
|
|
return report, nil
|
|
|
|
}
|
|
|
|
|
2018-06-06 13:22:18 -04:00
|
|
|
for i := len(filteredLayers) - 1; i >= 0; i-- {
|
2018-03-06 09:12:29 -05:00
|
|
|
// Form the clair layer.
|
2018-06-11 13:32:59 -04:00
|
|
|
l, err := c.NewClairLayer(r, repo, filteredLayers, i)
|
2017-04-06 06:41:43 -04:00
|
|
|
if err != nil {
|
|
|
|
return report, err
|
|
|
|
}
|
|
|
|
|
2018-03-06 09:12:29 -05:00
|
|
|
// Post the layer.
|
2017-04-06 06:41:43 -04:00
|
|
|
if _, err := c.PostLayer(l); err != nil {
|
|
|
|
return report, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
vl, err := c.GetLayer(filteredLayers[0].Digest.String(), true, true)
|
2017-04-06 06:41:43 -04:00
|
|
|
if err != nil {
|
|
|
|
return report, err
|
|
|
|
}
|
|
|
|
|
2018-03-06 09:12:29 -05:00
|
|
|
// Get the vulns.
|
2017-04-06 06:41:43 -04:00
|
|
|
for _, f := range vl.Features {
|
2018-03-06 09:19:55 -05:00
|
|
|
report.Vulns = append(report.Vulns, f.Vulnerabilities...)
|
2017-04-06 06:41:43 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
vulnsBy := func(sev string, store map[string][]Vulnerability) []Vulnerability {
|
|
|
|
items, found := store[sev]
|
|
|
|
if !found {
|
|
|
|
items = make([]Vulnerability, 0)
|
|
|
|
store[sev] = items
|
|
|
|
}
|
|
|
|
return items
|
|
|
|
}
|
|
|
|
|
|
|
|
// group by severity
|
|
|
|
for _, v := range report.Vulns {
|
|
|
|
sevRow := vulnsBy(v.Severity, report.VulnsBySeverity)
|
|
|
|
report.VulnsBySeverity[v.Severity] = append(sevRow, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
// calculate number of bad vulns
|
|
|
|
report.BadVulns = len(report.VulnsBySeverity["High"]) + len(report.VulnsBySeverity["Critical"]) + len(report.VulnsBySeverity["Defcon1"])
|
|
|
|
|
|
|
|
return report, nil
|
|
|
|
}
|
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
// VulnerabilitiesV3 scans the given repo and tag using the clair v3 API.
|
|
|
|
func (c *Clair) VulnerabilitiesV3(r *registry.Registry, repo, tag string) (VulnerabilityReport, error) {
|
|
|
|
report := VulnerabilityReport{
|
|
|
|
RegistryURL: r.Domain,
|
|
|
|
Repo: repo,
|
|
|
|
Tag: tag,
|
|
|
|
Date: time.Now().Local().Format(time.RFC1123),
|
|
|
|
VulnsBySeverity: make(map[string][]Vulnerability),
|
2017-04-06 06:41:43 -04:00
|
|
|
}
|
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
filteredLayers, err := c.getFilteredLayers(r, repo, tag)
|
2017-04-06 06:41:43 -04:00
|
|
|
if err != nil {
|
2018-06-11 13:32:59 -04:00
|
|
|
return report, fmt.Errorf("getting filtered layers failed: %v", err)
|
2017-04-06 06:41:43 -04:00
|
|
|
}
|
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
if len(filteredLayers) == 0 {
|
|
|
|
fmt.Printf("No need to analyse image %s:%s as there is no non-emtpy layer", repo, tag)
|
|
|
|
return report, nil
|
2017-04-06 06:41:43 -04:00
|
|
|
}
|
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
clairLayers := []*clairpb.PostAncestryRequest_PostLayer{}
|
|
|
|
for i := len(filteredLayers) - 1; i >= 0; i-- {
|
|
|
|
// Form the clair layer.
|
|
|
|
l, err := c.NewClairV3Layer(r, repo, filteredLayers[i])
|
|
|
|
if err != nil {
|
|
|
|
return report, err
|
2017-06-19 11:14:53 -04:00
|
|
|
}
|
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
// Append the layer.
|
|
|
|
clairLayers = append(clairLayers, l)
|
2017-08-14 06:34:56 -04:00
|
|
|
}
|
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
// Post the ancestry.
|
|
|
|
if err := c.PostAncestry(filteredLayers[0].Digest.String(), clairLayers); err != nil {
|
|
|
|
return report, fmt.Errorf("posting ancestry failed: %v", err)
|
2017-08-14 06:34:56 -04:00
|
|
|
}
|
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
// Get the ancestry.
|
|
|
|
vl, err := c.GetAncestry(filteredLayers[0].Digest.String(), true, true)
|
|
|
|
if err != nil {
|
|
|
|
return report, err
|
2017-08-14 06:34:56 -04:00
|
|
|
}
|
|
|
|
|
2018-06-11 14:34:08 -04:00
|
|
|
if vl == nil {
|
|
|
|
return report, errors.New("ancestry response was nil")
|
|
|
|
}
|
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
// Get the vulns.
|
|
|
|
for _, f := range vl.Features {
|
|
|
|
for _, v := range f.Vulnerabilities {
|
|
|
|
report.Vulns = append(report.Vulns, Vulnerability{
|
|
|
|
Name: v.Name,
|
|
|
|
NamespaceName: v.NamespaceName,
|
|
|
|
Description: v.Description,
|
|
|
|
Link: v.Link,
|
|
|
|
Severity: v.Severity,
|
|
|
|
Metadata: map[string]interface{}{v.Metadata: ""},
|
|
|
|
FixedBy: v.FixedBy,
|
|
|
|
})
|
2017-08-14 06:34:56 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
vulnsBy := func(sev string, store map[string][]Vulnerability) []Vulnerability {
|
|
|
|
items, found := store[sev]
|
|
|
|
if !found {
|
|
|
|
items = make([]Vulnerability, 0)
|
|
|
|
store[sev] = items
|
2018-06-06 13:22:18 -04:00
|
|
|
}
|
2018-06-11 13:32:59 -04:00
|
|
|
return items
|
2018-06-06 13:22:18 -04:00
|
|
|
}
|
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
// Group by severity.
|
|
|
|
for _, v := range report.Vulns {
|
|
|
|
sevRow := vulnsBy(v.Severity, report.VulnsBySeverity)
|
|
|
|
report.VulnsBySeverity[v.Severity] = append(sevRow, v)
|
2018-06-06 13:22:18 -04:00
|
|
|
}
|
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
// calculate number of bad vulns
|
|
|
|
report.BadVulns = len(report.VulnsBySeverity["High"]) + len(report.VulnsBySeverity["Critical"]) + len(report.VulnsBySeverity["Defcon1"])
|
2018-06-06 13:22:18 -04:00
|
|
|
|
2018-06-11 13:32:59 -04:00
|
|
|
return report, nil
|
2018-06-06 13:22:18 -04:00
|
|
|
}
|