diff --git a/cmd/osv-scanner/main.go b/cmd/osv-scanner/main.go index 3b545cbf8fd..0a394f96a85 100644 --- a/cmd/osv-scanner/main.go +++ b/cmd/osv-scanner/main.go @@ -63,11 +63,15 @@ func run(args []string, stdout, stderr io.Writer) int { Usage: "sets the output format", Value: "table", Action: func(context *cli.Context, s string) error { - if s != "table" && s != "json" { - return fmt.Errorf("unsupported output format \"%s\" - must be either \"table\" or \"json\"", s) + switch s { + case + "table", + "json", + "markdown": + return nil } - return nil + return fmt.Errorf("unsupported output format \"%s\" - must be one of: \"table\", \"json\", \"markdown\"", s) }, }, &cli.BoolFlag{ diff --git a/cmd/osv-scanner/main_test.go b/cmd/osv-scanner/main_test.go index 1bc3e017471..df22b68247f 100644 --- a/cmd/osv-scanner/main_test.go +++ b/cmd/osv-scanner/main_test.go @@ -222,6 +222,17 @@ func TestRun(t *testing.T) { Scanned %%/fixtures/locks-many/composer.lock file and found 1 packages `, }, + // output format: markdown table + { + name: "", + args: []string{"", "--format", "markdown", "./fixtures/locks-many/composer.lock"}, + wantExitCode: 0, + wantStdout: ` + Scanning dir ./fixtures/locks-many/composer.lock + Scanned %%/fixtures/locks-many/composer.lock file and found 1 packages + `, + wantStderr: "", + }, } for _, tt := range tests { tt := tt diff --git a/internal/output/markdowntable.go b/internal/output/markdowntable.go new file mode 100644 index 00000000000..c141d4f173d --- /dev/null +++ b/internal/output/markdowntable.go @@ -0,0 +1,23 @@ +package output + +import ( + "io" + + "github.com/google/osv-scanner/pkg/models" + + "github.com/jedib0t/go-pretty/v6/table" +) + +// PrintTableResults prints the osv scan results into a human friendly table. +func PrintMarkdownTableResults(vulnResult *models.VulnerabilityResults, outputWriter io.Writer) { + outputTable := table.NewWriter() + outputTable.SetOutputMirror(outputWriter) + outputTable.AppendHeader(table.Row{"OSV URL", "Ecosystem", "Package", "Version", "Source"}) + + outputTable = tableBuilder(outputTable, vulnResult, false) + + if outputTable.Length() == 0 { + return + } + outputTable.RenderMarkdown() +} diff --git a/internal/output/reporter.go b/internal/output/reporter.go index cd744aa3334..f37234ad1e0 100644 --- a/internal/output/reporter.go +++ b/internal/output/reporter.go @@ -61,6 +61,8 @@ func (r *Reporter) PrintResult(vulnResult *models.VulnerabilityResults) error { switch r.format { case "json": return PrintJSONResults(vulnResult, r.stdout) + case "markdown": + PrintMarkdownTableResults(vulnResult, r.stdout) case "table": PrintTableResults(vulnResult, r.stdout) } diff --git a/internal/output/table.go b/internal/output/table.go index da0cf6e760f..b42c9a1c43c 100644 --- a/internal/output/table.go +++ b/internal/output/table.go @@ -31,17 +31,28 @@ 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) + + if outputTable.Length() == 0 { + return + } + outputTable.Render() +} + +func tableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResults, addStyling bool) table.Writer { + // Working directory used to simplify path + workingDir, workingDirErr := os.Getwd() for _, sourceRes := range vulnResult.Results { for _, pkg := range sourceRes.Packages { - workingDir, err := os.Getwd() source := sourceRes.Source - if err == nil { + if workingDirErr == nil { sourcePath, err := filepath.Rel(workingDir, source.Path) if err == nil { // Simplify the path if possible source.Path = sourcePath } } + // Merge groups into the same row for _, group := range pkg.Groups { outputRow := table.Row{} shouldMerge := false @@ -49,7 +60,7 @@ func PrintTableResults(vulnResult *models.VulnerabilityResults, outputWriter io. var links []string for _, vuln := range group.IDs { - if isTerminal { + if addStyling { links = append(links, osv.BaseVulnerabilityURL+text.Bold.EscapeSeq()+vuln+text.Reset.EscapeSeq()) } else { links = append(links, osv.BaseVulnerabilityURL+vuln) @@ -71,8 +82,5 @@ func PrintTableResults(vulnResult *models.VulnerabilityResults, outputWriter io. } } - if outputTable.Length() == 0 { - return - } - outputTable.Render() + return outputTable }