Skip to content

Commit

Permalink
Colorize severity in table output
Browse files Browse the repository at this point in the history
- Create flag "--no-color" to allow disabling the color. By default its enabled.
- When "--no-color" not specified highlight severity in its color:
  - Critical -> Bold Red
  - High -> Red
  - Medium -> Yellow
  - Low -> Green
  - Negligible -> Blue
  - Note: Golang doesn't have all colors available. Also, doesn't seem to be able use hex codes properly.

Closes anchore#225

Signed-off-by: Shane Dell <shanedell100@gmail.com>
  • Loading branch information
shanedell committed May 9, 2023
1 parent 0ace6b1 commit b29efc8
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 9 deletions.
3 changes: 2 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func init() {
func setGlobalCliOptions() {
// setup global CLI options (available on all CLI commands)
rootCmd.PersistentFlags().StringVarP(&persistentOpts.ConfigPath, "config", "c", "", "application config file")
rootCmd.PersistentFlags().BoolVar(&persistentOpts.NoColor, "no-color", false, "disable color for table output")

flag := "quiet"
rootCmd.PersistentFlags().BoolP(
Expand Down Expand Up @@ -392,7 +393,7 @@ func startWorker(userInput string, failOnSeverity *vulnerability.Severity) <-cha

bus.Publish(partybus.Event{
Type: event.VulnerabilityScanningFinished,
Value: presenter.GetPresenter(presenterConfig, pb),
Value: presenter.GetPresenter(presenterConfig, pb, persistentOpts.NoColor),
})
}()
return errs
Expand Down
6 changes: 3 additions & 3 deletions grype/presenter/presenter.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ type Presenter interface {

// GetPresenter retrieves a Presenter that matches a CLI option
// TODO dependency cycle with presenter package to sub formats
func GetPresenter(c Config, pb models.PresenterConfig) Presenter {
func GetPresenter(c Config, pb models.PresenterConfig, tableNoColor bool) Presenter {
switch c.format {
case jsonFormat:
return json.NewPresenter(pb)
case tableFormat:
if c.showSuppressed {
return table.NewPresenter(pb)
return table.NewPresenter(pb, tableNoColor)
}
return table.NewPresenter(pb)
return table.NewPresenter(pb, tableNoColor)

// NOTE: cyclonedx is identical to embeddedVEXJSON
// The cyclonedx library only provides two BOM formats: JSON and XML
Expand Down
34 changes: 32 additions & 2 deletions grype/presenter/table/presenter.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,17 @@ type Presenter struct {
ignoredMatches []match.IgnoredMatch
packages []pkg.Package
metadataProvider vulnerability.MetadataProvider
noColor bool
}

// NewPresenter is a *Presenter constructor
func NewPresenter(pb models.PresenterConfig) *Presenter {
func NewPresenter(pb models.PresenterConfig, noColor bool) *Presenter {
return &Presenter{
results: pb.Matches,
ignoredMatches: pb.IgnoredMatches,
packages: pb.Packages,
metadataProvider: pb.MetadataProvider,
noColor: noColor,
}
}

Expand Down Expand Up @@ -94,12 +96,40 @@ func (pres *Presenter) Present(output io.Writer) error {
table.SetTablePadding(" ")
table.SetNoWhiteSpace(true)

table.AppendBulk(rows)
if !pres.noColor {
for _, row := range rows {
severityColor := getSeverityColor(row[len(row)-1])
table.Rich(row, []tablewriter.Colors{{}, {}, {}, {}, {}, severityColor})
}
} else {
table.AppendBulk(rows)
}

table.Render()

return nil
}

func getSeverityColor(severity string) tablewriter.Colors {
severityFontType, severityColor := tablewriter.Normal, tablewriter.Normal

switch strings.ToLower(severity) {
case "critical":
severityFontType = tablewriter.Bold
severityColor = tablewriter.FgRedColor
case "high":
severityColor = tablewriter.FgRedColor
case "medium":
severityColor = tablewriter.FgYellowColor
case "low":
severityColor = tablewriter.FgGreenColor
case "negligible":
severityColor = tablewriter.FgBlueColor
}

return tablewriter.Colors{severityFontType, severityColor}
}

func removeDuplicateRows(items [][]string) [][]string {
seen := map[string][]string{}
var result [][]string
Expand Down
41 changes: 38 additions & 3 deletions grype/presenter/table/presenter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func TestCreateRow(t *testing.T) {
}
}

func TestTablePresenter(t *testing.T) {
func TestTablePresenter_Color(t *testing.T) {

var buffer bytes.Buffer
matches, packages, _, metadataProvider, _, _ := models.GenerateAnalysis(t, source.ImageScheme)
Expand All @@ -84,7 +84,42 @@ func TestTablePresenter(t *testing.T) {
MetadataProvider: metadataProvider,
}

pres := NewPresenter(pb)
pres := NewPresenter(pb, false)

// run presenter
err := pres.Present(&buffer)
if err != nil {
t.Fatal(err)
}
actual := buffer.Bytes()
if *update {
testutils.UpdateGoldenFileContents(t, actual)
}

var expected = testutils.GetGoldenFileContents(t)

if !bytes.Equal(expected, actual) {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(string(expected), string(actual), true)
t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs))
}

// TODO: add me back in when there is a JSON schema
// validateAgainstDbSchema(t, string(actual))
}

func TestTablePresenter_NoColor(t *testing.T) {

var buffer bytes.Buffer
matches, packages, _, metadataProvider, _, _ := models.GenerateAnalysis(t, source.ImageScheme)

pb := models.PresenterConfig{
Matches: matches,
Packages: packages,
MetadataProvider: metadataProvider,
}

pres := NewPresenter(pb, true)

// run presenter
err := pres.Present(&buffer)
Expand Down Expand Up @@ -121,7 +156,7 @@ func TestEmptyTablePresenter(t *testing.T) {
MetadataProvider: nil,
}

pres := NewPresenter(pb)
pres := NewPresenter(pb, true)

// run presenter
err := pres.Present(&buffer)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
package-1 1.1.1 the-next-version rpm CVE-1999-0001 Low
package-2 2.2.2 deb CVE-1999-0002 Critical
1 change: 1 addition & 0 deletions internal/config/cli_only_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ package config

type CliOnlyOptions struct {
ConfigPath string
NoColor bool
Verbosity int
}

0 comments on commit b29efc8

Please sign in to comment.