-
Notifications
You must be signed in to change notification settings - Fork 2.6k
feat: add report summary table #8177
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
Changes from 11 commits
ff62336
ca75fc9
ef26940
3ea064f
8b2acf7
751823a
37a072d
f4f46a5
4292e65
19fc0fe
a27f879
239251b
8b9d908
e11221a
5f61893
85edf16
d5ca966
eb4d2fa
a57dcc6
4f349c5
84169b4
892d6de
db0cbe1
28ebb24
0166a45
5f13964
7f863b9
2628b70
c78bfe5
ba8f756
d37ad35
786e704
9c3e8ce
878b9f4
4b6214b
29388c4
8de06d7
5ea3e4f
1159d63
5b048c1
fd00dc9
67a729c
f648dc6
ea453d8
9d58ab2
5b6e9ea
71a53b2
df6c0de
014e4c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -109,6 +109,11 @@ var ( | |||||
ConfigName: "scan.show-suppressed", | ||||||
Usage: "[EXPERIMENTAL] show suppressed vulnerabilities", | ||||||
} | ||||||
NoSummaryTableFlag = Flag[bool]{ | ||||||
Name: "no-summary-table", | ||||||
ConfigName: "no-summary-table", | ||||||
Usage: "hide summary table", | ||||||
} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a major UI change, so there might be some backlash from the community. In that case, since switching from opt-out to opt-in is possible, it might be a good idea to mark it as experimental. Also, as @simar7 pointed out, boolean flags are not very flexible. Specifying it as something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hm... it make sense. I will update PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are several options:
Also,
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a good idea.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My friend :) recommended When deciding on terminology to distinguish between a concise summary output and a more exhaustive one, many tools and UIs use the terms “Summary” and “Detailed.” While “Detail” or “Details” might seem similar at first glance, “detailed” is often chosen for the following reasons:
In short, “detailed” pairs nicely with “summary” and is more recognizable to users as the counterpart for a “deep-dive” option, while “detail” or “details” tend to read more like nouns referring to the actual data rather than the presentation style. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the interest of not committing bike shedding... I'll vote for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed Perhaps you will have more ideas. |
||||||
) | ||||||
|
||||||
// ReportFlagGroup composes common printer flag structs | ||||||
|
@@ -128,6 +133,7 @@ type ReportFlagGroup struct { | |||||
Severity *Flag[[]string] | ||||||
Compliance *Flag[string] | ||||||
ShowSuppressed *Flag[bool] | ||||||
NoSummaryTable *Flag[bool] | ||||||
} | ||||||
|
||||||
type ReportOptions struct { | ||||||
|
@@ -145,6 +151,7 @@ type ReportOptions struct { | |||||
Severities []dbTypes.Severity | ||||||
Compliance spec.ComplianceSpec | ||||||
ShowSuppressed bool | ||||||
NoSummaryTable bool | ||||||
} | ||||||
|
||||||
func NewReportFlagGroup() *ReportFlagGroup { | ||||||
|
@@ -163,6 +170,7 @@ func NewReportFlagGroup() *ReportFlagGroup { | |||||
Severity: SeverityFlag.Clone(), | ||||||
Compliance: ComplianceFlag.Clone(), | ||||||
ShowSuppressed: ShowSuppressedFlag.Clone(), | ||||||
NoSummaryTable: NoSummaryTableFlag.Clone(), | ||||||
} | ||||||
} | ||||||
|
||||||
|
@@ -186,6 +194,7 @@ func (f *ReportFlagGroup) Flags() []Flagger { | |||||
f.Severity, | ||||||
f.Compliance, | ||||||
f.ShowSuppressed, | ||||||
f.NoSummaryTable, | ||||||
} | ||||||
} | ||||||
|
||||||
|
@@ -198,6 +207,7 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) { | |||||
template := f.Template.Value() | ||||||
dependencyTree := f.DependencyTree.Value() | ||||||
listAllPkgs := f.ListAllPkgs.Value() | ||||||
noSummaryTable := f.NoSummaryTable.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 noSummaryTable && format != types.FormatTable { | ||||||
noSummaryTable = false | ||||||
log.Warn(`"--no-summary-table" can be used only with "--format table".`) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For example, suppose a flag has already been added for a long time, and we don't want to terminate a program for backward compatibility as it will change behavior. In that case, we have to make it a warning, but an error is basically better because the user may not see the warning message.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. changed in 786e704 |
||||||
} | ||||||
|
||||||
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(), | ||||||
NoSummaryTable: noSummaryTable, | ||||||
}, nil | ||||||
} | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Uh oh!
There was an error while loading. Please reload this page.