Skip to content

Commit cf00131

Browse files
knqyf263DmitriyLewen
authored andcommitted
feat: add report summary table (aquasecurity#8177)
Signed-off-by: knqyf263 <[email protected]> Co-authored-by: DmitriyLewen <[email protected]> Co-authored-by: DmitriyLewen <[email protected]>
1 parent fff8c16 commit cf00131

32 files changed

+1256
-176
lines changed

docs/docs/configuration/reporting.md

+147-2
Large diffs are not rendered by default.

docs/docs/references/configuration/cli/trivy_config.md

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ trivy config [flags] DIR
5151
--skip-check-update skip fetching rego check updates
5252
--skip-dirs strings specify the directories or glob patterns to skip
5353
--skip-files strings specify the files or glob patterns to skip
54+
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
5455
-t, --template string output template
5556
--tf-exclude-downloaded-modules exclude misconfigurations for downloaded terraform modules
5657
--tf-vars strings specify paths to override the Terraform tfvars files

docs/docs/references/configuration/cli/trivy_convert.md

+2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ trivy convert [flags] RESULT_JSON
3030
-o, --output string output file name
3131
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
3232
--report string specify a report format for the output (all,summary) (default "all")
33+
--scanners strings List of scanners included when generating the json report. Used only for rendering the summary table. (vuln,misconfig,secret,license)
3334
-s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
3435
--show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities
36+
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
3537
-t, --template string output template
3638
```
3739

docs/docs/references/configuration/cli/trivy_filesystem.md

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ trivy filesystem [flags] PATH
9292
--skip-files strings specify the files or glob patterns to skip
9393
--skip-java-db-update skip updating Java index database
9494
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
95+
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
9596
-t, --template string output template
9697
--tf-exclude-downloaded-modules exclude misconfigurations for downloaded terraform modules
9798
--tf-vars strings specify paths to override the Terraform tfvars files

docs/docs/references/configuration/cli/trivy_image.md

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ trivy image [flags] IMAGE_NAME
114114
--skip-files strings specify the files or glob patterns to skip
115115
--skip-java-db-update skip updating Java index database
116116
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
117+
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
117118
-t, --template string output template
118119
--tf-exclude-downloaded-modules exclude misconfigurations for downloaded terraform modules
119120
--token string for authentication in client/server mode

docs/docs/references/configuration/cli/trivy_repository.md

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL)
9090
--skip-files strings specify the files or glob patterns to skip
9191
--skip-java-db-update skip updating Java index database
9292
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
93+
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
9394
--tag string pass the tag name to be scanned
9495
-t, --template string output template
9596
--tf-exclude-downloaded-modules exclude misconfigurations for downloaded terraform modules

docs/docs/references/configuration/cli/trivy_rootfs.md

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ trivy rootfs [flags] ROOTDIR
9393
--skip-files strings specify the files or glob patterns to skip
9494
--skip-java-db-update skip updating Java index database
9595
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
96+
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
9697
-t, --template string output template
9798
--tf-exclude-downloaded-modules exclude misconfigurations for downloaded terraform modules
9899
--tf-vars strings specify paths to override the Terraform tfvars files

docs/docs/references/configuration/cli/trivy_sbom.md

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ trivy sbom [flags] SBOM_PATH
6868
--skip-files strings specify the files or glob patterns to skip
6969
--skip-java-db-update skip updating Java index database
7070
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
71+
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
7172
-t, --template string output template
7273
--token string for authentication in client/server mode
7374
--token-header string specify a header name for token in client/server mode (default "Trivy-Token")

docs/docs/references/configuration/cli/trivy_vm.md

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ trivy vm [flags] VM_IMAGE
8181
--skip-files strings specify the files or glob patterns to skip
8282
--skip-java-db-update skip updating Java index database
8383
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
84+
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
8485
-t, --template string output template
8586
--tf-exclude-downloaded-modules exclude misconfigurations for downloaded terraform modules
8687
--token string for authentication in client/server mode

docs/docs/references/configuration/config-file.md

+5
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,11 @@ severity:
553553
- HIGH
554554
- CRITICAL
555555

556+
# Same as '--table-mode'
557+
table-mode:
558+
- summary
559+
- detailed
560+
556561
# Same as '--template'
557562
template: ""
558563

integration/testdata/license-cyclonedx.json.golden

+3-12
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616
}
1717
},
1818
"Results": [
19-
{
20-
"Target": "OS Packages",
21-
"Class": "license"
22-
},
2319
{
2420
"Target": "Java",
2521
"Class": "license",
@@ -30,6 +26,7 @@
3026
"PkgName": "org.eclipse.sisu:org.eclipse.sisu.plexus",
3127
"FilePath": "",
3228
"Name": "EPL-1.0",
29+
"Text": "",
3330
"Confidence": 1,
3431
"Link": ""
3532
},
@@ -39,6 +36,7 @@
3936
"PkgName": "org.ow2.asm:asm",
4037
"FilePath": "",
4138
"Name": "BSD-3-Clause",
39+
"Text": "",
4240
"Confidence": 1,
4341
"Link": ""
4442
},
@@ -48,18 +46,11 @@
4846
"PkgName": "org.slf4j:slf4j-api",
4947
"FilePath": "",
5048
"Name": "MIT License",
49+
"Text": "",
5150
"Confidence": 1,
5251
"Link": ""
5352
}
5453
]
55-
},
56-
{
57-
"Target": "pom.xml",
58-
"Class": "license"
59-
},
60-
{
61-
"Target": "Loose File License(s)",
62-
"Class": "license-file"
6354
}
6455
]
6556
}

pkg/commands/app.go

+8
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,13 @@ func NewConvertCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
520520
ReportFlagGroup: flag.NewReportFlagGroup(),
521521
}
522522

523+
// To display the summary table, we need to enable scanners (to build columns).
524+
// We can't get scanner information from the report (we don't include empty licenses and secrets in the report).
525+
// So we need to ask the user to configure scanners (if needed).
526+
convertFlags.ScanFlagGroup.Scanners = flag.ScannersFlag.Clone()
527+
convertFlags.ScanFlagGroup.Scanners.Default = nil // disable default scanners
528+
convertFlags.ScanFlagGroup.Scanners.Usage = "List of scanners included when generating the json report. Used only for rendering the summary table."
529+
523530
cmd := &cobra.Command{
524531
Use: "convert [flags] RESULT_JSON",
525532
Aliases: []string{"conv"},
@@ -977,6 +984,7 @@ func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
977984
}
978985
reportFlagGroup.Compliance = compliance // override usage as the accepted values differ for each subcommand.
979986
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
987+
reportFlagGroup.TableMode = nil // disable '--table-mode'
980988

981989
formatFlag := flag.FormatFlag.Clone()
982990
formatFlag.Values = xstrings.ToStringSlice([]types.Format{

pkg/commands/convert/run.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"context"
55
"encoding/json"
66
"os"
7+
"slices"
78

9+
"github.com/samber/lo"
810
"golang.org/x/xerrors"
911

1012
"github.com/aquasecurity/trivy/pkg/commands/operation"
@@ -18,6 +20,7 @@ import (
1820
)
1921

2022
func Run(ctx context.Context, opts flag.Options) (err error) {
23+
logger := log.WithPrefix("convert")
2124
ctx, cancel := context.WithTimeout(ctx, opts.Timeout)
2225
defer cancel()
2326

@@ -42,7 +45,14 @@ func Run(ctx context.Context, opts flag.Options) (err error) {
4245
return xerrors.Errorf("unable to filter results: %w", err)
4346
}
4447

45-
log.Debug("Writing report to output...")
48+
if len(opts.Scanners) == 0 && opts.Format == types.FormatTable && slices.Contains(opts.TableModes, types.Summary) {
49+
logger.Info("To display the summary table, enable the scanners used during JSON report generation.")
50+
opts.TableModes = lo.Filter(opts.TableModes, func(mode types.TableMode, _ int) bool {
51+
return mode != types.Summary
52+
})
53+
}
54+
55+
logger.Debug("Writing report to output...")
4656
if err = report.Write(ctx, r, opts); err != nil {
4757
return xerrors.Errorf("unable to write results: %w", err)
4858
}

pkg/flag/options.go

+1
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ func (o *Options) ScanOpts() types.ScanOptions {
460460
ImageConfigScanners: o.ImageConfigScanners, // this is valid only for 'image' subcommand
461461
ScanRemovedPackages: o.ScanRemovedPkgs, // this is valid only for 'image' subcommand
462462
LicenseCategories: o.LicenseCategories,
463+
LicenseFull: o.LicenseFull,
463464
FilePatterns: o.FilePatterns,
464465
IncludeDevDeps: o.IncludeDevDeps,
465466
Distro: o.Distro,

pkg/flag/report_flags.go

+18
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@ var (
109109
ConfigName: "scan.show-suppressed",
110110
Usage: "[EXPERIMENTAL] show suppressed vulnerabilities",
111111
}
112+
TableModeFlag = Flag[[]string]{
113+
Name: "table-mode",
114+
ConfigName: "table-mode",
115+
Default: xstrings.ToStringSlice(types.SupportedTableModes),
116+
Values: xstrings.ToStringSlice(types.SupportedTableModes),
117+
Usage: "[EXPERIMENTAL] tables that will be displayed in 'table' format",
118+
}
112119
)
113120

114121
// ReportFlagGroup composes common printer flag structs
@@ -128,6 +135,7 @@ type ReportFlagGroup struct {
128135
Severity *Flag[[]string]
129136
Compliance *Flag[string]
130137
ShowSuppressed *Flag[bool]
138+
TableMode *Flag[[]string]
131139
}
132140

133141
type ReportOptions struct {
@@ -145,6 +153,7 @@ type ReportOptions struct {
145153
Severities []dbTypes.Severity
146154
Compliance spec.ComplianceSpec
147155
ShowSuppressed bool
156+
TableModes []types.TableMode
148157
}
149158

150159
func NewReportFlagGroup() *ReportFlagGroup {
@@ -163,6 +172,7 @@ func NewReportFlagGroup() *ReportFlagGroup {
163172
Severity: SeverityFlag.Clone(),
164173
Compliance: ComplianceFlag.Clone(),
165174
ShowSuppressed: ShowSuppressedFlag.Clone(),
175+
TableMode: TableModeFlag.Clone(),
166176
}
167177
}
168178

@@ -186,6 +196,7 @@ func (f *ReportFlagGroup) Flags() []Flagger {
186196
f.Severity,
187197
f.Compliance,
188198
f.ShowSuppressed,
199+
f.TableMode,
189200
}
190201
}
191202

@@ -198,6 +209,7 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) {
198209
template := f.Template.Value()
199210
dependencyTree := f.DependencyTree.Value()
200211
listAllPkgs := f.ListAllPkgs.Value()
212+
tableModes := f.TableMode.Value()
201213

202214
if template != "" {
203215
if format == "" {
@@ -227,6 +239,11 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) {
227239
}
228240
}
229241

242+
// "--table-mode" option is available only with "--format table".
243+
if viper.IsSet(TableModeFlag.ConfigName) && format != types.FormatTable {
244+
return ReportOptions{}, xerrors.New(`"--table-mode" can be used only with "--format table".`)
245+
}
246+
230247
cs, err := loadComplianceTypes(f.Compliance.Value())
231248
if err != nil {
232249
return ReportOptions{}, xerrors.Errorf("unable to load compliance spec: %w", err)
@@ -259,6 +276,7 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) {
259276
Severities: toSeverity(f.Severity.Value()),
260277
Compliance: cs,
261278
ShowSuppressed: f.ShowSuppressed.Value(),
279+
TableModes: xstrings.ToTSlice[types.TableMode](tableModes),
262280
}, nil
263281
}
264282

pkg/flag/report_flags_test.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
iacTypes "github.com/aquasecurity/trivy/pkg/iac/types"
1414
"github.com/aquasecurity/trivy/pkg/log"
1515
"github.com/aquasecurity/trivy/pkg/types"
16+
xstrings "github.com/aquasecurity/trivy/pkg/x/strings"
1617
)
1718

1819
func TestReportFlagGroup_ToOptions(t *testing.T) {
@@ -32,11 +33,13 @@ func TestReportFlagGroup_ToOptions(t *testing.T) {
3233
compliance string
3334
debug bool
3435
pkgTypes string
36+
tableModes []string
3537
}
3638
tests := []struct {
3739
name string
3840
fields fields
3941
want flag.ReportOptions
42+
wantErr string
4043
wantLogs []string
4144
}{
4245
{
@@ -160,6 +163,14 @@ func TestReportFlagGroup_ToOptions(t *testing.T) {
160163
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
161164
},
162165
},
166+
{
167+
name: "invalid option combination: --table-modes with --format json",
168+
fields: fields{
169+
format: "json",
170+
tableModes: xstrings.ToStringSlice(types.SupportedTableModes),
171+
},
172+
wantErr: `"--table-mode" can be used only with "--format table".`,
173+
},
163174
}
164175
for _, tt := range tests {
165176
t.Run(tt.name, func(t *testing.T) {
@@ -184,6 +195,7 @@ func TestReportFlagGroup_ToOptions(t *testing.T) {
184195
setValue(flag.OutputPluginArgFlag.ConfigName, tt.fields.outputPluginArgs)
185196
setValue(flag.SeverityFlag.ConfigName, tt.fields.severities)
186197
setValue(flag.ComplianceFlag.ConfigName, tt.fields.compliance)
198+
setSliceValue(flag.TableModeFlag.ConfigName, tt.fields.tableModes)
187199

188200
// Assert options
189201
f := &flag.ReportFlagGroup{
@@ -199,10 +211,15 @@ func TestReportFlagGroup_ToOptions(t *testing.T) {
199211
OutputPluginArg: flag.OutputPluginArgFlag.Clone(),
200212
Severity: flag.SeverityFlag.Clone(),
201213
Compliance: flag.ComplianceFlag.Clone(),
214+
TableMode: flag.TableModeFlag.Clone(),
202215
}
203216

204217
got, err := f.ToOptions()
205-
require.NoError(t, err)
218+
if tt.wantErr != "" {
219+
require.Contains(t, err.Error(), tt.wantErr)
220+
return
221+
}
222+
206223
assert.EqualExportedValuesf(t, tt.want, got, "ToOptions()")
207224

208225
// Assert log messages

0 commit comments

Comments
 (0)