Skip to content

Add option to include severity in table output #409

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions cmd/osv-scanner/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func run(args []string, stdout, stderr io.Writer) int {

cli.VersionPrinter = func(ctx *cli.Context) {
// Use the app Writer and ErrWriter since they will be the writers to keep parallel tests consistent
r = reporter.NewTableReporter(ctx.App.Writer, ctx.App.ErrWriter, false)
r = reporter.NewTableReporter(ctx.App.Writer, ctx.App.ErrWriter, false, false)
r.PrintText(fmt.Sprintf("osv-scanner version: %s\ncommit: %s\nbuilt at: %s\n", ctx.App.Version, commit, date))
}

Expand Down Expand Up @@ -104,6 +104,11 @@ func run(args []string, stdout, stderr io.Writer) int {
Usage: "also scan files that would be ignored by .gitignore",
Value: false,
},
&cli.BoolFlag{
Name: "include-severity",
Usage: "include severity column in table format",
Value: false,
},
},
ArgsUsage: "[directory1 directory2...]",
Action: func(context *cli.Context) error {
Expand All @@ -117,9 +122,9 @@ func run(args []string, stdout, stderr io.Writer) int {
case "json":
r = reporter.NewJSONReporter(stdout, stderr)
case "table":
r = reporter.NewTableReporter(stdout, stderr, false)
r = reporter.NewTableReporter(stdout, stderr, false, context.Bool("include-severity"))
case "markdown":
r = reporter.NewTableReporter(stdout, stderr, true)
r = reporter.NewTableReporter(stdout, stderr, true, context.Bool("include-severity"))
default:
return fmt.Errorf("%v is not a valid format", format)
}
Expand Down Expand Up @@ -153,7 +158,7 @@ func run(args []string, stdout, stderr io.Writer) int {

if err := app.Run(args); err != nil {
if r == nil {
r = reporter.NewTableReporter(stdout, stderr, false)
r = reporter.NewTableReporter(stdout, stderr, false, false)
}
if errors.Is(err, osvscanner.VulnerabilitiesFoundErr) {
return 1
Expand Down
23 changes: 23 additions & 0 deletions cmd/osv-scanner/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,29 @@ func TestRun(t *testing.T) {
`,
wantStderr: "",
},
// one specific supported sbom with vulns and severity output
{
name: "",
args: []string{"", "--config=./fixtures/osv-scanner-empty-config.toml", "--include-severity", "./fixtures/sbom-insecure/postgres-stretch.cdx.xml"},
wantExitCode: 1,
wantStdout: `
Scanning dir ./fixtures/sbom-insecure/postgres-stretch.cdx.xml
Scanned %%/fixtures/sbom-insecure/postgres-stretch.cdx.xml as CycloneDX SBOM and found 136 packages
+-------------------------------------+----------+-----------+---------+------------------------------------+-------------------------------------------------+
| OSV URL (ID IN BOLD) | SEVERITY | ECOSYSTEM | PACKAGE | VERSION | SOURCE |
+-------------------------------------+----------+-----------+---------+------------------------------------+-------------------------------------------------+
| https://osv.dev/GHSA-v95c-p5hm-xq8f | 6 | Go | runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
| https://osv.dev/GO-2022-0274 | | | | | |
| https://osv.dev/GHSA-f3fp-gc8g-vw66 | 5.9 | Go | runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
| https://osv.dev/GHSA-g2j6-57v7-gm8c | 6.1 | Go | runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
| https://osv.dev/GHSA-m8cg-xc2p-r3fc | 2.5 | Go | runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
| https://osv.dev/GHSA-vpvm-3wq2-2wvm | 7 | Go | runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
| https://osv.dev/GHSA-p782-xgp4-8hr8 | 5.3 | Go | sys | v0.0.0-20210817142637-7d9622a276b7 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
| https://osv.dev/GO-2022-0493 | | | | | |
+-------------------------------------+----------+-----------+---------+------------------------------------+-------------------------------------------------+
`,
wantStderr: "",
},
// one specific unsupported lockfile
{
name: "",
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/CycloneDX/cyclonedx-go v0.7.1
github.com/go-git/go-billy/v5 v5.4.1
github.com/go-git/go-git/v5 v5.7.0
github.com/goark/go-cvss v1.6.6
github.com/google/go-cmp v0.5.9
github.com/jedib0t/go-pretty/v6 v6.4.6
github.com/kr/pretty v0.3.1
Expand All @@ -31,6 +32,7 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/goark/errs v1.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw4
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE=
github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8=
github.com/goark/errs v1.1.0 h1:FKnyw4LVyRADIjM8Nj0Up6r0/y5cfADvZAd1E+tthXE=
github.com/goark/errs v1.1.0/go.mod h1:TtaPEoadm2mzqzfXdkkfpN2xuniCFm2q4JH+c1qzaqw=
github.com/goark/go-cvss v1.6.6 h1:WJFuIWqmAw1Ilb9USv0vuX+nYzOWJp8lIujseJ/y3sU=
github.com/goark/go-cvss v1.6.6/go.mod h1:H3qbfUSUlV7XtA3EwWNunvXz6OySwWHOuO+R6ZPMQPI=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
Expand Down
11 changes: 8 additions & 3 deletions internal/output/markdowntable.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ import (
)

// PrintTableResults prints the osv scan results into a human friendly table.
func PrintMarkdownTableResults(vulnResult *models.VulnerabilityResults, outputWriter io.Writer) {
func PrintMarkdownTableResults(vulnResult *models.VulnerabilityResults, outputWriter io.Writer, includeSeverity bool) {
outputTable := table.NewWriter()
outputTable.SetOutputMirror(outputWriter)
outputTable.AppendHeader(table.Row{"OSV URL", "Ecosystem", "Package", "Version", "Source"})
row := table.Row{"OSV URL"}
if includeSeverity {
row = append(row, "Severity")
}
row = append(row, "Ecosystem", "Package", "Version", "Source")
outputTable.AppendHeader(row)

outputTable = tableBuilder(outputTable, vulnResult, false)
outputTable = tableBuilder(outputTable, vulnResult, false, includeSeverity)

if outputTable.Length() == 0 {
return
Expand Down
55 changes: 48 additions & 7 deletions internal/output/table.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package output

import (
"fmt"
"io"
"os"
"path/filepath"
"strings"

v2_metric "github.com/goark/go-cvss/v2/metric"
v3_metric "github.com/goark/go-cvss/v3/metric"

"github.com/google/osv-scanner/pkg/models"
"github.com/google/osv-scanner/pkg/osv"

Expand All @@ -15,10 +19,15 @@ import (
)

// PrintTableResults prints the osv scan results into a human friendly table.
func PrintTableResults(vulnResult *models.VulnerabilityResults, outputWriter io.Writer) {
func PrintTableResults(vulnResult *models.VulnerabilityResults, outputWriter io.Writer, includeSeverity bool) {
outputTable := table.NewWriter()
outputTable.SetOutputMirror(outputWriter)
outputTable.AppendHeader(table.Row{"OSV URL (ID In Bold)", "Ecosystem", "Package", "Version", "Source"})
row := table.Row{"OSV URL (ID In Bold)"}
if includeSeverity {
row = append(row, "Severity")
}
row = append(row, "Ecosystem", "Package", "Version", "Source")
outputTable.AppendHeader(row)
width, _, err := term.GetSize(int(os.Stdout.Fd()))
isTerminal := false
if err == nil { // If output is a terminal, set max length to width and add styling
Expand All @@ -30,21 +39,21 @@ func PrintTableResults(vulnResult *models.VulnerabilityResults, outputWriter io.
isTerminal = true
} // Otherwise use default ascii (e.g. getting piped to a file)

outputTable = tableBuilder(outputTable, vulnResult, isTerminal)
outputTable = tableBuilder(outputTable, vulnResult, isTerminal, includeSeverity)

if outputTable.Length() == 0 {
return
}
outputTable.Render()
}

func tableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResults, addStyling bool) table.Writer {
rows := tableBuilderInner(vulnResult, addStyling, true)
func tableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResults, addStyling bool, includeSeverity bool) table.Writer {
rows := tableBuilderInner(vulnResult, addStyling, includeSeverity, true)
for _, elem := range rows {
outputTable.AppendRow(elem.row, table.RowConfig{AutoMerge: elem.shouldMerge})
}

uncalledRows := tableBuilderInner(vulnResult, addStyling, false)
uncalledRows := tableBuilderInner(vulnResult, addStyling, includeSeverity, false)
if len(uncalledRows) == 0 {
return outputTable
}
Expand All @@ -65,7 +74,7 @@ type tbInnerResponse struct {
shouldMerge bool
}

func tableBuilderInner(vulnResult *models.VulnerabilityResults, addStyling bool, calledVulns bool) []tbInnerResponse {
func tableBuilderInner(vulnResult *models.VulnerabilityResults, addStyling bool, includeSeverity bool, calledVulns bool) []tbInnerResponse {
allOutputRows := []tbInnerResponse{}
// Working directory used to simplify path
workingDir, workingDirErr := os.Getwd()
Expand Down Expand Up @@ -100,6 +109,38 @@ func tableBuilderInner(vulnResult *models.VulnerabilityResults, addStyling bool,

outputRow = append(outputRow, strings.Join(links, "\n"))

if includeSeverity {
var outputSeverities []string
for _, vulnID := range group.IDs {
var severities []models.Severity
for _, vuln := range pkg.Vulnerabilities {
if vuln.ID == vulnID {
severities = vuln.Severity
}
}
for i, severity := range severities {
if i != 0 {
outputSeverities = append(outputSeverities, ", ")
}

var outputSeverity string
switch severity.Type {
case models.SeverityCVSSV2:
numericSeverity, _ := v2_metric.NewBase().Decode(severity.Score)
outputSeverity = fmt.Sprintf("%v", numericSeverity.Score())
case models.SeverityCVSSV3:
numericSeverity, _ := v3_metric.NewBase().Decode(severity.Score)
outputSeverity = fmt.Sprintf("%v", numericSeverity.Score())
default:
outputSeverity = severity.Score
}

outputSeverities = append(outputSeverities, outputSeverity)
}
}
outputRow = append(outputRow, strings.Join(outputSeverities, "\n"))
}

if pkg.Package.Ecosystem == "GIT" {
outputRow = append(outputRow, "GIT", pkg.Package.Version, pkg.Package.Version)
shouldMerge = true
Expand Down
8 changes: 5 additions & 3 deletions pkg/reporter/table_reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ type TableReporter struct {
stdout io.Writer
stderr io.Writer
markdown bool
includeSeverity bool
}

func NewTableReporter(stdout io.Writer, stderr io.Writer, markdown bool) *TableReporter {
func NewTableReporter(stdout io.Writer, stderr io.Writer, markdown bool, includeSeverity bool) *TableReporter {
return &TableReporter{
stdout: stdout,
stderr: stderr,
hasPrintedError: false,
markdown: markdown,
includeSeverity: includeSeverity,
}
}

Expand All @@ -44,9 +46,9 @@ func (r *TableReporter) PrintResult(vulnResult *models.VulnerabilityResults) err
}

if r.markdown {
output.PrintMarkdownTableResults(vulnResult, r.stdout)
output.PrintMarkdownTableResults(vulnResult, r.stdout, r.includeSeverity)
} else {
output.PrintTableResults(vulnResult, r.stdout)
output.PrintTableResults(vulnResult, r.stdout, r.includeSeverity)
}

return nil
Expand Down