Skip to content

Commit

Permalink
feat: update table sort to be name, version, type, severity, vulnerab…
Browse files Browse the repository at this point in the history
…ility (#1400)

* feat: update table sort to be name, version, type, severity, vuln
---------
Signed-off-by: Christopher Phillips <[email protected]>
  • Loading branch information
spiffcs authored Jul 26, 2023
1 parent 5ee6bf4 commit eb6c3b0
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 14 deletions.
65 changes: 53 additions & 12 deletions grype/presenter/table/presenter.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ func (pres *Presenter) Present(output io.Writer) error {
// Generate rows for matching vulnerabilities
for m := range pres.results.Enumerate() {
row, err := createRow(m, pres.metadataProvider, "")

if err != nil {
return err
}
Expand All @@ -71,19 +70,9 @@ func (pres *Presenter) Present(output io.Writer) error {
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][col] != rows[j][col] {
return rows[i][col] < rows[j][col]
}
}
return false
})
rows = removeDuplicateRows(rows)
rows = sortRows(removeDuplicateRows(rows))

table := tablewriter.NewWriter(output)

table.SetHeader(columns)
table.SetAutoWrapText(false)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
Expand All @@ -104,6 +93,58 @@ func (pres *Presenter) Present(output io.Writer) error {
return nil
}

func sortRows(rows [][]string) [][]string {
// sort
sort.SliceStable(rows, func(i, j int) bool {
var (
name = 0
ver = 1
packageType = 3
vuln = 4
sev = 5
)
// name, version, type, severity, vulnerability
// > is for numeric sorting like severity or year/number of vulnerability
// < is for alphabetical sorting like name, version, type
if rows[i][name] == rows[j][name] {
if rows[i][ver] == rows[j][ver] {
if rows[i][packageType] == rows[j][packageType] {
if sevScore(rows[i][sev]) == sevScore(rows[j][sev]) {
// we use > here to get the most recently filed vulnerabilities
// to show at the top of the severity
return rows[i][vuln] > rows[j][vuln]
}
return sevScore(rows[i][sev]) > sevScore(rows[j][sev])
}
return rows[i][packageType] < rows[j][packageType]
}
return rows[i][ver] < rows[j][ver]
}
return rows[i][name] < rows[j][name]
})

return rows
}

func sevScore(sev string) int {
switch sev {
case "Unknown":
return 0
case "Negligible":
return 1
case "Low":
return 2
case "Medium":
return 3
case "High":
return 4
case "Critical":
return 5
default:
return 0
}
}

func removeDuplicateRows(items [][]string) [][]string {
seen := map[string][]string{}
var result [][]string
Expand Down
31 changes: 30 additions & 1 deletion grype/presenter/table/presenter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"

"github.com/go-test/deep"
"github.com/google/go-cmp/cmp"
"github.com/sergi/go-diff/diffmatchpatch"
"github.com/stretchr/testify/assert"

Expand Down Expand Up @@ -74,7 +75,6 @@ func TestCreateRow(t *testing.T) {
}

func TestTablePresenter(t *testing.T) {

var buffer bytes.Buffer
matches, packages, _, metadataProvider, _, _ := internal.GenerateAnalysis(t, internal.ImageSource)

Expand Down Expand Up @@ -169,7 +169,36 @@ func TestRemoveDuplicateRows(t *testing.T) {
t.Errorf(" diff: %+v", d)
}
}
}

func TestSortRows(t *testing.T) {
data := [][]string{
{"a", "v0.1.0", "", "deb", "CVE-2019-9996", "Critical"},
{"a", "v0.1.0", "", "deb", "CVE-2018-9996", "Critical"},
{"a", "v0.2.0", "", "deb", "CVE-2010-9996", "High"},
{"b", "v0.2.0", "", "deb", "CVE-2010-9996", "Medium"},
{"b", "v0.2.0", "", "deb", "CVE-2019-9996", "High"},
{"d", "v0.4.0", "", "node", "CVE-2011-9996", "Low"},
{"d", "v0.4.0", "", "node", "CVE-2012-9996", "Negligible"},
{"c", "v0.6.0", "", "node", "CVE-2013-9996", "Critical"},
}

expected := [][]string{
{"a", "v0.1.0", "", "deb", "CVE-2019-9996", "Critical"},
{"a", "v0.1.0", "", "deb", "CVE-2018-9996", "Critical"},
{"a", "v0.2.0", "", "deb", "CVE-2010-9996", "High"},
{"b", "v0.2.0", "", "deb", "CVE-2019-9996", "High"},
{"b", "v0.2.0", "", "deb", "CVE-2010-9996", "Medium"},
{"c", "v0.6.0", "", "node", "CVE-2013-9996", "Critical"},
{"d", "v0.4.0", "", "node", "CVE-2011-9996", "Low"},
{"d", "v0.4.0", "", "node", "CVE-2012-9996", "Negligible"},
}

actual := sortRows(data)

if diff := cmp.Diff(expected, actual); diff != "" {
t.Errorf("sortRows() mismatch (-want +got):\n%s", diff)
}
}

func TestHidesIgnoredMatches(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
package-1 1.1.1 rpm CVE-1999-0002 Critical
package-1 1.1.1 the-next-version rpm CVE-1999-0001 Low
package-2 2.2.2 deb CVE-1999-0001 Low (suppressed)
package-2 2.2.2 deb CVE-1999-0002 Critical (suppressed)
package-2 2.2.2 deb CVE-1999-0001 Low (suppressed)

0 comments on commit eb6c3b0

Please sign in to comment.