From 407ebd957266b2236504f55b2eea0c4e7be0d003 Mon Sep 17 00:00:00 2001 From: Darren Murray Date: Tue, 6 Apr 2021 17:00:01 +0100 Subject: [PATCH 01/15] feat: Filter vuln host list cves by severity Signed-off-by: Darren Murray --- cli/cmd/event.go | 2 + cli/cmd/vuln_host.go | 76 ++++++++++++++++++++++-------- cli/cmd/vuln_host_test.go | 97 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 20 deletions(-) create mode 100644 cli/cmd/vuln_host_test.go diff --git a/cli/cmd/event.go b/cli/cmd/event.go index d69dffee5..8d3431d58 100644 --- a/cli/cmd/event.go +++ b/cli/cmd/event.go @@ -843,6 +843,8 @@ func severityToProperTypes(severity string) (int, string) { return 4, "Low" case "5", "info": return 5, "Info" + case "6", "negligible": + return 6, "Negligible" default: return 0, "Unknown" } diff --git a/cli/cmd/vuln_host.go b/cli/cmd/vuln_host.go index 8e88b25f9..666711e14 100644 --- a/cli/cmd/vuln_host.go +++ b/cli/cmd/vuln_host.go @@ -206,6 +206,10 @@ with fixes: $ lacework vulnerability host list-cves --active --fixable`, RunE: func(_ *cobra.Command, args []string) error { + if err := validateSeverityFlags(); err != nil { + return err + } + response, err := cli.LwApi.Vulnerabilities.Host.ListCves() if err != nil { return errors.Wrap(err, "unable to get CVEs from hosts") @@ -234,7 +238,7 @@ with fixes: return nil } - rows := hostVulnCVEsTable(response.CVEs) + rows, filtered := hostVulnCVEsTable(response.CVEs) // if the user wants to show only online or // offline hosts, show a friendly message if len(rows) == 0 { @@ -250,6 +254,10 @@ with fixes: ), ) + if filtered != "" { + cli.OutputHuman(filtered) + } + if !vulCmdState.Active { cli.OutputHuman( "\nTry adding '--active' to only show vulnerabilities of packages actively running.\n", @@ -405,6 +413,11 @@ func init() { vulHostShowAssessmentCmd.Flags(), ) + setSeverityFlag( + vulHostListCvesCmd.Flags(), + vulHostShowAssessmentCmd.Flags(), + ) + setFailOnSeverityFlag( vulHostShowAssessmentCmd.Flags(), vulHostScanPkgManifestCmd.Flags(), @@ -556,30 +569,53 @@ func hostVulnPackagesTable(cves []api.HostVulnCVE, withHosts bool) [][]string { return out } -func hostVulnCVEsTable(cves []api.HostVulnCVE) [][]string { - out := [][]string{} - out = append(out, hostVulnCVEsTableForSeverity(cves, "Critical")...) - out = append(out, hostVulnCVEsTableForSeverity(cves, "High")...) - out = append(out, hostVulnCVEsTableForSeverity(cves, "Medium")...) - out = append(out, hostVulnCVEsTableForSeverity(cves, "Low")...) - //out = append(out, hostVulnCVEsTableForSeverity(cves, "Info")...) - out = append(out, hostVulnCVEsTableForSeverity(cves, "Negligible")...) - return out +func hostVulnCVEsTable(cves []api.HostVulnCVE) ([][]string, string) { + var out [][]string + var filteredCves = 0 + var totalCves = 0 + + for _, severity := range api.ValidEventSeverities { + _, sev := severityToProperTypes(severity) + output, filtered, total := hostVulnCVEsTableForSeverity(cves, sev) + filteredCves += filtered + totalCves += total + out = append(out, output...) + } + + if filteredCves > 0 { + return out, fmt.Sprintf("\n %v of %v cve(s) showing \n", filteredCves, totalCves) + } + + return out, "" } -func hostVulnCVEsTableForSeverity(cves []api.HostVulnCVE, severity string) [][]string { +func hostVulnCVEsTableForSeverity(cves []api.HostVulnCVE, severity string) ([][]string, int ,int) { out := [][]string{} + var filtered = 0 + var total = 0 + for _, cve := range cves { for _, pkg := range cve.Packages { - // if the user wants to show only vulnerabilities of acive packages - if vulCmdState.Active && pkg.PackageStatus == "" { - continue - } - if vulCmdState.Fixable && pkg.FixedVersion == "" { - continue - } - if pkg.Severity == severity { + total++ + + // if the user wants to show only vulnerabilities of active packages + if vulCmdState.Active && pkg.PackageStatus == "" { + filtered++ + continue + } + if vulCmdState.Fixable && pkg.FixedVersion == "" { + filtered++ + continue + } + + if vulCmdState.Severity != "" { + if filterSeverity(severity, vulCmdState.Severity) { + filtered++ + continue + } + } + out = append(out, []string{ cve.ID, pkg.Severity, @@ -601,7 +637,7 @@ func hostVulnCVEsTableForSeverity(cves []api.HostVulnCVE, severity string) [][]s return stringToInt(out[i][7]) > stringToInt(out[j][7]) }) - return out + return out, total-filtered, total } func hostVulnHostDetailsToTable(assessment api.HostVulnHostAssessment) string { diff --git a/cli/cmd/vuln_host_test.go b/cli/cmd/vuln_host_test.go new file mode 100644 index 000000000..d46e2ff4a --- /dev/null +++ b/cli/cmd/vuln_host_test.go @@ -0,0 +1,97 @@ +// +// Author:: Darren Murray () +// Copyright:: Copyright 2021, Lacework Inc. +// License:: Apache License, Version 2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package cmd + +import ( + "github.com/lacework/go-sdk/api" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestListCvesFilterSeverity(t *testing.T) { + vulCmdState.Severity = "critical" + mockCves := []api.HostVulnCVE{mockCveOne} + result, output := hostVulnCVEsTable(mockCves) + + assert.Equal(t, len(result), 1) + assert.Equal(t, output, "\n 1 of 2 cve(s) showing \n") + + clearVulnFilters() +} + +func clearVulnFilters() { + vulCmdState.Severity = "" +} + +var mockCveOne = api.HostVulnCVE{ + ID: "", + Packages: []api.HostVulnPackage{mockPackageOne, mockPackageTwo}, + Summary: api.HostVulnCveSummary{ + Severity: api.HostVulnSeverityCounts{ + Critical: &api.HostVulnSeverityCountsDetails{ + Fixable: 1, + Vulnerabilities: 1, + }, + High: &api.HostVulnSeverityCountsDetails{ + Fixable: 1, + Vulnerabilities: 1, + }, + Medium: nil, + Low: nil, + Negligible: nil, + }, + TotalVulnerabilities: 2, + LastEvaluationTime: api.Json16DigitTime{}, + }, +} + +var mockPackageOne = api.HostVulnPackage{ + Name: "test", + Namespace: "rhel:8", + Severity: "High", + Status: "Active", + VulnerabilityStatus: "", + Version: "", + HostCount: "1", + PackageStatus: "", + CveLink: "", + CvssScore: "", + CvssV2Score: "", + CvssV3Score: "", + FixAvailable: "1", + FixedVersion: "", +} + +var mockPackageTwo = api.HostVulnPackage{ + Name: "test2", + Namespace: "rhel:8", + Severity: "Critical", + Status: "Active", + VulnerabilityStatus: "", + Version: "", + HostCount: "1", + PackageStatus: "", + CveLink: "", + CvssScore: "", + CvssV2Score: "", + CvssV3Score: "", + FixAvailable: "1", + FixedVersion: "", +} + From 74ec9d6d098924624040abd0c52974db4f9283b2 Mon Sep 17 00:00:00 2001 From: Darren <75614232+dmurray-lacework@users.noreply.github.com> Date: Wed, 7 Apr 2021 09:57:54 +0100 Subject: [PATCH 02/15] style: declare vars in block Co-authored-by: Salim Afiune --- cli/cmd/vuln_host.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cli/cmd/vuln_host.go b/cli/cmd/vuln_host.go index 666711e14..f405a3d48 100644 --- a/cli/cmd/vuln_host.go +++ b/cli/cmd/vuln_host.go @@ -590,9 +590,11 @@ func hostVulnCVEsTable(cves []api.HostVulnCVE) ([][]string, string) { } func hostVulnCVEsTableForSeverity(cves []api.HostVulnCVE, severity string) ([][]string, int ,int) { - out := [][]string{} - var filtered = 0 - var total = 0 + var ( + filtered = 0 + total = 0 + out = [][]string{} + ) for _, cve := range cves { for _, pkg := range cve.Packages { From fb71605cab0ea158e90a0ca8d7e5171ee64397a9 Mon Sep 17 00:00:00 2001 From: Darren Murray Date: Wed, 7 Apr 2021 10:08:19 +0100 Subject: [PATCH 03/15] refactor: Address code review comments Signed-off-by: Darren Murray --- cli/cmd/vuln_host.go | 5 +++-- cli/cmd/vuln_host_test.go | 25 +++---------------------- 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/cli/cmd/vuln_host.go b/cli/cmd/vuln_host.go index f405a3d48..c26744618 100644 --- a/cli/cmd/vuln_host.go +++ b/cli/cmd/vuln_host.go @@ -583,7 +583,8 @@ func hostVulnCVEsTable(cves []api.HostVulnCVE) ([][]string, string) { } if filteredCves > 0 { - return out, fmt.Sprintf("\n %v of %v cve(s) showing \n", filteredCves, totalCves) + showing := totalCves - filteredCves + return out, fmt.Sprintf("\n %v of %v cve(s) showing \n", showing, totalCves) } return out, "" @@ -639,7 +640,7 @@ func hostVulnCVEsTableForSeverity(cves []api.HostVulnCVE, severity string) ([][] return stringToInt(out[i][7]) > stringToInt(out[j][7]) }) - return out, total-filtered, total + return out, filtered, total } func hostVulnHostDetailsToTable(assessment api.HostVulnHostAssessment) string { diff --git a/cli/cmd/vuln_host_test.go b/cli/cmd/vuln_host_test.go index d46e2ff4a..5c7d8e745 100644 --- a/cli/cmd/vuln_host_test.go +++ b/cli/cmd/vuln_host_test.go @@ -26,13 +26,13 @@ import ( func TestListCvesFilterSeverity(t *testing.T) { vulCmdState.Severity = "critical" + defer clearVulnFilters() + mockCves := []api.HostVulnCVE{mockCveOne} result, output := hostVulnCVEsTable(mockCves) assert.Equal(t, len(result), 1) assert.Equal(t, output, "\n 1 of 2 cve(s) showing \n") - - clearVulnFilters() } func clearVulnFilters() { @@ -40,7 +40,7 @@ func clearVulnFilters() { } var mockCveOne = api.HostVulnCVE{ - ID: "", + ID: "TestID", Packages: []api.HostVulnPackage{mockPackageOne, mockPackageTwo}, Summary: api.HostVulnCveSummary{ Severity: api.HostVulnSeverityCounts{ @@ -52,9 +52,6 @@ var mockCveOne = api.HostVulnCVE{ Fixable: 1, Vulnerabilities: 1, }, - Medium: nil, - Low: nil, - Negligible: nil, }, TotalVulnerabilities: 2, LastEvaluationTime: api.Json16DigitTime{}, @@ -66,16 +63,8 @@ var mockPackageOne = api.HostVulnPackage{ Namespace: "rhel:8", Severity: "High", Status: "Active", - VulnerabilityStatus: "", - Version: "", HostCount: "1", - PackageStatus: "", - CveLink: "", - CvssScore: "", - CvssV2Score: "", - CvssV3Score: "", FixAvailable: "1", - FixedVersion: "", } var mockPackageTwo = api.HostVulnPackage{ @@ -83,15 +72,7 @@ var mockPackageTwo = api.HostVulnPackage{ Namespace: "rhel:8", Severity: "Critical", Status: "Active", - VulnerabilityStatus: "", - Version: "", HostCount: "1", - PackageStatus: "", - CveLink: "", - CvssScore: "", - CvssV2Score: "", - CvssV3Score: "", FixAvailable: "1", - FixedVersion: "", } From 99c2bf5747a998a4e348b29aeab7b76a98c12fc9 Mon Sep 17 00:00:00 2001 From: Darren Murray Date: Wed, 7 Apr 2021 11:58:52 +0100 Subject: [PATCH 04/15] feat: Filter vuln host show-assessment by severity Signed-off-by: Darren Murray --- cli/cmd/vuln_host.go | 36 +++++++++++++++++++++++++++--------- cli/cmd/vuln_host_test.go | 39 +++++++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/cli/cmd/vuln_host.go b/cli/cmd/vuln_host.go index c26744618..b4107d610 100644 --- a/cli/cmd/vuln_host.go +++ b/cli/cmd/vuln_host.go @@ -583,14 +583,14 @@ func hostVulnCVEsTable(cves []api.HostVulnCVE) ([][]string, string) { } if filteredCves > 0 { - showing := totalCves - filteredCves + showing := totalCves - filteredCves return out, fmt.Sprintf("\n %v of %v cve(s) showing \n", showing, totalCves) } return out, "" } -func hostVulnCVEsTableForSeverity(cves []api.HostVulnCVE, severity string) ([][]string, int ,int) { +func hostVulnCVEsTableForSeverity(cves []api.HostVulnCVE, severity string) ([][]string, int, int) { var ( filtered = 0 total = 0 @@ -686,7 +686,7 @@ func hostVulnHostDetailsToTable(assessment api.HostVulnHostAssessment) string { ) mainBldr.WriteString("\n") - if vulCmdState.Details || vulCmdState.Fixable || vulCmdState.Packages || vulCmdState.Active { + if vulCmdState.Details || vulCmdState.Fixable || vulCmdState.Packages || vulCmdState.Active || vulCmdState.Severity != "" { if vulCmdState.Packages { mainBldr.WriteString( renderSimpleTable( @@ -695,7 +695,7 @@ func hostVulnHostDetailsToTable(assessment api.HostVulnHostAssessment) string { ), ) } else { - rows := hostVulnCVEsTableForHostView(assessment.CVEs) + rows, filtered := hostVulnCVEsTableForHostView(assessment.CVEs) // if the user wants to show only vulnerabilities of active packages // and we don't have any, show a friendly message @@ -716,11 +716,15 @@ func hostVulnHostDetailsToTable(assessment api.HostVulnHostAssessment) string { rows, )) } + + if filtered != "" { + mainBldr.WriteString(filtered) + } } mainBldr.WriteString("\n") } - if !vulCmdState.Details && !vulCmdState.Active && !vulCmdState.Fixable && !vulCmdState.Packages { + if !vulCmdState.Details && !vulCmdState.Active && !vulCmdState.Fixable && !vulCmdState.Packages && vulCmdState.Severity != "" { mainBldr.WriteString( "Try adding '--details' to increase details shown about the vulnerability assessment.\n", ) @@ -737,11 +741,16 @@ func hostVulnHostDetailsToTable(assessment api.HostVulnHostAssessment) string { return mainBldr.String() } -func hostVulnCVEsTableForHostView(cves []api.HostVulnCVE) [][]string { - out := [][]string{} +func hostVulnCVEsTableForHostView(cves []api.HostVulnCVE) ([][]string, string) { + var ( + total = 0 + out [][]string + ) + for _, cve := range cves { for _, pkg := range cve.Packages { - // if the user wants to show only vulnerabilities of acive packages + total++ + // if the user wants to show only vulnerabilities of active packages if vulCmdState.Active && pkg.PackageStatus == "" { continue } @@ -750,6 +759,10 @@ func hostVulnCVEsTableForHostView(cves []api.HostVulnCVE) [][]string { continue } + if vulCmdState.Severity != "" && filterSeverity(pkg.Severity, vulCmdState.Severity) { + continue + } + out = append(out, []string{ cve.ID, pkg.Severity, @@ -768,7 +781,12 @@ func hostVulnCVEsTableForHostView(cves []api.HostVulnCVE) [][]string { return severityOrder(out[i][1]) < severityOrder(out[j][1]) }) - return out + if len(out) < total { + showing := total - len(out) + return out, fmt.Sprintf("\n %v of %v cve(s) showing \n", showing, total) + } + + return out, "" } func getNamespaceFromHostVuln(cves []api.HostVulnCVE) string { diff --git a/cli/cmd/vuln_host_test.go b/cli/cmd/vuln_host_test.go index 5c7d8e745..835352f4e 100644 --- a/cli/cmd/vuln_host_test.go +++ b/cli/cmd/vuln_host_test.go @@ -19,9 +19,10 @@ package cmd import ( + "testing" + "github.com/lacework/go-sdk/api" "github.com/stretchr/testify/assert" - "testing" ) func TestListCvesFilterSeverity(t *testing.T) { @@ -35,6 +36,17 @@ func TestListCvesFilterSeverity(t *testing.T) { assert.Equal(t, output, "\n 1 of 2 cve(s) showing \n") } +func TestShowAssessmentFilterSeverity(t *testing.T) { + vulCmdState.Severity = "critical" + defer clearVulnFilters() + + mockCves := []api.HostVulnCVE{mockCveOne} + result, output := hostVulnCVEsTableForHostView(mockCves) + + assert.Equal(t, len(result), 1) + assert.Equal(t, output, "\n 1 of 2 cve(s) showing \n") +} + func clearVulnFilters() { vulCmdState.Severity = "" } @@ -59,20 +71,19 @@ var mockCveOne = api.HostVulnCVE{ } var mockPackageOne = api.HostVulnPackage{ - Name: "test", - Namespace: "rhel:8", - Severity: "High", - Status: "Active", - HostCount: "1", - FixAvailable: "1", + Name: "test", + Namespace: "rhel:8", + Severity: "High", + Status: "Active", + HostCount: "1", + FixAvailable: "1", } var mockPackageTwo = api.HostVulnPackage{ - Name: "test2", - Namespace: "rhel:8", - Severity: "Critical", - Status: "Active", - HostCount: "1", - FixAvailable: "1", + Name: "test2", + Namespace: "rhel:8", + Severity: "Critical", + Status: "Active", + HostCount: "1", + FixAvailable: "1", } - From ad04c0ed4c35bfc4a976555033494b0cb43fd82c Mon Sep 17 00:00:00 2001 From: Darren Murray Date: Wed, 7 Apr 2021 15:03:38 +0100 Subject: [PATCH 05/15] feat: Filter by severity when packages flag is enabled Signed-off-by: Darren Murray --- cli/cmd/vuln_container.go | 2 + cli/cmd/vuln_host.go | 118 +++++++++++++++++++++++--------------- 2 files changed, 75 insertions(+), 45 deletions(-) diff --git a/cli/cmd/vuln_container.go b/cli/cmd/vuln_container.go index 8cb8f2d15..582a14e36 100644 --- a/cli/cmd/vuln_container.go +++ b/cli/cmd/vuln_container.go @@ -629,6 +629,8 @@ type packageTable struct { packageName string currentVersion string fixVersion string + packageStatus string + hostCount int } func aggregatePackages(slice []packageTable, s packageTable) []packageTable { diff --git a/cli/cmd/vuln_host.go b/cli/cmd/vuln_host.go index b4107d610..fda8d2ac2 100644 --- a/cli/cmd/vuln_host.go +++ b/cli/cmd/vuln_host.go @@ -229,12 +229,16 @@ with fixes: } if vulCmdState.Packages { + packages, filtered := hostVulnPackagesTable(response.CVEs, true) cli.OutputHuman( renderSimpleTable( []string{"CVE Count", "Severity", "Package", "Current Version", "Fix Version", "Pkg Status", "Hosts"}, - hostVulnPackagesTable(response.CVEs, true), + packages, ), ) + if filtered != "" { + cli.OutputHuman(filtered) + } return nil } @@ -506,58 +510,73 @@ func hostVulnSummaryFromHostDetail(hostVulnSummary *api.HostVulnCveSummary) (str return strings.Join(summary, " "), true } -func hostVulnPackagesTable(cves []api.HostVulnCVE, withHosts bool) [][]string { - out := [][]string{} +func aggregatePackagesWithHosts(slice []packageTable, s packageTable, host bool) []packageTable { + for i, item := range slice { + if item.packageName == s.packageName && + item.currentVersion == s.currentVersion && + item.severity == s.severity && + item.fixVersion == s.fixVersion && + item.packageStatus == s.packageStatus { + slice[i].cveCount++ + if host { + slice[i].hostCount++ + } + return slice + } + } + return append(slice, s) +} + +func hostVulnPackagesTable(cves []api.HostVulnCVE, withHosts bool) ([][]string, string) { + var ( + out [][]string + filteredPackages []packageTable + aggregatedPackages []packageTable + ) + for _, cve := range cves { for _, pkg := range cve.Packages { - // if the user wants to show only vulnerabilities of acive packages + pack := packageTable{ + cveCount: 1, + severity: strings.Title(pkg.Severity), + packageName: pkg.Name, + currentVersion: pkg.Version, + fixVersion: pkg.FixedVersion, + packageStatus: pkg.PackageStatus, + } + if vulCmdState.Active && pkg.PackageStatus == "" { + filteredPackages = aggregatePackagesWithHosts(filteredPackages, pack, withHosts) continue } + if vulCmdState.Fixable && pkg.FixedVersion == "" { + filteredPackages = aggregatePackagesWithHosts(filteredPackages, pack, withHosts) continue } - added := false - for i := range out { - if out[i][1] == strings.Title(pkg.Severity) && - out[i][2] == pkg.Name && - out[i][3] == pkg.Version && - out[i][4] == pkg.FixedVersion && - out[i][5] == pkg.PackageStatus { - - if countCVEs, err := strconv.Atoi(out[i][0]); err == nil { - out[i][0] = fmt.Sprintf("%d", (countCVEs + 1)) - added = true - } - - if withHosts { - if countHosts, err := strconv.Atoi(out[i][6]); err == nil { - prevCount := stringToInt(pkg.HostCount) - out[i][6] = fmt.Sprintf("%d", (countHosts + prevCount)) - added = true - } - } - + if vulCmdState.Severity != "" { + if filterSeverity(pkg.Severity, vulCmdState.Severity) { + filteredPackages = aggregatePackagesWithHosts(filteredPackages, pack, withHosts) + continue } } + aggregatedPackages = aggregatePackagesWithHosts(aggregatedPackages, pack, withHosts) + } + } - if added { - continue - } + for _, p := range aggregatedPackages { + out = append(out, []string{ + strconv.Itoa(p.cveCount), + p.severity, + p.packageName, + p.currentVersion, + p.fixVersion, + p.packageStatus, + }) - row := []string{ - "1", - strings.Title(pkg.Severity), - pkg.Name, - pkg.Version, - pkg.FixedVersion, - pkg.PackageStatus, - } - if withHosts { - row = append(row, pkg.HostCount) - } - out = append(out, row) + if p.hostCount > 0 { + out = append(out, []string{strconv.Itoa(p.hostCount)}) } } @@ -566,7 +585,12 @@ func hostVulnPackagesTable(cves []api.HostVulnCVE, withHosts bool) [][]string { return severityOrder(out[i][1]) < severityOrder(out[j][1]) }) - return out + if len(filteredPackages) > 0 { + filteredOutput := fmt.Sprintf("%v of %v packages showing \n", len(out), len(aggregatedPackages)+len(filteredPackages)) + return out, filteredOutput + } + + return out, "" } func hostVulnCVEsTable(cves []api.HostVulnCVE) ([][]string, string) { @@ -688,12 +712,16 @@ func hostVulnHostDetailsToTable(assessment api.HostVulnHostAssessment) string { mainBldr.WriteString("\n") if vulCmdState.Details || vulCmdState.Fixable || vulCmdState.Packages || vulCmdState.Active || vulCmdState.Severity != "" { if vulCmdState.Packages { + packages, filtered := hostVulnPackagesTable(assessment.CVEs, false) mainBldr.WriteString( renderSimpleTable( []string{"CVE Count", "Severity", "Package", "Current Version", "Fix Version", "Pkg Status"}, - hostVulnPackagesTable(assessment.CVEs, false), + packages, ), ) + if filtered != "" { + mainBldr.WriteString(filtered) + } } else { rows, filtered := hostVulnCVEsTableForHostView(assessment.CVEs) @@ -724,7 +752,7 @@ func hostVulnHostDetailsToTable(assessment api.HostVulnHostAssessment) string { mainBldr.WriteString("\n") } - if !vulCmdState.Details && !vulCmdState.Active && !vulCmdState.Fixable && !vulCmdState.Packages && vulCmdState.Severity != "" { + if !vulCmdState.Details && !vulCmdState.Active && !vulCmdState.Fixable && !vulCmdState.Packages && vulCmdState.Severity == "" { mainBldr.WriteString( "Try adding '--details' to increase details shown about the vulnerability assessment.\n", ) @@ -743,8 +771,8 @@ func hostVulnHostDetailsToTable(assessment api.HostVulnHostAssessment) string { func hostVulnCVEsTableForHostView(cves []api.HostVulnCVE) ([][]string, string) { var ( - total = 0 - out [][]string + total = 0 + out [][]string ) for _, cve := range cves { From b90ac84c4453a3df0e20abe99b3371b6527c34a5 Mon Sep 17 00:00:00 2001 From: Darren Murray Date: Wed, 7 Apr 2021 15:33:03 +0100 Subject: [PATCH 06/15] test: Add additional vuln host severity filter tests Signed-off-by: Darren Murray --- cli/cmd/vuln_host.go | 2 +- cli/cmd/vuln_host_test.go | 12 ++++++++++++ integration/host_vulnerability_test.go | 22 ++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/cli/cmd/vuln_host.go b/cli/cmd/vuln_host.go index fda8d2ac2..b53e6a276 100644 --- a/cli/cmd/vuln_host.go +++ b/cli/cmd/vuln_host.go @@ -586,7 +586,7 @@ func hostVulnPackagesTable(cves []api.HostVulnCVE, withHosts bool) ([][]string, }) if len(filteredPackages) > 0 { - filteredOutput := fmt.Sprintf("%v of %v packages showing \n", len(out), len(aggregatedPackages)+len(filteredPackages)) + filteredOutput := fmt.Sprintf("%v of %v package(s) showing \n", len(out), len(aggregatedPackages)+len(filteredPackages)) return out, filteredOutput } diff --git a/cli/cmd/vuln_host_test.go b/cli/cmd/vuln_host_test.go index 835352f4e..476d4a14e 100644 --- a/cli/cmd/vuln_host_test.go +++ b/cli/cmd/vuln_host_test.go @@ -47,6 +47,18 @@ func TestShowAssessmentFilterSeverity(t *testing.T) { assert.Equal(t, output, "\n 1 of 2 cve(s) showing \n") } +func TestShowAssessmentFilterSeverityWithPackages(t *testing.T) { + vulCmdState.Severity = "critical" + vulCmdState.Packages = true + defer clearVulnFilters() + + mockCves := []api.HostVulnCVE{mockCveOne} + result, output := hostVulnPackagesTable(mockCves, true) + + assert.Equal(t, len(result), 1) + assert.Equal(t, output, "1 of 2 package(s) showing \n") +} + func clearVulnFilters() { vulCmdState.Severity = "" } diff --git a/integration/host_vulnerability_test.go b/integration/host_vulnerability_test.go index 112adf75b..91b33a8ae 100644 --- a/integration/host_vulnerability_test.go +++ b/integration/host_vulnerability_test.go @@ -293,3 +293,25 @@ func TestHostVulnerabilityFailOnFixableWithJson(t *testing.T) { "ERROR ENFORCE POLICY: fixable vulnerabilities found (exit code: 9)", "STDERR does not match") } + +func TestHostVulnerabilityListCvesFilterSeverity(t *testing.T) { + out, err, exitcode := LaceworkCLIWithTOMLConfig("vulnerability", "host", "list-cves", "--severity", "high") + + assert.Empty(t, err.String(), "STDERR should be empty") + assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") + + showAssessmentOutput := out.String() + assert.Contains(t, showAssessmentOutput, "cve(s) showing", + "Filtered assessment output should contain filtered result ") +} + +func TestHostVulnerabilityAssessmentFilterSeverity(t *testing.T) { + out, err, exitcode := LaceworkCLIWithTOMLConfig("vulnerability", "host", "show-assessment", machineID, "--severity", "high") + + assert.Empty(t, err.String(), "STDERR should be empty") + assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") + + showAssessmentOutput := out.String() + assert.Contains(t, showAssessmentOutput, "cve(s) showing", + "Filtered assessment output should contain filtered result ") +} From 42ba24d885f86ba2867190f27155429b86597ae1 Mon Sep 17 00:00:00 2001 From: Darren Murray Date: Wed, 7 Apr 2021 16:41:14 +0100 Subject: [PATCH 07/15] fix: Host count Signed-off-by: Darren Murray --- cli/cmd/vuln_host.go | 9 ++++----- integration/host_vulnerability_test.go | 11 +++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/cli/cmd/vuln_host.go b/cli/cmd/vuln_host.go index b53e6a276..d4c889149 100644 --- a/cli/cmd/vuln_host.go +++ b/cli/cmd/vuln_host.go @@ -566,18 +566,17 @@ func hostVulnPackagesTable(cves []api.HostVulnCVE, withHosts bool) ([][]string, } for _, p := range aggregatedPackages { - out = append(out, []string{ + output := []string{ strconv.Itoa(p.cveCount), p.severity, p.packageName, p.currentVersion, p.fixVersion, - p.packageStatus, - }) - + p.packageStatus} if p.hostCount > 0 { - out = append(out, []string{strconv.Itoa(p.hostCount)}) + output = append(output, strconv.Itoa(p.hostCount)) } + out = append(out, output) } // order by severity diff --git a/integration/host_vulnerability_test.go b/integration/host_vulnerability_test.go index 91b33a8ae..2f73bbf15 100644 --- a/integration/host_vulnerability_test.go +++ b/integration/host_vulnerability_test.go @@ -315,3 +315,14 @@ func TestHostVulnerabilityAssessmentFilterSeverity(t *testing.T) { assert.Contains(t, showAssessmentOutput, "cve(s) showing", "Filtered assessment output should contain filtered result ") } + +func TestHostVulnerabilityListCvesFilterSeverityWithPackages(t *testing.T) { + out, err, exitcode := LaceworkCLIWithTOMLConfig("vulnerability", "host", "list-cves", "--severity", "high", "--packages") + + assert.Empty(t, err.String(), "STDERR should be empty") + assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") + + showAssessmentOutput := out.String() + assert.Contains(t, showAssessmentOutput, "package(s) showing", + "Filtered assessment output should contain filtered result ") +} From 9676f75cde0a26df1271da2a7f5b636cfefbd64d Mon Sep 17 00:00:00 2001 From: Darren Murray Date: Wed, 7 Apr 2021 16:58:43 +0100 Subject: [PATCH 08/15] fix: Host count Signed-off-by: Darren Murray --- cli/cmd/vuln_host.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cli/cmd/vuln_host.go b/cli/cmd/vuln_host.go index d4c889149..88e3b2ec5 100644 --- a/cli/cmd/vuln_host.go +++ b/cli/cmd/vuln_host.go @@ -544,6 +544,9 @@ func hostVulnPackagesTable(cves []api.HostVulnCVE, withHosts bool) ([][]string, fixVersion: pkg.FixedVersion, packageStatus: pkg.PackageStatus, } + if withHosts { + pack.hostCount = 1 + } if vulCmdState.Active && pkg.PackageStatus == "" { filteredPackages = aggregatePackagesWithHosts(filteredPackages, pack, withHosts) From 73dcbfd27b220c218bcff8adcdc9219ee38dfa6e Mon Sep 17 00:00:00 2001 From: Darren Murray Date: Wed, 7 Apr 2021 20:08:39 +0100 Subject: [PATCH 09/15] fix: Remove negligible severity status Signed-off-by: Darren Murray --- cli/cmd/event.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cli/cmd/event.go b/cli/cmd/event.go index 8d3431d58..d69dffee5 100644 --- a/cli/cmd/event.go +++ b/cli/cmd/event.go @@ -843,8 +843,6 @@ func severityToProperTypes(severity string) (int, string) { return 4, "Low" case "5", "info": return 5, "Info" - case "6", "negligible": - return 6, "Negligible" default: return 0, "Unknown" } From ec202252b004e6fecd74c11201cbcff90448bd58 Mon Sep 17 00:00:00 2001 From: Darren Murray Date: Thu, 8 Apr 2021 10:36:23 +0100 Subject: [PATCH 10/15] fix: Output when no vulns are found with severity flag Signed-off-by: Darren Murray --- cli/cmd/vuln_host.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cli/cmd/vuln_host.go b/cli/cmd/vuln_host.go index 88e3b2ec5..46476535c 100644 --- a/cli/cmd/vuln_host.go +++ b/cli/cmd/vuln_host.go @@ -851,6 +851,11 @@ func buildHostVulnCVEsToTableError() string { if vulCmdState.Fixable { msg = fmt.Sprintf("%s fixable", msg) } + + if vulCmdState.Severity != "" { + msg = fmt.Sprintf("%s %s", msg, vulCmdState.Severity) + } + msg = fmt.Sprintf("%s vulnerabilities", msg) if vulCmdState.Active { From 3687c1a242104a43b572f509aa21a87a4312cacb Mon Sep 17 00:00:00 2001 From: Darren <75614232+dmurray-lacework@users.noreply.github.com> Date: Fri, 9 Apr 2021 09:22:30 +0100 Subject: [PATCH 11/15] fix: Use correct types for formatted string Co-authored-by: Salim Afiune --- cli/cmd/vuln_host.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cmd/vuln_host.go b/cli/cmd/vuln_host.go index 46476535c..68498f653 100644 --- a/cli/cmd/vuln_host.go +++ b/cli/cmd/vuln_host.go @@ -610,7 +610,7 @@ func hostVulnCVEsTable(cves []api.HostVulnCVE) ([][]string, string) { if filteredCves > 0 { showing := totalCves - filteredCves - return out, fmt.Sprintf("\n %v of %v cve(s) showing \n", showing, totalCves) + return out, fmt.Sprintf("\n%d of %d cve(s) showing \n", showing, totalCves) } return out, "" From 80293c50018a79d091c0dca32d44586bd2da75e2 Mon Sep 17 00:00:00 2001 From: Darren <75614232+dmurray-lacework@users.noreply.github.com> Date: Fri, 9 Apr 2021 09:22:41 +0100 Subject: [PATCH 12/15] fix: Use correct types for formatted string Co-authored-by: Salim Afiune --- cli/cmd/vuln_host.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cmd/vuln_host.go b/cli/cmd/vuln_host.go index 68498f653..327f05335 100644 --- a/cli/cmd/vuln_host.go +++ b/cli/cmd/vuln_host.go @@ -813,7 +813,7 @@ func hostVulnCVEsTableForHostView(cves []api.HostVulnCVE) ([][]string, string) { if len(out) < total { showing := total - len(out) - return out, fmt.Sprintf("\n %v of %v cve(s) showing \n", showing, total) + return out, fmt.Sprintf("\n%d of %d cve(s) showing \n", showing, total) } return out, "" From 287e42cdb9c09d2a2f8059cf4c047ea6fd7b75ad Mon Sep 17 00:00:00 2001 From: Darren <75614232+dmurray-lacework@users.noreply.github.com> Date: Fri, 9 Apr 2021 09:22:50 +0100 Subject: [PATCH 13/15] fix: Use correct types for formatted string Co-authored-by: Salim Afiune --- cli/cmd/vuln_host.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cmd/vuln_host.go b/cli/cmd/vuln_host.go index 327f05335..eca05c669 100644 --- a/cli/cmd/vuln_host.go +++ b/cli/cmd/vuln_host.go @@ -588,7 +588,7 @@ func hostVulnPackagesTable(cves []api.HostVulnCVE, withHosts bool) ([][]string, }) if len(filteredPackages) > 0 { - filteredOutput := fmt.Sprintf("%v of %v package(s) showing \n", len(out), len(aggregatedPackages)+len(filteredPackages)) + filteredOutput := fmt.Sprintf("%d of %d package(s) showing \n", len(out), len(aggregatedPackages)+len(filteredPackages)) return out, filteredOutput } From 6ebb2b7707ff2d732014762be0e9361c89d455d7 Mon Sep 17 00:00:00 2001 From: Darren Murray Date: Fri, 9 Apr 2021 15:00:55 +0100 Subject: [PATCH 14/15] test: fix unit test Signed-off-by: Darren Murray --- cli/cmd/vuln_host_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cmd/vuln_host_test.go b/cli/cmd/vuln_host_test.go index 476d4a14e..981bceacb 100644 --- a/cli/cmd/vuln_host_test.go +++ b/cli/cmd/vuln_host_test.go @@ -44,7 +44,7 @@ func TestShowAssessmentFilterSeverity(t *testing.T) { result, output := hostVulnCVEsTableForHostView(mockCves) assert.Equal(t, len(result), 1) - assert.Equal(t, output, "\n 1 of 2 cve(s) showing \n") + assert.Equal(t, output, "\n1 of 2 cve(s) showing \n") } func TestShowAssessmentFilterSeverityWithPackages(t *testing.T) { From ee9c596a526455901b3aab8116da96f3469c3e7f Mon Sep 17 00:00:00 2001 From: Darren Murray Date: Fri, 9 Apr 2021 15:21:47 +0100 Subject: [PATCH 15/15] test: fix unit test Signed-off-by: Darren Murray --- cli/cmd/vuln_host_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cmd/vuln_host_test.go b/cli/cmd/vuln_host_test.go index 981bceacb..f2ec1d3ef 100644 --- a/cli/cmd/vuln_host_test.go +++ b/cli/cmd/vuln_host_test.go @@ -33,7 +33,7 @@ func TestListCvesFilterSeverity(t *testing.T) { result, output := hostVulnCVEsTable(mockCves) assert.Equal(t, len(result), 1) - assert.Equal(t, output, "\n 1 of 2 cve(s) showing \n") + assert.Equal(t, output, "\n1 of 2 cve(s) showing \n") } func TestShowAssessmentFilterSeverity(t *testing.T) {