diff --git a/pkg/cli/client/cve_cmd_internal_test.go b/pkg/cli/client/cve_cmd_internal_test.go index 1a97271f..327f776c 100644 --- a/pkg/cli/client/cve_cmd_internal_test.go +++ b/pkg/cli/client/cve_cmd_internal_test.go @@ -140,9 +140,22 @@ func TestSearchCVECmd(t *testing.T) { cmd.SetArgs(args) err := cmd.Execute() space := regexp.MustCompile(`\s+`) - str := space.ReplaceAllString(buff.String(), " ") - So(strings.TrimSpace(str), ShouldEqual, "CRITICAL 0, HIGH 1, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 1 "+ - "ID SEVERITY TITLE dummyCVEID HIGH Title of that CVE") + outputLines := strings.Split(buff.String(), "\n") + + expected := []string{ + "CRITICAL 0, HIGH 1, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 1", + "", + "ID SEVERITY TITLE VULNERABLE PACKAGE PATH INSTALL-VER FIXED-VER", + "dummyCVEID HIGH Title of that CVE", + "packagename - installedver fixedver", + } + + for expectedLineIndex, expectedLine := range expected { + currentOutputLine := outputLines[expectedLineIndex] + str := space.ReplaceAllString(currentOutputLine, " ") + So(strings.TrimSpace(str), ShouldEqual, expectedLine) + } + So(err, ShouldBeNil) }) @@ -207,9 +220,22 @@ func TestSearchCVECmd(t *testing.T) { cveCmd.SetArgs(args) err := cveCmd.Execute() space := regexp.MustCompile(`\s+`) - str := space.ReplaceAllString(buff.String(), " ") - So(strings.TrimSpace(str), ShouldEqual, "CRITICAL 0, HIGH 1, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 1 "+ - "ID SEVERITY TITLE dummyCVEID HIGH Title of that CVE") + outputLines := strings.Split(buff.String(), "\n") + + expected := []string{ + "CRITICAL 0, HIGH 1, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 1", + "", + "ID SEVERITY TITLE VULNERABLE PACKAGE PATH INSTALL-VER FIXED-VER", + "dummyCVEID HIGH Title of that CVE", + "packagename - installedver fixedver", + } + + for expectedLineIndex, expectedLine := range expected { + currentOutputLine := outputLines[expectedLineIndex] + str := space.ReplaceAllString(currentOutputLine, " ") + So(strings.TrimSpace(str), ShouldEqual, expectedLine) + } + So(err, ShouldBeNil) }) diff --git a/pkg/cli/client/cve_cmd_test.go b/pkg/cli/client/cve_cmd_test.go index 552fa388..e06a6c91 100644 --- a/pkg/cli/client/cve_cmd_test.go +++ b/pkg/cli/client/cve_cmd_test.go @@ -901,15 +901,25 @@ func TestCVESort(t *testing.T) { cmd.SetArgs(args) err := cmd.Execute() So(err, ShouldBeNil) - str := space.ReplaceAllString(buff.String(), " ") - actual := strings.TrimSpace(str) - So(actual, ShouldResemble, - "CRITICAL 1, HIGH 1, MEDIUM 2, LOW 1, UNKNOWN 0, TOTAL 5 ID SEVERITY TITLE "+ - "CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par... "+ - "CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ... "+ - "CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif... "+ - "CVE-2023-3817 MEDIUM Excessive time spent checking DH q parameter ... "+ - "CVE-2023-1255 LOW Input buffer over-read in AES-XTS implementat...") + + outputLines := strings.Split(buff.String(), "\n") + + expected := []string{ + "CRITICAL 1, HIGH 1, MEDIUM 2, LOW 1, UNKNOWN 0, TOTAL 5", + "", + "ID SEVERITY TITLE VULNERABLE PACKAGE PATH INSTALL-VER FIXED-VER", + "CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par...", + "CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ...", + "CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif...", + "CVE-2023-3817 MEDIUM Excessive time spent checking DH q parameter ...", + "CVE-2023-1255 LOW Input buffer over-read in AES-XTS implementat...", + } + + for expectedLineIndex, expectedLine := range expected { + currentOutputLine := outputLines[expectedLineIndex] + str := space.ReplaceAllString(currentOutputLine, " ") + So(strings.TrimSpace(str), ShouldResemble, expectedLine) + } args = []string{"list", "repo:tag", "--sort-by", "alpha-asc", "--url", baseURL} cmd = client.NewCVECommand(client.NewSearchService()) @@ -919,15 +929,25 @@ func TestCVESort(t *testing.T) { cmd.SetArgs(args) err = cmd.Execute() So(err, ShouldBeNil) - str = space.ReplaceAllString(buff.String(), " ") - actual = strings.TrimSpace(str) - So(actual, ShouldResemble, - "CRITICAL 1, HIGH 1, MEDIUM 2, LOW 1, UNKNOWN 0, TOTAL 5 ID SEVERITY TITLE "+ - "CVE-2023-1255 LOW Input buffer over-read in AES-XTS implementat... "+ - "CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif... "+ - "CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ... "+ - "CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par... "+ - "CVE-2023-3817 MEDIUM Excessive time spent checking DH q parameter ...") + + outputLines = strings.Split(buff.String(), "\n") + + expected = []string{ + "CRITICAL 1, HIGH 1, MEDIUM 2, LOW 1, UNKNOWN 0, TOTAL 5", + "", + "ID SEVERITY TITLE VULNERABLE PACKAGE PATH INSTALL-VER FIXED-VER", + "CVE-2023-1255 LOW Input buffer over-read in AES-XTS implementat...", + "CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif...", + "CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ...", + "CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par...", + "CVE-2023-3817 MEDIUM Excessive time spent checking DH q parameter ...", + } + + for expectedLineIndex, expectedLine := range expected { + currentOutputLine := outputLines[expectedLineIndex] + str := space.ReplaceAllString(currentOutputLine, " ") + So(strings.TrimSpace(str), ShouldResemble, expectedLine) + } args = []string{"list", "repo:tag", "--sort-by", "alpha-dsc", "--url", baseURL} cmd = client.NewCVECommand(client.NewSearchService()) @@ -937,15 +957,25 @@ func TestCVESort(t *testing.T) { cmd.SetArgs(args) err = cmd.Execute() So(err, ShouldBeNil) - str = space.ReplaceAllString(buff.String(), " ") - actual = strings.TrimSpace(str) - So(actual, ShouldResemble, - "CRITICAL 1, HIGH 1, MEDIUM 2, LOW 1, UNKNOWN 0, TOTAL 5 ID SEVERITY TITLE "+ - "CVE-2023-3817 MEDIUM Excessive time spent checking DH q parameter ... "+ - "CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par... "+ - "CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ... "+ - "CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif... "+ - "CVE-2023-1255 LOW Input buffer over-read in AES-XTS implementat...") + + outputLines = strings.Split(buff.String(), "\n") + + expected = []string{ + "CRITICAL 1, HIGH 1, MEDIUM 2, LOW 1, UNKNOWN 0, TOTAL 5", + "", + "ID SEVERITY TITLE VULNERABLE PACKAGE PATH INSTALL-VER FIXED-VER", + "CVE-2023-3817 MEDIUM Excessive time spent checking DH q parameter ...", + "CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par...", + "CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ...", + "CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif...", + "CVE-2023-1255 LOW Input buffer over-read in AES-XTS implementat...", + } + + for expectedLineIndex, expectedLine := range expected { + currentOutputLine := outputLines[expectedLineIndex] + str := space.ReplaceAllString(currentOutputLine, " ") + So(strings.TrimSpace(str), ShouldResemble, expectedLine) + } }) } diff --git a/pkg/cli/client/search_functions_internal_test.go b/pkg/cli/client/search_functions_internal_test.go index 8d54f69c..97f5e163 100644 --- a/pkg/cli/client/search_functions_internal_test.go +++ b/pkg/cli/client/search_functions_internal_test.go @@ -390,9 +390,12 @@ func TestSearchCVEForImageGQL(t *testing.T) { expected := []string{ "CRITICAL 0, HIGH 2, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 2", "", - "ID SEVERITY TITLE", + "ID SEVERITY TITLE VULNERABLE PACKAGE PATH INSTALL-VER FIXED-VER", "dummyCVEID HIGH Title of that CVE", + "packagename - installedver fixedver", "test-cve-id2 HIGH Test CVE 2", + "packagename /usr/bin/dummy.jar installedver fixedver", + "packagename /usr/bin/dummy.gem installedver fixedver", } space := regexp.MustCompile(`\s+`) diff --git a/pkg/cli/client/service.go b/pkg/cli/client/service.go index 48ee82d6..41538ff8 100644 --- a/pkg/cli/client/service.go +++ b/pkg/cli/client/service.go @@ -877,16 +877,21 @@ func (cve cveResult) stringPlainText() string { table := getCVETableWriter(&builder) - for _, c := range cve.Data.CVEListForImage.CVEList { - id := ellipsize(c.ID, cveIDWidth, ellipsis) - title := ellipsize(c.Title, cveTitleWidth, ellipsis) - severity := ellipsize(c.Severity, cveSeverityWidth, ellipsis) + for _, cveListItem := range cve.Data.CVEListForImage.CVEList { + id := ellipsize(cveListItem.ID, cveIDWidth, ellipsis) + title := ellipsize(cveListItem.Title, cveTitleWidth, ellipsis) + severity := ellipsize(cveListItem.Severity, cveSeverityWidth, ellipsis) row := make([]string, 3) //nolint:gomnd row[colCVEIDIndex] = id row[colCVESeverityIndex] = severity row[colCVETitleIndex] = title table.Append(row) + + for _, pkg := range cveListItem.PackageList { + pkgRow := generateTableRowForVulnerablePackage(pkg) + table.Append(pkgRow) + } } table.Render() @@ -894,6 +899,25 @@ func (cve cveResult) stringPlainText() string { return builder.String() } +func generateTableRowForVulnerablePackage(pkg packageList) []string { + row := make([]string, cveColTotalCount) + pkgName := ellipsize(pkg.Name, cveVulnPkgNameWidth, ellipsis) + pkgPath := "-" + + if pkg.PackagePath != "" { + pkgPath = ellipsize(pkg.PackagePath, cveVulnPkgPathWidth, ellipsis) + } + pkgInstalledVer := ellipsize(pkg.InstalledVersion, cveVulnPkgInstalledVerWidth, ellipsis) + pkgFixedVer := ellipsize(pkg.FixedVersion, cveVulnPkgFixedVerWidth, ellipsis) + + row[colCVEVulnPkgNameIndex] = pkgName + row[colCVEVulnPkgPathIndex] = pkgPath + row[colCVEVulnPkgInstalledVerIndex] = pkgInstalledVer + row[colCVEVulnPkgFixedVerIndex] = pkgFixedVer + + return row +} + func (cve cveResult) stringJSON() (string, error) { // Output is in json lines format - do not indent, append new line after json json := jsoniter.ConfigCompatibleWithStandardLibrary @@ -1362,6 +1386,10 @@ func getCVETableWriter(writer io.Writer) *tablewriter.Table { table.SetColMinWidth(colCVEIDIndex, cveIDWidth) table.SetColMinWidth(colCVESeverityIndex, cveSeverityWidth) table.SetColMinWidth(colCVETitleIndex, cveTitleWidth) + table.SetColMinWidth(colCVEVulnPkgNameIndex, cveVulnPkgNameWidth) + table.SetColMinWidth(colCVEVulnPkgPathIndex, cveVulnPkgPathWidth) + table.SetColMinWidth(colCVEVulnPkgInstalledVerIndex, cveVulnPkgInstalledVerWidth) + table.SetColMinWidth(colCVEVulnPkgFixedVerIndex, cveVulnPkgFixedVerWidth) return table } @@ -1459,13 +1487,23 @@ const ( layersWidth = 8 ellipsis = "..." - cveIDWidth = 16 - cveSeverityWidth = 8 - cveTitleWidth = 48 + cveIDWidth = 16 + cveSeverityWidth = 8 + cveTitleWidth = 48 + cveVulnPkgNameWidth = 35 + cveVulnPkgPathWidth = 30 + cveVulnPkgInstalledVerWidth = 20 + cveVulnPkgFixedVerWidth = 20 - colCVEIDIndex = 0 - colCVESeverityIndex = 1 - colCVETitleIndex = 2 + colCVEIDIndex = 0 + colCVESeverityIndex = 1 + colCVETitleIndex = 2 + colCVEVulnPkgNameIndex = 3 + colCVEVulnPkgPathIndex = 4 + colCVEVulnPkgInstalledVerIndex = 5 + colCVEVulnPkgFixedVerIndex = 6 + + cveColTotalCount = 7 defaultOutputFormat = "text" ) diff --git a/pkg/cli/client/utils.go b/pkg/cli/client/utils.go index 8de8510c..dc7a5752 100644 --- a/pkg/cli/client/utils.go +++ b/pkg/cli/client/utils.go @@ -184,12 +184,12 @@ func printImageTableHeader(writer io.Writer, verbose bool, maxImageNameLen, maxT func printCVETableHeader(writer io.Writer) { table := getCVETableWriter(writer) - row := make([]string, 3) //nolint:gomnd - row[colCVEIDIndex] = "ID" - row[colCVESeverityIndex] = "SEVERITY" - row[colCVETitleIndex] = "TITLE" + columnHeadingsRow := []string{ + "ID", "SEVERITY", "TITLE", + "VULNERABLE PACKAGE", "PATH", "INSTALL-VER", "FIXED-VER", + } - table.Append(row) + table.Append(columnHeadingsRow) table.Render() }