From 87fda76f38a3a6939a87828c3df0c5ac2cf7fce3 Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Tue, 10 Jun 2025 11:22:35 +0600 Subject: [PATCH] fix(report): don't panic when report contains vulns, but doesn't contain packages for `table` format (#8549) --- pkg/report/table/summary.go | 23 ++++++-- pkg/report/table/summary_test.go | 91 ++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/pkg/report/table/summary.go b/pkg/report/table/summary.go index e5a6fd25ac..6307ccd00f 100644 --- a/pkg/report/table/summary.go +++ b/pkg/report/table/summary.go @@ -171,7 +171,7 @@ func (r *summaryRenderer) Render(report types.Report) { t.SetHeaders(headers...) t.SetAlignment(alignments...) - for _, result := range splitAggregatedPackages(report.Results) { + for _, result := range r.splitAggregatedPackages(report.Results) { resultType := string(result.Type) switch result.Class { case types.ClassSecret: @@ -232,7 +232,7 @@ func (r *summaryRenderer) showEmptyResultsWarning() { // splitAggregatedPackages splits aggregated packages into different results with path as target. // Other results will be returned as is. -func splitAggregatedPackages(results types.Results) types.Results { +func (r *summaryRenderer) splitAggregatedPackages(results types.Results) types.Results { var newResults types.Results for _, result := range results { @@ -243,14 +243,22 @@ func splitAggregatedPackages(results types.Results) types.Results { continue } - newResults = append(newResults, splitAggregatedVulns(result)...) + newResults = append(newResults, r.splitAggregatedVulns(result)...) newResults = append(newResults, splitAggregatedLicenses(result)...) } return newResults } -func splitAggregatedVulns(result types.Result) types.Results { +func (r *summaryRenderer) splitAggregatedVulns(result types.Result) types.Results { + // Handle case when result doesn't contain Package + // cf. https://github.com/aquasecurity/trivy/discussions/8537 + if len(result.Packages) == 0 && len(result.Vulnerabilities) > 0 { + r.logger.Warn("Packages not found unexpectedly", log.String("target", result.Target)) + return types.Results{ + result, + } + } // Save packages to display them in the table even if no vulnerabilities were found resultMap := lo.SliceToMap(result.Packages, func(pkg ftypes.Package) (string, *types.Result) { filePath := rootJarFromPath(pkg.FilePath) @@ -262,7 +270,12 @@ func splitAggregatedVulns(result types.Result) types.Results { }) for _, vuln := range result.Vulnerabilities { pkgPath := rootJarFromPath(vuln.PkgPath) - resultMap[pkgPath].Vulnerabilities = append(resultMap[pkgPath].Vulnerabilities, vuln) + + if res, ok := resultMap[pkgPath]; !ok { + r.logger.Warn("Package not found unexpectedly", log.String("package_path", pkgPath), log.String("vuln_id", vuln.VulnerabilityID)) + } else { + res.Vulnerabilities = append(res.Vulnerabilities, vuln) + } } newResults := lo.Values(resultMap) sort.Slice(newResults, func(i, j int) bool { diff --git a/pkg/report/table/summary_test.go b/pkg/report/table/summary_test.go index 341cde1152..da74ad045b 100644 --- a/pkg/report/table/summary_test.go +++ b/pkg/report/table/summary_test.go @@ -59,6 +59,49 @@ var ( }, } + jarVulnsWithoutPackages = types.Result{ + Target: "Java", + Class: types.ClassLangPkg, + Type: ftypes.Jar, + Vulnerabilities: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2022-42003", + PkgName: "com.fasterxml.jackson.core:jackson-databind", + PkgPath: "app/jackson-databind-2.13.4.1.jar", + }, + { + VulnerabilityID: "CVE-2021-44832", + PkgName: "org.apache.logging.log4j:log4j-core", + PkgPath: "app/jackson-databind-2.13.4.1.jar/nested/app2/log4j-core-2.17.0.jar", + }, + }, + } + + jarVulnsWithMissedPackage = types.Result{ + Target: "Java", + Class: types.ClassLangPkg, + Type: ftypes.Jar, + Packages: []ftypes.Package{ + { + Name: "com.fasterxml.jackson.core:jackson-databind", + Version: "2.13.4.1", + FilePath: "app/jackson-databind-2.13.4.1.jar", + }, + }, + Vulnerabilities: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2022-42003", + PkgName: "com.fasterxml.jackson.core:jackson-databind", + PkgPath: "app/jackson-databind-2.13.4.1.jar", + }, + { + VulnerabilityID: "CVE-2021-44832", + PkgName: "org.apache.logging.log4j:log4j-core", + PkgPath: "app/log4j-core-2.17.0.jar", + }, + }, + } + npmVulns = types.Result{ Target: "Node.js", Class: types.ClassLangPkg, @@ -285,6 +328,54 @@ Legend: - '-': Not scanned - '0': Clean (no security findings detected) +`, + }, + { + name: "happy path when Result doesn't include Packages", + scanners: []types.Scanner{ + types.VulnerabilityScanner, + }, + report: types.Report{ + Results: []types.Result{ + jarVulnsWithoutPackages, + }, + }, + want: ` +Report Summary + +┌────────┬──────┬─────────────────┐ +│ Target │ Type │ Vulnerabilities │ +├────────┼──────┼─────────────────┤ +│ Java │ jar │ 2 │ +└────────┴──────┴─────────────────┘ +Legend: +- '-': Not scanned +- '0': Clean (no security findings detected) + +`, + }, + { + name: "happy path when Result doesn't include all required Packages", + scanners: []types.Scanner{ + types.VulnerabilityScanner, + }, + report: types.Report{ + Results: []types.Result{ + jarVulnsWithMissedPackage, + }, + }, + want: ` +Report Summary + +┌───────────────────────────────────┬──────┬─────────────────┐ +│ Target │ Type │ Vulnerabilities │ +├───────────────────────────────────┼──────┼─────────────────┤ +│ app/jackson-databind-2.13.4.1.jar │ jar │ 1 │ +└───────────────────────────────────┴──────┴─────────────────┘ +Legend: +- '-': Not scanned +- '0': Clean (no security findings detected) + `, }, {