-
Notifications
You must be signed in to change notification settings - Fork 171
/
vulns.go
127 lines (108 loc) · 3.42 KB
/
vulns.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main
import (
"context"
"errors"
"flag"
"fmt"
"os"
"github.com/genuinetools/reg/clair"
"github.com/genuinetools/reg/registry"
"github.com/sirupsen/logrus"
)
const vulnsHelp = `Get a vulnerability report for a repository from a CoreOS Clair server.`
func (cmd *vulnsCommand) Name() string { return "vulns" }
func (cmd *vulnsCommand) Args() string { return "[OPTIONS] NAME[:TAG|@DIGEST]" }
func (cmd *vulnsCommand) ShortHelp() string { return vulnsHelp }
func (cmd *vulnsCommand) LongHelp() string { return vulnsHelp }
func (cmd *vulnsCommand) Hidden() bool { return false }
func (cmd *vulnsCommand) Register(fs *flag.FlagSet) {
fs.StringVar(&cmd.clairServer, "clair", os.Getenv("CLAIR_URL"), "url to clair instance (or env var CLAIR_URL)")
fs.IntVar(&cmd.fixableThreshold, "fixable-threshhold", 0, "number of fixable issues permitted")
}
type vulnsCommand struct {
clairServer string
fixableThreshold int
}
func (cmd *vulnsCommand) Run(ctx context.Context, args []string) error {
if len(cmd.clairServer) < 1 {
return errors.New("clair url cannot be empty, pass --clair")
}
if cmd.fixableThreshold < 0 {
return errors.New("fixable threshold must be a positive integer")
}
if len(args) < 1 {
return fmt.Errorf("pass the name of the repository")
}
image, err := registry.ParseImage(args[0])
if err != nil {
return err
}
// Create the registry client.
r, err := createRegistryClient(ctx, image.Domain)
if err != nil {
return err
}
// Initialize clair client.
cr, err := clair.New(cmd.clairServer, clair.Opt{
Debug: debug,
Timeout: timeout,
Insecure: insecure,
})
if err != nil {
return fmt.Errorf("creation of clair client at %s failed: %v", cmd.clairServer, err)
}
// Get the vulnerability report.
report, err := cr.VulnerabilitiesV3(ctx, r, image.Path, image.Reference())
if err != nil {
// Fallback to Clair v2 API.
report, err = cr.Vulnerabilities(ctx, r, image.Path, image.Reference())
if err != nil {
return err
}
}
// Iterate over the vulnerabilities by severity list.
for sev, vulns := range report.VulnsBySeverity {
for _, v := range vulns {
if sev == "Fixable" {
fmt.Printf("%s: [%s] \n%s\n%s\n", v.Name, v.Severity+" - Fixable", v.Description, v.Link)
fmt.Printf("Fixed by: %s\n", v.FixedBy)
} else {
fmt.Printf("%s: [%s] \n%s\n%s\n", v.Name, v.Severity, v.Description, v.Link)
}
fmt.Println("-----------------------------------------")
}
}
if len(report.VulnsBySeverity) < 1 {
fmt.Println("No vulnerabilies found.")
return nil
}
// Print summary and count.
for sev, vulns := range report.VulnsBySeverity {
fmt.Printf("%s: %d\n", sev, len(vulns))
}
// Return an error if there are more than 1 fixable vulns.
fixable, ok := report.VulnsBySeverity["Fixable"]
if ok {
if len(fixable) > cmd.fixableThreshold {
logrus.Fatalf("%d fixable vulnerabilities found", len(fixable))
}
}
// Return an error if there are more than 10 bad vulns.
badVulns := 0
// Include any high vulns.
if highVulns, ok := report.VulnsBySeverity["High"]; ok {
badVulns += len(highVulns)
}
// Include any critical vulns.
if criticalVulns, ok := report.VulnsBySeverity["Critical"]; ok {
badVulns += len(criticalVulns)
}
// Include any defcon1 vulns.
if defcon1Vulns, ok := report.VulnsBySeverity["Defcon1"]; ok {
badVulns += len(defcon1Vulns)
}
if badVulns > 10 {
logrus.Fatalf("%d bad vulnerabilities found", badVulns)
}
return nil
}