Skip to content

Commit

Permalink
feat(cli): add --details flag to vulnerability cmd
Browse files Browse the repository at this point in the history
This change is adding a new flag to the vulnerability command called
`--details`, this flag will extend the report shown to the end-user.

The new information presented with this flag is the list of CVE's with
its severity, version and, if any, fix version, plus the layer that the
vulnerability was introduced by.

Example:
```
$ lacework vulnerability report sha256:123ab...xyz --details
```

This flag is available to all sub-commands inside the `vulnerability`
cmd. (`scan run`, `scan show`, and `report` as shown above)

Closes #82

Signed-off-by: Salim Afiune Maya <[email protected]>
  • Loading branch information
afiune committed Apr 27, 2020
1 parent 88e92a8 commit 227a7b2
Showing 1 changed file with 103 additions and 9 deletions.
112 changes: 103 additions & 9 deletions cli/cmd/vulnerability.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ package cmd

import (
"fmt"
"sort"
"strings"
"time"

"github.com/olekukonko/tablewriter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"

"github.com/lacework/go-sdk/api"
)
Expand All @@ -40,6 +42,9 @@ var (

// when enabled we tread the provided sha256 hash as image digest
Digest bool

// display extended details about a vulnerability scan/report
Details bool
}{PollInterval: time.Second * 5}

// vulnerability represents the vulnerability command
Expand Down Expand Up @@ -260,13 +265,15 @@ func init() {
vulScanCmd.AddCommand(vulScanRunCmd)
vulScanCmd.AddCommand(vulScanShowCmd)

vulScanRunCmd.Flags().BoolVar(&vulCmdState.Poll, "poll", false,
fmt.Sprintf("poll until the vulnerability scan completes (%vs intervals)",
vulCmdState.PollInterval.Seconds()),
setPollFlag(
vulScanRunCmd.Flags(),
vulScanShowCmd.Flags(),
)
vulScanShowCmd.Flags().BoolVar(&vulCmdState.Poll, "poll", false,
fmt.Sprintf("poll until the vulnerability scan completes (%vs intervals)",
vulCmdState.PollInterval.Seconds()),

setDetailsFlag(
vulScanRunCmd.Flags(),
vulScanShowCmd.Flags(),
vulReportCmd.Flags(),
)

vulReportCmd.Flags().BoolVar(
Expand All @@ -275,6 +282,27 @@ func init() {
)
}

func setPollFlag(cmds ...*flag.FlagSet) {
for _, cmd := range cmds {
if cmd != nil {
cmd.BoolVar(&vulCmdState.Poll, "poll", false,
fmt.Sprintf("poll until the vulnerability scan completes (%vs intervals)",
vulCmdState.PollInterval.Seconds()),
)
}
}
}

func setDetailsFlag(cmds ...*flag.FlagSet) {
for _, cmd := range cmds {
if cmd != nil {
cmd.BoolVar(&vulCmdState.Details, "details", false,
"display extended details about a vulnerability report",
)
}
}
}

func pollScanStatus(requestID string, lacework *api.Client) error {
cli.StartProgress(" Scan running...")

Expand Down Expand Up @@ -334,7 +362,7 @@ func buildVulnerabilityReport(report *api.VulContainerReport) string {
t *tablewriter.Table
imageDetailsTable = &strings.Builder{}
vulCountsTable = &strings.Builder{}
mainReport = &strings.Builder{}
summaryReport = &strings.Builder{}
)

t = tablewriter.NewWriter(imageDetailsTable)
Expand All @@ -352,7 +380,7 @@ func buildVulnerabilityReport(report *api.VulContainerReport) string {
t.AppendBulk(vulContainerReportToCountsTable(report))
t.Render()

t = tablewriter.NewWriter(mainReport)
t = tablewriter.NewWriter(summaryReport)
t.SetBorder(false)
t.SetAutoWrapText(false)
t.SetHeader([]string{
Expand All @@ -365,7 +393,73 @@ func buildVulnerabilityReport(report *api.VulContainerReport) string {
})
t.Render()

return mainReport.String()
summary := summaryReport.String()

if vulCmdState.Details {
cveDetailsTable, length := buildVulnerabilityReportDetails(report)
// if the extended report is bigger than 100 rows,
// lets repeat the summary table at the bottom
if length > 100 {
return summary + cveDetailsTable + "\n" + summary
}

return summary + cveDetailsTable + "\n"
}

return summary
}

func buildVulnerabilityReportDetails(report *api.VulContainerReport) (string, int) {
var (
imageLayersTable = vulContainerImageLayersToTable(&report.Image)
detailsTable = &strings.Builder{}
t = tablewriter.NewWriter(detailsTable)
)

t.SetRowLine(true)
t.SetBorders(tablewriter.Border{
Left: false,
Right: false,
Top: true,
Bottom: true,
})
t.SetHeader([]string{
"CVE",
"Severity",
"Package",
"Current Version",
"Fix Version",
"Introduced in Layer (sha256)",
})
t.AppendBulk(imageLayersTable)
t.Render()

return detailsTable.String(), len(imageLayersTable)
}

func vulContainerImageLayersToTable(image *api.VulContainerImage) [][]string {
out := [][]string{}
for _, layer := range image.ImageLayers {
for _, pkg := range layer.Packages {
for _, vul := range pkg.Vulnerabilities {
out = append(out, []string{
vul.Name,
strings.Title(vul.Severity),
pkg.Name,
pkg.Version,
vul.FixVersion,
layer.Hash,
})
}
}
}

// @afiune change
sort.Slice(out, func(i, j int) bool {
return out[i][1] < out[j][1]
})

return out
}

func vulContainerReportToCountsTable(report *api.VulContainerReport) [][]string {
Expand Down

0 comments on commit 227a7b2

Please sign in to comment.