Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add report summary table #8177

Merged
merged 49 commits into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
ff62336
feat(cli): add report summary
knqyf263 Dec 25, 2024
ca75fc9
test: fix table tests
DmitriyLewen Jan 16, 2025
ef26940
feat: add `--no-summary` flag
DmitriyLewen Jan 16, 2025
3ea064f
test: add test for `renderSummary`
DmitriyLewen Jan 16, 2025
8b2acf7
mage docs:generate
DmitriyLewen Jan 16, 2025
751823a
refactor: rename `no-summary` to `no-summary-table`
DmitriyLewen Jan 17, 2025
37a072d
fix: linter errors
DmitriyLewen Jan 17, 2025
f4f46a5
test: refactor secret result
DmitriyLewen Jan 17, 2025
4292e65
Merge branch 'main' of github.com:aquasecurity/trivy into feat/summar…
DmitriyLewen Jan 17, 2025
19fc0fe
docs: add info about summary table
DmitriyLewen Jan 17, 2025
a27f879
feat: add logs about `-` and `0` in summary table
DmitriyLewen Jan 17, 2025
239251b
fix: linter error
DmitriyLewen Jan 17, 2025
8b9d908
Merge branch 'main' of github.com:aquasecurity/trivy into feat/summar…
DmitriyLewen Jan 29, 2025
e11221a
refactor: use footer instead of log for legend
DmitriyLewen Jan 29, 2025
5f61893
refactor: use log when results array is empty
DmitriyLewen Jan 29, 2025
85edf16
chore: add comment for showEmptyResultsWarning
DmitriyLewen Jan 29, 2025
d5ca966
refactor
DmitriyLewen Jan 29, 2025
eb4d2fa
refactor: hide empty results for OS packages license and file licenses
DmitriyLewen Jan 29, 2025
a57dcc6
fix: add LicenseFiles into summary table
DmitriyLewen Jan 29, 2025
4f349c5
test: add LicenseFiles in test
DmitriyLewen Jan 29, 2025
84169b4
feat: split aggregated pkgs
DmitriyLewen Jan 29, 2025
892d6de
fix: show packages without vulns
DmitriyLewen Jan 29, 2025
db0cbe1
fix: tests
DmitriyLewen Jan 29, 2025
28ebb24
refactor: don't show empty vuln table title for OS pkgs
DmitriyLewen Jan 29, 2025
0166a45
refactor: use info log when results didn't find
DmitriyLewen Jan 29, 2025
5f13964
fix: typo
DmitriyLewen Jan 29, 2025
7f863b9
refactor: show legend after table
DmitriyLewen Jan 29, 2025
2628b70
docs: fix typo
DmitriyLewen Feb 4, 2025
c78bfe5
fix: nested list
DmitriyLewen Feb 4, 2025
ba8f756
refactor: stop iter vulns
DmitriyLewen Feb 4, 2025
d37ad35
refactor: split license functions
DmitriyLewen Feb 4, 2025
786e704
refactor: use error for no-summary + no-table format
DmitriyLewen Feb 4, 2025
9c3e8ce
refactor: add empty table with `-`
DmitriyLewen Feb 4, 2025
878b9f4
refactor: use `Renderer` interface for summary table
DmitriyLewen Feb 5, 2025
4b6214b
fix: linter error
DmitriyLewen Feb 5, 2025
29388c4
Merge branch 'main' into 'feat/summary_table'
DmitriyLewen Mar 3, 2025
8de06d7
refactor: after rebase
DmitriyLewen Mar 3, 2025
5ea3e4f
fix(report): skip applications without licenses
DmitriyLewen Mar 3, 2025
1159d63
refactor: enable `--scanners` for convert mode
DmitriyLewen Mar 3, 2025
5b048c1
fix: linter errors
DmitriyLewen Mar 3, 2025
fd00dc9
mage docs:generate
DmitriyLewen Mar 3, 2025
67a729c
refactor: add logger into summaryRenderer
DmitriyLewen Mar 3, 2025
f648dc6
Merge branch 'main' into 'feat/summary_table'
DmitriyLewen Mar 4, 2025
ea453d8
refactor: change --no-summary-table to -tables-mode
DmitriyLewen Mar 4, 2025
9d58ab2
docs: remove not for `--table-mode` flag
DmitriyLewen Mar 4, 2025
5b6e9ea
docs: fix typo
DmitriyLewen Mar 4, 2025
71a53b2
docs: fix typo
DmitriyLewen Mar 4, 2025
df6c0de
refactor: use Info log for secrets/licenses
DmitriyLewen Mar 4, 2025
014e4c4
refactor: use AlignCenter for empty summary table
DmitriyLewen Mar 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 147 additions & 2 deletions docs/docs/configuration/reporting.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ trivy config [flags] DIR
--skip-check-update skip fetching rego check updates
--skip-dirs strings specify the directories or glob patterns to skip
--skip-files strings specify the files or glob patterns to skip
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
-t, --template string output template
--tf-exclude-downloaded-modules exclude misconfigurations for downloaded terraform modules
--tf-vars strings specify paths to override the Terraform tfvars files
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/references/configuration/cli/trivy_convert.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ trivy convert [flags] RESULT_JSON
-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")
--scanners strings List of scanners included when generating the json report. Used only for rendering the summary table. (vuln,misconfig,secret,license)
-s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
-t, --template string output template
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ trivy filesystem [flags] PATH
--skip-files strings specify the files or glob patterns to skip
--skip-java-db-update skip updating Java index database
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
-t, --template string output template
--tf-exclude-downloaded-modules exclude misconfigurations for downloaded terraform modules
--tf-vars strings specify paths to override the Terraform tfvars files
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_image.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ trivy image [flags] IMAGE_NAME
--skip-files strings specify the files or glob patterns to skip
--skip-java-db-update skip updating Java index database
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
-t, --template string output template
--tf-exclude-downloaded-modules exclude misconfigurations for downloaded terraform modules
--token string for authentication in client/server mode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL)
--skip-files strings specify the files or glob patterns to skip
--skip-java-db-update skip updating Java index database
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
--tag string pass the tag name to be scanned
-t, --template string output template
--tf-exclude-downloaded-modules exclude misconfigurations for downloaded terraform modules
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_rootfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ trivy rootfs [flags] ROOTDIR
--skip-files strings specify the files or glob patterns to skip
--skip-java-db-update skip updating Java index database
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
-t, --template string output template
--tf-exclude-downloaded-modules exclude misconfigurations for downloaded terraform modules
--tf-vars strings specify paths to override the Terraform tfvars files
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_sbom.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ trivy sbom [flags] SBOM_PATH
--skip-files strings specify the files or glob patterns to skip
--skip-java-db-update skip updating Java index database
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
-t, --template string output template
--token string for authentication in client/server mode
--token-header string specify a header name for token in client/server mode (default "Trivy-Token")
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ trivy vm [flags] VM_IMAGE
--skip-files strings specify the files or glob patterns to skip
--skip-java-db-update skip updating Java index database
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
--table-mode strings [EXPERIMENTAL] tables that will be displayed in 'table' format (summary,detailed) (default [summary,detailed])
-t, --template string output template
--tf-exclude-downloaded-modules exclude misconfigurations for downloaded terraform modules
--token string for authentication in client/server mode
Expand Down
5 changes: 5 additions & 0 deletions docs/docs/references/configuration/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,11 @@ severity:
- HIGH
- CRITICAL

# Same as '--table-mode'
table-mode:
- summary
- detailed

# Same as '--template'
template: ""

Expand Down
15 changes: 3 additions & 12 deletions integration/testdata/license-cyclonedx.json.golden
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@
}
},
"Results": [
{
"Target": "OS Packages",
"Class": "license"
},
{
"Target": "Java",
"Class": "license",
Expand All @@ -30,6 +26,7 @@
"PkgName": "org.eclipse.sisu:org.eclipse.sisu.plexus",
"FilePath": "",
"Name": "EPL-1.0",
"Text": "",
"Confidence": 1,
"Link": ""
},
Expand All @@ -39,6 +36,7 @@
"PkgName": "org.ow2.asm:asm",
"FilePath": "",
"Name": "BSD-3-Clause",
"Text": "",
"Confidence": 1,
"Link": ""
},
Expand All @@ -48,18 +46,11 @@
"PkgName": "org.slf4j:slf4j-api",
"FilePath": "",
"Name": "MIT License",
"Text": "",
"Confidence": 1,
"Link": ""
}
]
},
{
"Target": "pom.xml",
"Class": "license"
},
{
"Target": "Loose File License(s)",
"Class": "license-file"
}
]
}
8 changes: 8 additions & 0 deletions pkg/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,13 @@ func NewConvertCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
ReportFlagGroup: flag.NewReportFlagGroup(),
}

// To display the summary table, we need to enable scanners (to build columns).
// We can't get scanner information from the report (we don't include empty licenses and secrets in the report).
// So we need to ask the user to configure scanners (if needed).
convertFlags.ScanFlagGroup.Scanners = flag.ScannersFlag.Clone()
convertFlags.ScanFlagGroup.Scanners.Default = nil // disable default scanners
convertFlags.ScanFlagGroup.Scanners.Usage = "List of scanners included when generating the json report. Used only for rendering the summary table."
Comment on lines +526 to +528
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we could just enable all scanners. But I think we need to use the same format(logic) for convert and other subcommands.
So i enabled --scanners that users can get summary table as for table format.


cmd := &cobra.Command{
Use: "convert [flags] RESULT_JSON",
Aliases: []string{"conv"},
Expand Down Expand Up @@ -977,6 +984,7 @@ func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
}
reportFlagGroup.Compliance = compliance // override usage as the accepted values differ for each subcommand.
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
reportFlagGroup.TableMode = nil // disable '--table-mode'

formatFlag := flag.FormatFlag.Clone()
formatFlag.Values = xstrings.ToStringSlice([]types.Format{
Expand Down
12 changes: 11 additions & 1 deletion pkg/commands/convert/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"context"
"encoding/json"
"os"
"slices"

"github.com/samber/lo"
"golang.org/x/xerrors"

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

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

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

log.Debug("Writing report to output...")
if len(opts.Scanners) == 0 && opts.Format == types.FormatTable && slices.Contains(opts.TableModes, types.Summary) {
logger.Info("To display the summary table, enable the scanners used during JSON report generation.")
opts.TableModes = lo.Filter(opts.TableModes, func(mode types.TableMode, _ int) bool {
return mode != types.Summary
})
}

logger.Debug("Writing report to output...")
if err = report.Write(ctx, r, opts); err != nil {
return xerrors.Errorf("unable to write results: %w", err)
}
Expand Down
1 change: 1 addition & 0 deletions pkg/flag/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ func (o *Options) ScanOpts() types.ScanOptions {
ImageConfigScanners: o.ImageConfigScanners, // this is valid only for 'image' subcommand
ScanRemovedPackages: o.ScanRemovedPkgs, // this is valid only for 'image' subcommand
LicenseCategories: o.LicenseCategories,
LicenseFull: o.LicenseFull,
FilePatterns: o.FilePatterns,
IncludeDevDeps: o.IncludeDevDeps,
Distro: o.Distro,
Expand Down
18 changes: 18 additions & 0 deletions pkg/flag/report_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ var (
ConfigName: "scan.show-suppressed",
Usage: "[EXPERIMENTAL] show suppressed vulnerabilities",
}
TableModeFlag = Flag[[]string]{
Name: "table-mode",
ConfigName: "table-mode",
Default: xstrings.ToStringSlice(types.SupportedTableModes),
Values: xstrings.ToStringSlice(types.SupportedTableModes),
Usage: "[EXPERIMENTAL] tables that will be displayed in 'table' format",
}
)

// ReportFlagGroup composes common printer flag structs
Expand All @@ -128,6 +135,7 @@ type ReportFlagGroup struct {
Severity *Flag[[]string]
Compliance *Flag[string]
ShowSuppressed *Flag[bool]
TableMode *Flag[[]string]
}

type ReportOptions struct {
Expand All @@ -145,6 +153,7 @@ type ReportOptions struct {
Severities []dbTypes.Severity
Compliance spec.ComplianceSpec
ShowSuppressed bool
TableModes []types.TableMode
}

func NewReportFlagGroup() *ReportFlagGroup {
Expand All @@ -163,6 +172,7 @@ func NewReportFlagGroup() *ReportFlagGroup {
Severity: SeverityFlag.Clone(),
Compliance: ComplianceFlag.Clone(),
ShowSuppressed: ShowSuppressedFlag.Clone(),
TableMode: TableModeFlag.Clone(),
}
}

Expand All @@ -186,6 +196,7 @@ func (f *ReportFlagGroup) Flags() []Flagger {
f.Severity,
f.Compliance,
f.ShowSuppressed,
f.TableMode,
}
}

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

if template != "" {
if format == "" {
Expand Down Expand Up @@ -227,6 +239,11 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) {
}
}

// "--table-mode" option is available only with "--format table".
if viper.IsSet(TableModeFlag.ConfigName) && format != types.FormatTable {
return ReportOptions{}, xerrors.New(`"--table-mode" 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)
Expand Down Expand Up @@ -259,6 +276,7 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) {
Severities: toSeverity(f.Severity.Value()),
Compliance: cs,
ShowSuppressed: f.ShowSuppressed.Value(),
TableModes: xstrings.ToTSlice[types.TableMode](tableModes),
}, nil
}

Expand Down
19 changes: 18 additions & 1 deletion pkg/flag/report_flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
iacTypes "github.com/aquasecurity/trivy/pkg/iac/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
xstrings "github.com/aquasecurity/trivy/pkg/x/strings"
)

func TestReportFlagGroup_ToOptions(t *testing.T) {
Expand All @@ -32,11 +33,13 @@ func TestReportFlagGroup_ToOptions(t *testing.T) {
compliance string
debug bool
pkgTypes string
tableModes []string
}
tests := []struct {
name string
fields fields
want flag.ReportOptions
wantErr string
wantLogs []string
}{
{
Expand Down Expand Up @@ -160,6 +163,14 @@ func TestReportFlagGroup_ToOptions(t *testing.T) {
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
},
},
{
name: "invalid option combination: --table-modes with --format json",
fields: fields{
format: "json",
tableModes: xstrings.ToStringSlice(types.SupportedTableModes),
},
wantErr: `"--table-mode" can be used only with "--format table".`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -184,6 +195,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)
setSliceValue(flag.TableModeFlag.ConfigName, tt.fields.tableModes)

// Assert options
f := &flag.ReportFlagGroup{
Expand All @@ -199,10 +211,15 @@ func TestReportFlagGroup_ToOptions(t *testing.T) {
OutputPluginArg: flag.OutputPluginArgFlag.Clone(),
Severity: flag.SeverityFlag.Clone(),
Compliance: flag.ComplianceFlag.Clone(),
TableMode: flag.TableModeFlag.Clone(),
}

got, err := f.ToOptions()
require.NoError(t, err)
if tt.wantErr != "" {
require.Contains(t, err.Error(), tt.wantErr)
return
}

assert.EqualExportedValuesf(t, tt.want, got, "ToOptions()")

// Assert log messages
Expand Down
Loading