From ff62336fe08a6ae0196216fcf7b4a3be042c1cf7 Mon Sep 17 00:00:00 2001 From: knqyf263 Date: Wed, 25 Dec 2024 14:02:39 +0400 Subject: [PATCH 01/11] feat(cli): add report summary Signed-off-by: knqyf263 --- pkg/report/table/summary.go | 86 +++++++++++++++++++++++++++++++++++++ pkg/report/table/table.go | 75 ++++++++++++++++++++++++++++++++ pkg/report/writer.go | 1 + 3 files changed, 162 insertions(+) create mode 100644 pkg/report/table/summary.go diff --git a/pkg/report/table/summary.go b/pkg/report/table/summary.go new file mode 100644 index 000000000000..e9bffc242050 --- /dev/null +++ b/pkg/report/table/summary.go @@ -0,0 +1,86 @@ +package table + +import ( + "github.com/aquasecurity/table" + "github.com/aquasecurity/trivy/pkg/types" +) + +type Scanner interface { + Header() string + Alignment() table.Alignment + + // Count returns the number of findings, but -1 if the scanner is not applicable + Count(result types.Result) int +} + +func NewScanner(scanner types.Scanner) Scanner { + switch scanner { + case types.VulnerabilityScanner: + return VulnerabilityScanner{} + case types.MisconfigScanner: + return MisconfigScanner{} + case types.SecretScanner: + return SecretScanner{} + case types.LicenseScanner: + return LicenseScanner{} + } + return nil +} + +type scannerAlignment struct{} + +func (s scannerAlignment) Alignment() table.Alignment { + return table.AlignCenter +} + +type VulnerabilityScanner struct{ scannerAlignment } + +func (s VulnerabilityScanner) Header() string { + return "Vulnerabilities" +} + +func (s VulnerabilityScanner) Count(result types.Result) int { + if result.Class == types.ClassOSPkg || result.Class == types.ClassLangPkg { + return len(result.Vulnerabilities) + } + return -1 +} + +type MisconfigScanner struct{ scannerAlignment } + +func (s MisconfigScanner) Header() string { + return "Misconfigurations" +} + +func (s MisconfigScanner) Count(result types.Result) int { + if result.Class == types.ClassConfig { + return len(result.Misconfigurations) + } + return -1 +} + +type SecretScanner struct{ scannerAlignment } + +func (s SecretScanner) Header() string { + return "Secrets" +} + +func (s SecretScanner) Count(result types.Result) int { + if result.Class == types.ClassSecret { + return len(result.Secrets) + } + return -1 +} + +type LicenseScanner struct{ scannerAlignment } + +func (s LicenseScanner) Header() string { + return "Licenses" +} + +func (s LicenseScanner) Count(result types.Result) int { + if result.Class == types.ClassLicense { + return len(result.Licenses) + } + return -1 +} diff --git a/pkg/report/table/table.go b/pkg/report/table/table.go index 8bfa75922013..08f7651b5173 100644 --- a/pkg/report/table/table.go +++ b/pkg/report/table/table.go @@ -10,6 +10,8 @@ import ( "strings" "github.com/fatih/color" + "github.com/samber/lo" + "golang.org/x/xerrors" "github.com/aquasecurity/table" "github.com/aquasecurity/tml" @@ -29,6 +31,7 @@ var ( // Writer implements Writer and output in tabular form type Writer struct { + Scanners types.Scanners Severities []dbTypes.Severity Output io.Writer @@ -53,6 +56,13 @@ type Renderer interface { // Write writes the result on standard output func (tw Writer) Write(_ context.Context, report types.Report) error { + if !tw.isOutputToTerminal() { + tml.DisableFormatting() + } + + if err := tw.renderSummary(report); err != nil { + return xerrors.Errorf("failed to render summary: %w", err) + } for _, result := range report.Results { // Not display a table of custom resources @@ -64,6 +74,60 @@ func (tw Writer) Write(_ context.Context, report types.Report) error { return nil } +func (tw Writer) renderSummary(report types.Report) error { + // Fprintln has a bug + if err := tml.Fprintf(tw.Output, "\nReport Summary\n\n"); err != nil { + return err + } + + t := newTableWriter(tw.Output, tw.isOutputToTerminal()) + t.SetAutoMerge(false) + t.SetColumnMaxWidth(80) + + var scanners []Scanner + for _, scanner := range tw.Scanners { + s := NewScanner(scanner) + if lo.IsNil(s) { + continue + } + scanners = append(scanners, s) + } + + headers := []string{ + "Target", + "Type", + } + alignments := []table.Alignment{ + table.AlignLeft, + table.AlignCenter, + } + for _, scanner := range scanners { + headers = append(headers, scanner.Header()) + alignments = append(alignments, scanner.Alignment()) + } + t.SetHeaders(headers...) + t.SetAlignment(alignments...) + + for _, result := range report.Results { + resultType := string(result.Type) + if result.Class == types.ClassSecret { + resultType = "text" + } else if result.Class == types.ClassLicense { + resultType = "-" + } + rows := []string{ + result.Target, + resultType, + } + for _, scanner := range scanners { + rows = append(rows, tw.colorizeCount(scanner.Count(result))) + } + t.AddRows(rows) + } + t.Render() + return nil +} + func (tw Writer) write(result types.Result) { if result.IsEmpty() && result.Class != types.ClassOSPkg { return @@ -97,6 +161,17 @@ func (tw Writer) isOutputToTerminal() bool { return IsOutputToTerminal(tw.Output) } +func (tw Writer) colorizeCount(count int) string { + if count < 0 { + return "-" + } + sprintf := fmt.Sprintf + if count != 0 && tw.isOutputToTerminal() { + sprintf = color.New(color.FgHiRed).SprintfFunc() + } + return sprintf("%d", count) +} + func newTableWriter(output io.Writer, isTerminal bool) *table.Table { tableWriter := table.New(output) if isTerminal { // use ansi output if we're not piping elsewhere diff --git a/pkg/report/writer.go b/pkg/report/writer.go index f25d579d66ef..c8079fa0f969 100644 --- a/pkg/report/writer.go +++ b/pkg/report/writer.go @@ -45,6 +45,7 @@ func Write(ctx context.Context, report types.Report, option flag.Options) (err e switch option.Format { case types.FormatTable: writer = &table.Writer{ + Scanners: option.Scanners, Output: output, Severities: option.Severities, Tree: option.DependencyTree, From ca75fc90900dd483269f2c662850128292bd146f Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Thu, 16 Jan 2025 11:26:29 +0600 Subject: [PATCH 02/11] test: fix table tests --- pkg/report/table/table.go | 6 ++++ pkg/report/table/table_test.go | 55 ++++++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/pkg/report/table/table.go b/pkg/report/table/table.go index 08f7651b5173..6b8fd86a51e9 100644 --- a/pkg/report/table/table.go +++ b/pkg/report/table/table.go @@ -93,6 +93,12 @@ func (tw Writer) renderSummary(report types.Report) error { scanners = append(scanners, s) } + // It should be an impossible case. + // But it is possible when Trivy is used as a library. + if len(scanners) == 0 { + return xerrors.Errorf("unable to find scanners") + } + headers := []string{ "Target", "Type", diff --git a/pkg/report/table/table_test.go b/pkg/report/table/table_test.go index d52dda0dc232..0da0bd361b5b 100644 --- a/pkg/report/table/table_test.go +++ b/pkg/report/table/table_test.go @@ -16,15 +16,21 @@ import ( func TestWriter_Write(t *testing.T) { testCases := []struct { name string + scanners types.Scanners results types.Results - expectedOutput string + wantOutput string + wantError string includeNonFailures bool }{ { name: "vulnerability and custom resource", + scanners: types.Scanners{ + types.VulnerabilityScanner, + }, results: types.Results{ { Target: "test", + Type: ftypes.Jar, Class: types.ClassLangPkg, Vulnerabilities: []types.DetectedVulnerability{ { @@ -48,9 +54,17 @@ func TestWriter_Write(t *testing.T) { }, }, }, - expectedOutput: ` -test () -======= + wantOutput: ` +Report Summary + +┌────────┬──────┬─────────────────┐ +│ Target │ Type │ Vulnerabilities │ +├────────┼──────┼─────────────────┤ +│ test │ jar │ 1 │ +└────────┴──────┴─────────────────┘ + +test (jar) +========== Total: 1 (MEDIUM: 0, HIGH: 1) ┌─────────┬───────────────┬──────────┬──────────────┬───────────────────┬───────────────┬───────────────────────────────────────────┐ @@ -63,13 +77,36 @@ Total: 1 (MEDIUM: 0, HIGH: 1) }, { name: "no vulns", + scanners: types.Scanners{ + types.VulnerabilityScanner, + }, + results: types.Results{ + { + Target: "test", + Class: types.ClassLangPkg, + Type: ftypes.Jar, + }, + }, + wantOutput: ` +Report Summary + +┌────────┬──────┬─────────────────┐ +│ Target │ Type │ Vulnerabilities │ +├────────┼──────┼─────────────────┤ +│ test │ jar │ 0 │ +└────────┴──────┴─────────────────┘ +`, + }, + { + name: "no scanners", results: types.Results{ { Target: "test", Class: types.ClassLangPkg, + Type: ftypes.Jar, }, }, - expectedOutput: ``, + wantError: "unable to find scanners", }, } @@ -85,10 +122,16 @@ Total: 1 (MEDIUM: 0, HIGH: 1) dbTypes.SeverityHigh, dbTypes.SeverityMedium, }, + Scanners: tc.scanners, } err := writer.Write(nil, types.Report{Results: tc.results}) + if tc.wantError != "" { + require.Error(t, err) + return + } + require.NoError(t, err) - assert.Equal(t, tc.expectedOutput, tableWritten.String(), tc.name) + assert.Equal(t, tc.wantOutput, tableWritten.String(), tc.name) }) } } From ef269402e73eea8836c4388f981fb32ec926c678 Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Thu, 16 Jan 2025 11:59:10 +0600 Subject: [PATCH 03/11] feat: add `--no-summary` flag --- pkg/flag/report_flags.go | 17 +++++++++++++++++ pkg/flag/report_flags_test.go | 17 +++++++++++++++++ pkg/report/table/table.go | 9 +++++++-- pkg/report/table/table_test.go | 19 ++++++++++++++++++- pkg/report/writer.go | 1 + 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/pkg/flag/report_flags.go b/pkg/flag/report_flags.go index d69443e89547..52db8e050574 100644 --- a/pkg/flag/report_flags.go +++ b/pkg/flag/report_flags.go @@ -109,6 +109,11 @@ var ( ConfigName: "scan.show-suppressed", Usage: "[EXPERIMENTAL] show suppressed vulnerabilities", } + NoSummaryFlag = Flag[bool]{ + Name: "no-summary", + ConfigName: "no-summary", + Usage: "hide summary table", + } ) // ReportFlagGroup composes common printer flag structs @@ -128,6 +133,7 @@ type ReportFlagGroup struct { Severity *Flag[[]string] Compliance *Flag[string] ShowSuppressed *Flag[bool] + NoSummary *Flag[bool] } type ReportOptions struct { @@ -145,6 +151,7 @@ type ReportOptions struct { Severities []dbTypes.Severity Compliance spec.ComplianceSpec ShowSuppressed bool + NoSummary bool } func NewReportFlagGroup() *ReportFlagGroup { @@ -163,6 +170,7 @@ func NewReportFlagGroup() *ReportFlagGroup { Severity: SeverityFlag.Clone(), Compliance: ComplianceFlag.Clone(), ShowSuppressed: ShowSuppressedFlag.Clone(), + NoSummary: NoSummaryFlag.Clone(), } } @@ -186,6 +194,7 @@ func (f *ReportFlagGroup) Flags() []Flagger { f.Severity, f.Compliance, f.ShowSuppressed, + f.NoSummary, } } @@ -198,6 +207,7 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) { template := f.Template.Value() dependencyTree := f.DependencyTree.Value() listAllPkgs := f.ListAllPkgs.Value() + noSummary := f.NoSummary.Value() if template != "" { if format == "" { @@ -227,6 +237,12 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) { } } + // "--so-summary" option is available only with "--format table". + if noSummary && format != types.FormatTable { + noSummary = false + log.Warn(`"--no-summary" can be used only with "--format table".`) + } + cs, err := loadComplianceTypes(f.Compliance.Value()) if err != nil { return ReportOptions{}, xerrors.Errorf("unable to load compliance spec: %w", err) @@ -259,6 +275,7 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) { Severities: toSeverity(f.Severity.Value()), Compliance: cs, ShowSuppressed: f.ShowSuppressed.Value(), + NoSummary: noSummary, }, nil } diff --git a/pkg/flag/report_flags_test.go b/pkg/flag/report_flags_test.go index ab4baa53fbff..ded87412f590 100644 --- a/pkg/flag/report_flags_test.go +++ b/pkg/flag/report_flags_test.go @@ -32,6 +32,7 @@ func TestReportFlagGroup_ToOptions(t *testing.T) { compliance string debug bool pkgTypes string + noSummary bool } tests := []struct { name string @@ -115,6 +116,20 @@ func TestReportFlagGroup_ToOptions(t *testing.T) { ListAllPkgs: true, }, }, + { + name: "invalid option combination: --no-summary with --format json", + fields: fields{ + format: "json", + noSummary: true, + }, + wantLogs: []string{ + `"--no-summary" can be used only with "--format table".`, + }, + want: flag.ReportOptions{ + Format: "json", + NoSummary: false, + }, + }, { name: "happy path with output plugin args", fields: fields{ @@ -184,6 +199,7 @@ func TestReportFlagGroup_ToOptions(t *testing.T) { setValue(flag.OutputPluginArgFlag.ConfigName, tt.fields.outputPluginArgs) setValue(flag.SeverityFlag.ConfigName, tt.fields.severities) setValue(flag.ComplianceFlag.ConfigName, tt.fields.compliance) + setValue(flag.NoSummaryFlag.ConfigName, tt.fields.noSummary) // Assert options f := &flag.ReportFlagGroup{ @@ -199,6 +215,7 @@ func TestReportFlagGroup_ToOptions(t *testing.T) { OutputPluginArg: flag.OutputPluginArgFlag.Clone(), Severity: flag.SeverityFlag.Clone(), Compliance: flag.ComplianceFlag.Clone(), + NoSummary: flag.NoSummaryFlag.Clone(), } got, err := f.ToOptions() diff --git a/pkg/report/table/table.go b/pkg/report/table/table.go index 6b8fd86a51e9..ca9442d32616 100644 --- a/pkg/report/table/table.go +++ b/pkg/report/table/table.go @@ -41,6 +41,9 @@ type Writer struct { // Show suppressed findings ShowSuppressed bool + // Hide summary table + NoSummary bool + // For misconfigurations IncludeNonFailures bool Trace bool @@ -60,8 +63,10 @@ func (tw Writer) Write(_ context.Context, report types.Report) error { tml.DisableFormatting() } - if err := tw.renderSummary(report); err != nil { - return xerrors.Errorf("failed to render summary: %w", err) + if !tw.NoSummary { + if err := tw.renderSummary(report); err != nil { + return xerrors.Errorf("failed to render summary: %w", err) + } } for _, result := range report.Results { diff --git a/pkg/report/table/table_test.go b/pkg/report/table/table_test.go index 0da0bd361b5b..78f3ae63c126 100644 --- a/pkg/report/table/table_test.go +++ b/pkg/report/table/table_test.go @@ -17,6 +17,7 @@ func TestWriter_Write(t *testing.T) { testCases := []struct { name string scanners types.Scanners + noSummary bool results types.Results wantOutput string wantError string @@ -97,6 +98,21 @@ Report Summary └────────┴──────┴─────────────────┘ `, }, + { + name: "no summary", + scanners: types.Scanners{ + types.VulnerabilityScanner, + }, + noSummary: true, + results: types.Results{ + { + Target: "test", + Class: types.ClassLangPkg, + Type: ftypes.Jar, + }, + }, + wantOutput: ``, + }, { name: "no scanners", results: types.Results{ @@ -122,7 +138,8 @@ Report Summary dbTypes.SeverityHigh, dbTypes.SeverityMedium, }, - Scanners: tc.scanners, + Scanners: tc.scanners, + NoSummary: tc.noSummary, } err := writer.Write(nil, types.Report{Results: tc.results}) if tc.wantError != "" { diff --git a/pkg/report/writer.go b/pkg/report/writer.go index c8079fa0f969..38cb16fdb8a0 100644 --- a/pkg/report/writer.go +++ b/pkg/report/writer.go @@ -54,6 +54,7 @@ func Write(ctx context.Context, report types.Report, option flag.Options) (err e Trace: option.Trace, LicenseRiskThreshold: option.LicenseRiskThreshold, IgnoredLicenses: option.IgnoredLicenses, + NoSummary: option.NoSummary, } case types.FormatJSON: writer = &JSONWriter{ From 3ea064f9fdd1b59bdcbf601efa4b8c7a50558c7f Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Thu, 16 Jan 2025 15:13:30 +0600 Subject: [PATCH 04/11] test: add test for `renderSummary` --- pkg/report/table/table.go | 2 +- pkg/report/table/table_private_test.go | 227 +++++++++++++++++++++++++ 2 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 pkg/report/table/table_private_test.go diff --git a/pkg/report/table/table.go b/pkg/report/table/table.go index ca9442d32616..0da4b4a2c1c2 100644 --- a/pkg/report/table/table.go +++ b/pkg/report/table/table.go @@ -123,7 +123,7 @@ func (tw Writer) renderSummary(report types.Report) error { resultType := string(result.Type) if result.Class == types.ClassSecret { resultType = "text" - } else if result.Class == types.ClassLicense { + } else if result.Class == types.ClassLicense || result.Class == types.ClassLicenseFile { resultType = "-" } rows := []string{ diff --git a/pkg/report/table/table_private_test.go b/pkg/report/table/table_private_test.go new file mode 100644 index 000000000000..d732c298ac2d --- /dev/null +++ b/pkg/report/table/table_private_test.go @@ -0,0 +1,227 @@ +package table + +import ( + "bytes" + "testing" + + "github.com/aquasecurity/tml" + ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + osVuln = types.Result{ + Target: "test (alpine 3.20.3)", + Class: types.ClassOSPkg, + Type: ftypes.Alpine, + Vulnerabilities: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2024-9143", + PkgName: "libcrypto3", + }, + { + VulnerabilityID: "CVE-2024-9143", + PkgName: "libssl3", + }, + }, + } + jarVuln = 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/log4j-core-2.17.0.jar", + }, + }, + } + + noVuln = types.Result{ + Target: "requirements.txt", + Class: types.ClassLangPkg, + Type: ftypes.Pip, + } + + dockerfileMisconfig = types.Result{ + Target: "app/Dockerfile", + Class: types.ClassConfig, + Type: ftypes.Dockerfile, + Misconfigurations: []types.DetectedMisconfiguration{ + { + ID: "DS002", + }, + { + ID: "DS026", + }, + }, + } + secret = types.Result{ + Target: "/app/aws-secrets.txt", + Class: types.ClassSecret, + Secrets: []types.DetectedSecret{ + { + RuleID: "aws-access-key-id", + }, + }, + } + osLicense = types.Result{ + Target: "OS Packages", + Class: types.ClassLicense, + Licenses: []types.DetectedLicense{ + { + Name: "GPL-2.0-only", + }, + }, + } + + jarLicense = types.Result{ + Target: "Java", + Class: types.ClassLicense, + } + fileLicense = types.Result{ + Target: "Loose File License(s)", + Class: types.ClassLicenseFile, + } +) + +func Test_renderSummary(t *testing.T) { + tests := []struct { + name string + scanners types.Scanners + noSummary bool + report types.Report + want string + }{ + { + name: "happy path all scanners", + scanners: []types.Scanner{ + types.VulnerabilityScanner, + types.MisconfigScanner, + types.SecretScanner, + types.LicenseScanner, + }, + report: types.Report{ + Results: []types.Result{ + osVuln, + jarVuln, + dockerfileMisconfig, + secret, + osLicense, + jarLicense, + fileLicense, + }, + }, + want: ` +Report Summary + +┌───────────────────────┬────────────┬─────────────────┬───────────────────┬─────────┬──────────┐ +│ Target │ Type │ Vulnerabilities │ Misconfigurations │ Secrets │ Licenses │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ test (alpine 3.20.3) │ alpine │ 2 │ - │ - │ - │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ Java │ jar │ 2 │ - │ - │ - │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ app/Dockerfile │ dockerfile │ - │ 2 │ - │ - │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ /app/aws-secrets.txt │ text │ - │ - │ 1 │ - │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ OS Packages │ - │ - │ - │ - │ 1 │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ Java │ - │ - │ - │ - │ 0 │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ Loose File License(s) │ - │ - │ - │ - │ - │ +└───────────────────────┴────────────┴─────────────────┴───────────────────┴─────────┴──────────┘ +`, + }, + { + name: "happy path vuln scanner only", + scanners: []types.Scanner{ + types.VulnerabilityScanner, + }, + report: types.Report{ + Results: []types.Result{ + osVuln, + jarVuln, + }, + }, + want: ` +Report Summary + +┌──────────────────────┬────────┬─────────────────┐ +│ Target │ Type │ Vulnerabilities │ +├──────────────────────┼────────┼─────────────────┤ +│ test (alpine 3.20.3) │ alpine │ 2 │ +├──────────────────────┼────────┼─────────────────┤ +│ Java │ jar │ 2 │ +└──────────────────────┴────────┴─────────────────┘ +`, + }, + { + name: "happy path vuln scanner only without vulnerabilities", + scanners: []types.Scanner{ + types.VulnerabilityScanner, + }, + report: types.Report{ + Results: []types.Result{ + noVuln, + }, + }, + want: ` +Report Summary + +┌──────────────────┬──────┬─────────────────┐ +│ Target │ Type │ Vulnerabilities │ +├──────────────────┼──────┼─────────────────┤ +│ requirements.txt │ pip │ 0 │ +└──────────────────┴──────┴─────────────────┘ +`, + }, + { + name: "happy path vuln scanner only", + scanners: []types.Scanner{ + types.VulnerabilityScanner, + }, + report: types.Report{ + Results: []types.Result{ + osVuln, + jarVuln, + }, + }, + want: ` +Report Summary + +┌──────────────────────┬────────┬─────────────────┐ +│ Target │ Type │ Vulnerabilities │ +├──────────────────────┼────────┼─────────────────┤ +│ test (alpine 3.20.3) │ alpine │ 2 │ +├──────────────────────┼────────┼─────────────────┤ +│ Java │ jar │ 2 │ +└──────────────────────┴────────┴─────────────────┘ +`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tml.DisableFormatting() + tableWritten := bytes.Buffer{} + writer := Writer{ + Output: &tableWritten, + Scanners: tt.scanners, + NoSummary: tt.noSummary, + } + err := writer.renderSummary(tt.report) + require.NoError(t, err) + assert.Equal(t, tt.want, tableWritten.String()) + }) + } +} From 8b2acf7169a847ec2dd26102c4ac7f040b9604dc Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Thu, 16 Jan 2025 15:18:59 +0600 Subject: [PATCH 05/11] mage docs:generate --- docs/docs/references/configuration/cli/trivy_config.md | 1 + docs/docs/references/configuration/cli/trivy_convert.md | 1 + docs/docs/references/configuration/cli/trivy_filesystem.md | 1 + docs/docs/references/configuration/cli/trivy_image.md | 1 + docs/docs/references/configuration/cli/trivy_kubernetes.md | 1 + docs/docs/references/configuration/cli/trivy_repository.md | 1 + docs/docs/references/configuration/cli/trivy_rootfs.md | 1 + docs/docs/references/configuration/cli/trivy_sbom.md | 1 + docs/docs/references/configuration/cli/trivy_vm.md | 1 + docs/docs/references/configuration/config-file.md | 3 +++ 10 files changed, 12 insertions(+) diff --git a/docs/docs/references/configuration/cli/trivy_config.md b/docs/docs/references/configuration/cli/trivy_config.md index 7cc65a04e949..8ae258d3806c 100644 --- a/docs/docs/references/configuration/cli/trivy_config.md +++ b/docs/docs/references/configuration/cli/trivy_config.md @@ -36,6 +36,7 @@ trivy config [flags] DIR --k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0) --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") + --no-summary hide summary table -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. diff --git a/docs/docs/references/configuration/cli/trivy_convert.md b/docs/docs/references/configuration/cli/trivy_convert.md index d76d303e3b03..3c53495d27f5 100644 --- a/docs/docs/references/configuration/cli/trivy_convert.md +++ b/docs/docs/references/configuration/cli/trivy_convert.md @@ -27,6 +27,7 @@ trivy convert [flags] RESULT_JSON --ignore-policy string specify the Rego file path to evaluate each vulnerability --ignorefile string specify .trivyignore file (default ".trivyignore") --list-all-pkgs output all packages in the JSON report regardless of vulnerability + --no-summary hide summary table -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --report string specify a report format for the output (all,summary) (default "all") diff --git a/docs/docs/references/configuration/cli/trivy_filesystem.md b/docs/docs/references/configuration/cli/trivy_filesystem.md index dab87fc541fc..e3aa97ed4a3c 100644 --- a/docs/docs/references/configuration/cli/trivy_filesystem.md +++ b/docs/docs/references/configuration/cli/trivy_filesystem.md @@ -64,6 +64,7 @@ trivy filesystem [flags] PATH --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar + --no-summary hide summary table --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments diff --git a/docs/docs/references/configuration/cli/trivy_image.md b/docs/docs/references/configuration/cli/trivy_image.md index d9c312602190..b426621d0b02 100644 --- a/docs/docs/references/configuration/cli/trivy_image.md +++ b/docs/docs/references/configuration/cli/trivy_image.md @@ -82,6 +82,7 @@ trivy image [flags] IMAGE_NAME --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar + --no-summary hide summary table --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index 959532e1c806..4b5ee8fdc092 100644 --- a/docs/docs/references/configuration/cli/trivy_kubernetes.md +++ b/docs/docs/references/configuration/cli/trivy_kubernetes.md @@ -77,6 +77,7 @@ trivy kubernetes [flags] [CONTEXT] --list-all-pkgs output all packages in the JSON report regardless of vulnerability --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --no-progress suppress progress bar + --no-summary hide summary table --node-collector-imageref string indicate the image reference for the node-collector scan job (default "ghcr.io/aquasecurity/node-collector:0.3.1") --node-collector-namespace string specify the namespace in which the node-collector job should be deployed (default "trivy-temp") --offline-scan do not issue API requests to identify dependencies diff --git a/docs/docs/references/configuration/cli/trivy_repository.md b/docs/docs/references/configuration/cli/trivy_repository.md index 38ae6611b595..99f37c1ee5b5 100644 --- a/docs/docs/references/configuration/cli/trivy_repository.md +++ b/docs/docs/references/configuration/cli/trivy_repository.md @@ -63,6 +63,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar + --no-summary hide summary table --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments diff --git a/docs/docs/references/configuration/cli/trivy_rootfs.md b/docs/docs/references/configuration/cli/trivy_rootfs.md index 35cc54ff66a3..45c34d3a5a6a 100644 --- a/docs/docs/references/configuration/cli/trivy_rootfs.md +++ b/docs/docs/references/configuration/cli/trivy_rootfs.md @@ -66,6 +66,7 @@ trivy rootfs [flags] ROOTDIR --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar + --no-summary hide summary table --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments diff --git a/docs/docs/references/configuration/cli/trivy_sbom.md b/docs/docs/references/configuration/cli/trivy_sbom.md index 63f855b13335..5fc019e7fa06 100644 --- a/docs/docs/references/configuration/cli/trivy_sbom.md +++ b/docs/docs/references/configuration/cli/trivy_sbom.md @@ -45,6 +45,7 @@ trivy sbom [flags] SBOM_PATH --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [mirror.gcr.io/aquasec/trivy-java-db:1,ghcr.io/aquasecurity/trivy-java-db:1]) --list-all-pkgs output all packages in the JSON report regardless of vulnerability --no-progress suppress progress bar + --no-summary hide summary table --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments diff --git a/docs/docs/references/configuration/cli/trivy_vm.md b/docs/docs/references/configuration/cli/trivy_vm.md index 6fe9de30fcd8..5441bee3d9c4 100644 --- a/docs/docs/references/configuration/cli/trivy_vm.md +++ b/docs/docs/references/configuration/cli/trivy_vm.md @@ -58,6 +58,7 @@ trivy vm [flags] VM_IMAGE --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar + --no-summary hide summary table --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments diff --git a/docs/docs/references/configuration/config-file.md b/docs/docs/references/configuration/config-file.md index fe6332522ee0..41b3153e5462 100644 --- a/docs/docs/references/configuration/config-file.md +++ b/docs/docs/references/configuration/config-file.md @@ -521,6 +521,9 @@ ignorefile: ".trivyignore" # Same as '--list-all-pkgs' list-all-pkgs: false +# Same as '--no-summary' +no-summary: false + # Same as '--output' output: "" From 751823ab5b640bda9303c6f506a50aa474c0e50c Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Fri, 17 Jan 2025 13:29:05 +0600 Subject: [PATCH 06/11] refactor: rename `no-summary` to `no-summary-table` --- .../configuration/cli/trivy_config.md | 2 +- .../configuration/cli/trivy_convert.md | 2 +- .../configuration/cli/trivy_filesystem.md | 2 +- .../configuration/cli/trivy_image.md | 2 +- .../configuration/cli/trivy_kubernetes.md | 2 +- .../configuration/cli/trivy_repository.md | 2 +- .../configuration/cli/trivy_rootfs.md | 2 +- .../configuration/cli/trivy_sbom.md | 2 +- .../references/configuration/cli/trivy_vm.md | 2 +- .../references/configuration/config-file.md | 4 ++-- pkg/flag/report_flags.go | 24 +++++++++---------- pkg/flag/report_flags_test.go | 18 +++++++------- pkg/report/table/table.go | 4 ++-- pkg/report/table/table_private_test.go | 16 ++++++------- pkg/report/table/table_test.go | 8 +++---- pkg/report/writer.go | 2 +- 16 files changed, 47 insertions(+), 47 deletions(-) diff --git a/docs/docs/references/configuration/cli/trivy_config.md b/docs/docs/references/configuration/cli/trivy_config.md index 8ae258d3806c..fb9d40a8cf69 100644 --- a/docs/docs/references/configuration/cli/trivy_config.md +++ b/docs/docs/references/configuration/cli/trivy_config.md @@ -36,7 +36,7 @@ trivy config [flags] DIR --k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0) --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") - --no-summary hide summary table + --no-summary-table hide summary table -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. diff --git a/docs/docs/references/configuration/cli/trivy_convert.md b/docs/docs/references/configuration/cli/trivy_convert.md index 3c53495d27f5..ec01f6014eaf 100644 --- a/docs/docs/references/configuration/cli/trivy_convert.md +++ b/docs/docs/references/configuration/cli/trivy_convert.md @@ -27,7 +27,7 @@ trivy convert [flags] RESULT_JSON --ignore-policy string specify the Rego file path to evaluate each vulnerability --ignorefile string specify .trivyignore file (default ".trivyignore") --list-all-pkgs output all packages in the JSON report regardless of vulnerability - --no-summary hide summary table + --no-summary-table hide summary table -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --report string specify a report format for the output (all,summary) (default "all") diff --git a/docs/docs/references/configuration/cli/trivy_filesystem.md b/docs/docs/references/configuration/cli/trivy_filesystem.md index e3aa97ed4a3c..409b30357fee 100644 --- a/docs/docs/references/configuration/cli/trivy_filesystem.md +++ b/docs/docs/references/configuration/cli/trivy_filesystem.md @@ -64,7 +64,7 @@ trivy filesystem [flags] PATH --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar - --no-summary hide summary table + --no-summary-table hide summary table --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments diff --git a/docs/docs/references/configuration/cli/trivy_image.md b/docs/docs/references/configuration/cli/trivy_image.md index b426621d0b02..8cace7ae9eef 100644 --- a/docs/docs/references/configuration/cli/trivy_image.md +++ b/docs/docs/references/configuration/cli/trivy_image.md @@ -82,7 +82,7 @@ trivy image [flags] IMAGE_NAME --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar - --no-summary hide summary table + --no-summary-table hide summary table --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index 4b5ee8fdc092..27f372ae3b89 100644 --- a/docs/docs/references/configuration/cli/trivy_kubernetes.md +++ b/docs/docs/references/configuration/cli/trivy_kubernetes.md @@ -77,7 +77,7 @@ trivy kubernetes [flags] [CONTEXT] --list-all-pkgs output all packages in the JSON report regardless of vulnerability --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --no-progress suppress progress bar - --no-summary hide summary table + --no-summary-table hide summary table --node-collector-imageref string indicate the image reference for the node-collector scan job (default "ghcr.io/aquasecurity/node-collector:0.3.1") --node-collector-namespace string specify the namespace in which the node-collector job should be deployed (default "trivy-temp") --offline-scan do not issue API requests to identify dependencies diff --git a/docs/docs/references/configuration/cli/trivy_repository.md b/docs/docs/references/configuration/cli/trivy_repository.md index 99f37c1ee5b5..4df9b3798d56 100644 --- a/docs/docs/references/configuration/cli/trivy_repository.md +++ b/docs/docs/references/configuration/cli/trivy_repository.md @@ -63,7 +63,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar - --no-summary hide summary table + --no-summary-table hide summary table --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments diff --git a/docs/docs/references/configuration/cli/trivy_rootfs.md b/docs/docs/references/configuration/cli/trivy_rootfs.md index 45c34d3a5a6a..a02dc2b289e8 100644 --- a/docs/docs/references/configuration/cli/trivy_rootfs.md +++ b/docs/docs/references/configuration/cli/trivy_rootfs.md @@ -66,7 +66,7 @@ trivy rootfs [flags] ROOTDIR --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar - --no-summary hide summary table + --no-summary-table hide summary table --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments diff --git a/docs/docs/references/configuration/cli/trivy_sbom.md b/docs/docs/references/configuration/cli/trivy_sbom.md index 5fc019e7fa06..0ecd234ab457 100644 --- a/docs/docs/references/configuration/cli/trivy_sbom.md +++ b/docs/docs/references/configuration/cli/trivy_sbom.md @@ -45,7 +45,7 @@ trivy sbom [flags] SBOM_PATH --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [mirror.gcr.io/aquasec/trivy-java-db:1,ghcr.io/aquasecurity/trivy-java-db:1]) --list-all-pkgs output all packages in the JSON report regardless of vulnerability --no-progress suppress progress bar - --no-summary hide summary table + --no-summary-table hide summary table --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments diff --git a/docs/docs/references/configuration/cli/trivy_vm.md b/docs/docs/references/configuration/cli/trivy_vm.md index 5441bee3d9c4..3d2349a78ca6 100644 --- a/docs/docs/references/configuration/cli/trivy_vm.md +++ b/docs/docs/references/configuration/cli/trivy_vm.md @@ -58,7 +58,7 @@ trivy vm [flags] VM_IMAGE --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar - --no-summary hide summary table + --no-summary-table hide summary table --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments diff --git a/docs/docs/references/configuration/config-file.md b/docs/docs/references/configuration/config-file.md index 41b3153e5462..01cf1a9a27cd 100644 --- a/docs/docs/references/configuration/config-file.md +++ b/docs/docs/references/configuration/config-file.md @@ -521,8 +521,8 @@ ignorefile: ".trivyignore" # Same as '--list-all-pkgs' list-all-pkgs: false -# Same as '--no-summary' -no-summary: false +# Same as '--no-summary-table' +no-summary-table: false # Same as '--output' output: "" diff --git a/pkg/flag/report_flags.go b/pkg/flag/report_flags.go index 52db8e050574..ee7db0198c77 100644 --- a/pkg/flag/report_flags.go +++ b/pkg/flag/report_flags.go @@ -109,9 +109,9 @@ var ( ConfigName: "scan.show-suppressed", Usage: "[EXPERIMENTAL] show suppressed vulnerabilities", } - NoSummaryFlag = Flag[bool]{ - Name: "no-summary", - ConfigName: "no-summary", + NoSummaryTableFlag = Flag[bool]{ + Name: "no-summary-table", + ConfigName: "no-summary-table", Usage: "hide summary table", } ) @@ -133,7 +133,7 @@ type ReportFlagGroup struct { Severity *Flag[[]string] Compliance *Flag[string] ShowSuppressed *Flag[bool] - NoSummary *Flag[bool] + NoSummaryTable *Flag[bool] } type ReportOptions struct { @@ -151,7 +151,7 @@ type ReportOptions struct { Severities []dbTypes.Severity Compliance spec.ComplianceSpec ShowSuppressed bool - NoSummary bool + NoSummaryTable bool } func NewReportFlagGroup() *ReportFlagGroup { @@ -170,7 +170,7 @@ func NewReportFlagGroup() *ReportFlagGroup { Severity: SeverityFlag.Clone(), Compliance: ComplianceFlag.Clone(), ShowSuppressed: ShowSuppressedFlag.Clone(), - NoSummary: NoSummaryFlag.Clone(), + NoSummaryTable: NoSummaryTableFlag.Clone(), } } @@ -194,7 +194,7 @@ func (f *ReportFlagGroup) Flags() []Flagger { f.Severity, f.Compliance, f.ShowSuppressed, - f.NoSummary, + f.NoSummaryTable, } } @@ -207,7 +207,7 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) { template := f.Template.Value() dependencyTree := f.DependencyTree.Value() listAllPkgs := f.ListAllPkgs.Value() - noSummary := f.NoSummary.Value() + noSummaryTable := f.NoSummaryTable.Value() if template != "" { if format == "" { @@ -238,9 +238,9 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) { } // "--so-summary" option is available only with "--format table". - if noSummary && format != types.FormatTable { - noSummary = false - log.Warn(`"--no-summary" can be used only with "--format table".`) + if noSummaryTable && format != types.FormatTable { + noSummaryTable = false + log.Warn(`"--no-summary-table" can be used only with "--format table".`) } cs, err := loadComplianceTypes(f.Compliance.Value()) @@ -275,7 +275,7 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) { Severities: toSeverity(f.Severity.Value()), Compliance: cs, ShowSuppressed: f.ShowSuppressed.Value(), - NoSummary: noSummary, + NoSummaryTable: noSummaryTable, }, nil } diff --git a/pkg/flag/report_flags_test.go b/pkg/flag/report_flags_test.go index ded87412f590..4cd99fe8aef7 100644 --- a/pkg/flag/report_flags_test.go +++ b/pkg/flag/report_flags_test.go @@ -32,7 +32,7 @@ func TestReportFlagGroup_ToOptions(t *testing.T) { compliance string debug bool pkgTypes string - noSummary bool + noSummaryTable bool } tests := []struct { name string @@ -117,17 +117,17 @@ func TestReportFlagGroup_ToOptions(t *testing.T) { }, }, { - name: "invalid option combination: --no-summary with --format json", + name: "invalid option combination: --no-summary-table with --format json", fields: fields{ - format: "json", - noSummary: true, + format: "json", + noSummaryTable: true, }, wantLogs: []string{ - `"--no-summary" can be used only with "--format table".`, + `"--no-summary-table" can be used only with "--format table".`, }, want: flag.ReportOptions{ - Format: "json", - NoSummary: false, + Format: "json", + NoSummaryTable: false, }, }, { @@ -199,7 +199,7 @@ func TestReportFlagGroup_ToOptions(t *testing.T) { setValue(flag.OutputPluginArgFlag.ConfigName, tt.fields.outputPluginArgs) setValue(flag.SeverityFlag.ConfigName, tt.fields.severities) setValue(flag.ComplianceFlag.ConfigName, tt.fields.compliance) - setValue(flag.NoSummaryFlag.ConfigName, tt.fields.noSummary) + setValue(flag.NoSummaryTableFlag.ConfigName, tt.fields.noSummaryTable) // Assert options f := &flag.ReportFlagGroup{ @@ -215,7 +215,7 @@ func TestReportFlagGroup_ToOptions(t *testing.T) { OutputPluginArg: flag.OutputPluginArgFlag.Clone(), Severity: flag.SeverityFlag.Clone(), Compliance: flag.ComplianceFlag.Clone(), - NoSummary: flag.NoSummaryFlag.Clone(), + NoSummaryTable: flag.NoSummaryTableFlag.Clone(), } got, err := f.ToOptions() diff --git a/pkg/report/table/table.go b/pkg/report/table/table.go index 0da4b4a2c1c2..a4aeac332266 100644 --- a/pkg/report/table/table.go +++ b/pkg/report/table/table.go @@ -42,7 +42,7 @@ type Writer struct { ShowSuppressed bool // Hide summary table - NoSummary bool + NoSummaryTable bool // For misconfigurations IncludeNonFailures bool @@ -63,7 +63,7 @@ func (tw Writer) Write(_ context.Context, report types.Report) error { tml.DisableFormatting() } - if !tw.NoSummary { + if !tw.NoSummaryTable { if err := tw.renderSummary(report); err != nil { return xerrors.Errorf("failed to render summary: %w", err) } diff --git a/pkg/report/table/table_private_test.go b/pkg/report/table/table_private_test.go index d732c298ac2d..77a5ca064996 100644 --- a/pkg/report/table/table_private_test.go +++ b/pkg/report/table/table_private_test.go @@ -95,11 +95,11 @@ var ( func Test_renderSummary(t *testing.T) { tests := []struct { - name string - scanners types.Scanners - noSummary bool - report types.Report - want string + name string + scanners types.Scanners + noSummaryTable bool + report types.Report + want string }{ { name: "happy path all scanners", @@ -215,9 +215,9 @@ Report Summary tml.DisableFormatting() tableWritten := bytes.Buffer{} writer := Writer{ - Output: &tableWritten, - Scanners: tt.scanners, - NoSummary: tt.noSummary, + Output: &tableWritten, + Scanners: tt.scanners, + NoSummaryTable: tt.noSummaryTable, } err := writer.renderSummary(tt.report) require.NoError(t, err) diff --git a/pkg/report/table/table_test.go b/pkg/report/table/table_test.go index 78f3ae63c126..d88e8ee355c0 100644 --- a/pkg/report/table/table_test.go +++ b/pkg/report/table/table_test.go @@ -17,7 +17,7 @@ func TestWriter_Write(t *testing.T) { testCases := []struct { name string scanners types.Scanners - noSummary bool + noSummaryTable bool results types.Results wantOutput string wantError string @@ -103,7 +103,7 @@ Report Summary scanners: types.Scanners{ types.VulnerabilityScanner, }, - noSummary: true, + noSummaryTable: true, results: types.Results{ { Target: "test", @@ -138,8 +138,8 @@ Report Summary dbTypes.SeverityHigh, dbTypes.SeverityMedium, }, - Scanners: tc.scanners, - NoSummary: tc.noSummary, + Scanners: tc.scanners, + NoSummaryTable: tc.noSummaryTable, } err := writer.Write(nil, types.Report{Results: tc.results}) if tc.wantError != "" { diff --git a/pkg/report/writer.go b/pkg/report/writer.go index 38cb16fdb8a0..81d76dca69c7 100644 --- a/pkg/report/writer.go +++ b/pkg/report/writer.go @@ -54,7 +54,7 @@ func Write(ctx context.Context, report types.Report, option flag.Options) (err e Trace: option.Trace, LicenseRiskThreshold: option.LicenseRiskThreshold, IgnoredLicenses: option.IgnoredLicenses, - NoSummary: option.NoSummary, + NoSummaryTable: option.NoSummaryTable, } case types.FormatJSON: writer = &JSONWriter{ From 37a072d2223fd01c575fe57eba13b709ae8fe89b Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Fri, 17 Jan 2025 13:34:07 +0600 Subject: [PATCH 07/11] fix: linter errors --- pkg/report/table/table_private_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/report/table/table_private_test.go b/pkg/report/table/table_private_test.go index 77a5ca064996..15fc4d5c8e6f 100644 --- a/pkg/report/table/table_private_test.go +++ b/pkg/report/table/table_private_test.go @@ -4,11 +4,12 @@ import ( "bytes" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/aquasecurity/tml" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) var ( From f4f46a5045d52eb9dd9b7b746cbe9598c4cb33e8 Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Fri, 17 Jan 2025 14:02:44 +0600 Subject: [PATCH 08/11] test: refactor secret result --- pkg/report/table/table_private_test.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/pkg/report/table/table_private_test.go b/pkg/report/table/table_private_test.go index 15fc4d5c8e6f..c54c3ea5fb4c 100644 --- a/pkg/report/table/table_private_test.go +++ b/pkg/report/table/table_private_test.go @@ -66,7 +66,7 @@ var ( }, } secret = types.Result{ - Target: "/app/aws-secrets.txt", + Target: "requirements.txt", Class: types.ClassSecret, Secrets: []types.DetectedSecret{ { @@ -133,7 +133,7 @@ Report Summary ├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ │ app/Dockerfile │ dockerfile │ - │ 2 │ - │ - │ ├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ -│ /app/aws-secrets.txt │ text │ - │ - │ 1 │ - │ +│ requirements.txt │ text │ - │ - │ 1 │ - │ ├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ │ OS Packages │ - │ - │ - │ - │ 1 │ ├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ @@ -167,23 +167,27 @@ Report Summary `, }, { - name: "happy path vuln scanner only without vulnerabilities", + name: "happy path no vulns + secret", scanners: []types.Scanner{ types.VulnerabilityScanner, + types.SecretScanner, }, report: types.Report{ Results: []types.Result{ noVuln, + secret, }, }, want: ` Report Summary -┌──────────────────┬──────┬─────────────────┐ -│ Target │ Type │ Vulnerabilities │ -├──────────────────┼──────┼─────────────────┤ -│ requirements.txt │ pip │ 0 │ -└──────────────────┴──────┴─────────────────┘ +┌──────────────────┬──────┬─────────────────┬─────────┐ +│ Target │ Type │ Vulnerabilities │ Secrets │ +├──────────────────┼──────┼─────────────────┼─────────┤ +│ requirements.txt │ pip │ 0 │ - │ +├──────────────────┼──────┼─────────────────┼─────────┤ +│ requirements.txt │ text │ - │ 1 │ +└──────────────────┴──────┴─────────────────┴─────────┘ `, }, { From 19fc0fe95eeb0e0ffe3f06660930f5b654506401 Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Fri, 17 Jan 2025 14:35:33 +0600 Subject: [PATCH 09/11] docs: add info about summary table --- docs/docs/configuration/reporting.md | 73 +++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/docs/docs/configuration/reporting.md b/docs/docs/configuration/reporting.md index c6ddc1727a71..389731c6f107 100644 --- a/docs/docs/configuration/reporting.md +++ b/docs/docs/configuration/reporting.md @@ -19,10 +19,81 @@ Trivy supports the following formats: | Secret | ✓ | | License | ✓ | +```bash +$ trivy image -f table golang:1.22.11-alpine3.21 ``` -$ trivy image -f table golang:1.12-alpine + +
+Result + +``` +... + + +Report Summary + +┌─────────────────────────────────────────────┬──────────┬─────────────────┬─────────┐ +│ Target │ Type │ Vulnerabilities │ Secrets │ +├─────────────────────────────────────────────┼──────────┼─────────────────┼─────────┤ +│ golang:1.22.11-alpine3.21 (alpine 3.21.2) │ alpine │ 0 │ - │ +├─────────────────────────────────────────────┼──────────┼─────────────────┼─────────┤ +│ Node.js │ node-pkg │ 0 │ - │ +├─────────────────────────────────────────────┼──────────┼─────────────────┼─────────┤ +│ usr/local/go/bin/go │ gobinary │ 0 │ - │ +├─────────────────────────────────────────────┼──────────┼─────────────────┼─────────┤ +│ usr/local/go/bin/gofmt │ gobinary │ 0 │ - │ +├─────────────────────────────────────────────┼──────────┼─────────────────┼─────────┤ +... +├─────────────────────────────────────────────┼──────────┼─────────────────┼─────────┤ +│ usr/local/go/pkg/tool/linux_amd64/vet │ gobinary │ 0 │ - │ +└─────────────────────────────────────────────┴──────────┴─────────────────┴─────────┘ + +golang:1.22.11-alpine3.21 (alpine 3.21.2) + +Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0) + ``` +
+ +#### Summary table +Before result tables Trivy shows summary table. + +
+Report Summary + +``` +┌───────────────────────┬────────────┬─────────────────┬───────────────────┬─────────┬──────────┐ +│ Target │ Type │ Vulnerabilities │ Misconfigurations │ Secrets │ Licenses │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ test (alpine 3.20.3) │ alpine │ 2 │ - │ - │ - │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ Java │ jar │ 2 │ - │ - │ - │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ app/Dockerfile │ dockerfile │ - │ 2 │ - │ - │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ requirements.txt │ text │ 0 │ - │ - │ - │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ requirements.txt │ text │ - │ - │ 1 │ - │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ OS Packages │ - │ - │ - │ - │ 1 │ +├───────────────────────┼────────────┼─────────────────┼───────────────────┼─────────┼──────────┤ +│ Java │ - │ - │ - │ - │ 0 │ +└───────────────────────┴────────────┴─────────────────┴───────────────────┴─────────┴──────────┘ +``` + +
+ +This table: + +- include columns for enabled [scanners](../references/terminology.md#scanner) only. +- Contains separate lines for the same targets but different scanners. +- `-` means that Trivy didn't scan this target. +- `0` means that Trivy scanned this target, but found no vulns/misconfigs. + +!!! note + Use `--no-summary-table` flag to hide summary table. + #### Show origins of vulnerable dependencies | Scanner | Supported | From a27f879092d5e3f3dd08efd2b68666633416d0bd Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Fri, 17 Jan 2025 14:38:38 +0600 Subject: [PATCH 10/11] feat: add logs about `-` and `0` in summary table --- pkg/report/table/table.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/report/table/table.go b/pkg/report/table/table.go index a4aeac332266..8b1b7804563b 100644 --- a/pkg/report/table/table.go +++ b/pkg/report/table/table.go @@ -9,6 +9,7 @@ import ( "slices" "strings" + "github.com/aquasecurity/trivy/pkg/log" "github.com/fatih/color" "github.com/samber/lo" "golang.org/x/xerrors" @@ -80,6 +81,9 @@ func (tw Writer) Write(_ context.Context, report types.Report) error { } func (tw Writer) renderSummary(report types.Report) error { + log.WithPrefix("report").Info("Report Summary table contains special symbols", + log.String("'-'", "Target didn't scanned"), + log.String("'0'", "Target scanned, but didn't contain vulns/misconfigs/secrets/licenses")) // Fprintln has a bug if err := tml.Fprintf(tw.Output, "\nReport Summary\n\n"); err != nil { return err From 239251bde638e6697e0beda1549c5c64bb8730d9 Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Fri, 17 Jan 2025 14:51:16 +0600 Subject: [PATCH 11/11] fix: linter error --- pkg/report/table/table.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/report/table/table.go b/pkg/report/table/table.go index 8b1b7804563b..eeac9794da5f 100644 --- a/pkg/report/table/table.go +++ b/pkg/report/table/table.go @@ -9,7 +9,6 @@ import ( "slices" "strings" - "github.com/aquasecurity/trivy/pkg/log" "github.com/fatih/color" "github.com/samber/lo" "golang.org/x/xerrors" @@ -17,6 +16,7 @@ import ( "github.com/aquasecurity/table" "github.com/aquasecurity/tml" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" + "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/types" )