-
Notifications
You must be signed in to change notification settings - Fork 551
/
presenter.go
115 lines (94 loc) · 2.74 KB
/
presenter.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
package table
import (
"fmt"
"io"
"sort"
"strings"
"github.com/olekukonko/tablewriter"
grypeDb "github.com/anchore/grype/grype/db/v3"
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/vulnerability"
)
// Presenter is a generic struct for holding fields needed for reporting
type Presenter struct {
results match.Matches
packages []pkg.Package
metadataProvider vulnerability.MetadataProvider
}
// NewPresenter is a *Presenter constructor
func NewPresenter(results match.Matches, packages []pkg.Package, metadataProvider vulnerability.MetadataProvider) *Presenter {
return &Presenter{
results: results,
packages: packages,
metadataProvider: metadataProvider,
}
}
// Present creates a JSON-based reporting
func (pres *Presenter) Present(output io.Writer) error {
rows := make([][]string, 0)
columns := []string{"Name", "Installed", "Fixed-In", "Vulnerability", "Severity"}
for m := range pres.results.Enumerate() {
var severity string
metadata, err := pres.metadataProvider.GetMetadata(m.Vulnerability.ID, m.Vulnerability.Namespace)
if err != nil {
return fmt.Errorf("unable to fetch vuln=%q metadata: %+v", m.Vulnerability.ID, err)
}
if metadata != nil {
severity = metadata.Severity
}
fixVersion := strings.Join(m.Vulnerability.Fix.Versions, ", ")
switch m.Vulnerability.Fix.State {
case grypeDb.WontFixState:
fixVersion = "(won't fix)"
case grypeDb.UnknownFixState:
fixVersion = ""
}
rows = append(rows, []string{m.Package.Name, m.Package.Version, fixVersion, m.Vulnerability.ID, severity})
}
if len(rows) == 0 {
_, err := io.WriteString(output, "No vulnerabilities found\n")
return err
}
// sort by name, version, then type
sort.SliceStable(rows, func(i, j int) bool {
for col := 0; col < len(columns); col++ {
if rows[i][0] != rows[j][0] {
return rows[i][col] < rows[j][col]
}
}
return false
})
rows = removeDuplicateRows(rows)
table := tablewriter.NewWriter(output)
table.SetHeader(columns)
table.SetAutoWrapText(false)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetHeaderLine(false)
table.SetBorder(false)
table.SetAutoFormatHeaders(true)
table.SetCenterSeparator("")
table.SetColumnSeparator("")
table.SetRowSeparator("")
table.SetTablePadding(" ")
table.SetNoWhiteSpace(true)
table.AppendBulk(rows)
table.Render()
return nil
}
func removeDuplicateRows(items [][]string) [][]string {
seen := map[string][]string{}
// nolint:prealloc
var result [][]string
for _, v := range items {
key := strings.Join(v, "|")
if seen[key] != nil {
// dup!
continue
}
seen[key] = v
result = append(result, v)
}
return result
}