BREAKING: add support for k8s disable-node-collector flag (#6311)

Signed-off-by: chenk <hen.keinan@gmail.com>
This commit is contained in:
chenk
2024-05-02 14:08:59 +03:00
committed by GitHub
parent 194a814688
commit c6d5d856ce
11 changed files with 114 additions and 114 deletions

View File

@@ -34,11 +34,11 @@ trivy kubernetes [flags] [CONTEXT]
--cache-ttl duration cache TTL when using redis as cache backend
--clear-cache clear image caches without scanning
--compliance string compliance report to generate (k8s-nsa,k8s-cis,k8s-pss-baseline,k8s-pss-restricted)
--components strings specify which components to scan (workload,infra) (default [workload,infra])
--config-data strings specify paths from which data for the Rego policies will be recursively loaded
--config-policy strings specify the paths to the Rego policy files or to the directories containing them, applying config files
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
--disable-node-collector When the flag is activated, the node-collector job will not be executed, thus skipping misconfiguration findings on the node.
--download-db-only download/update vulnerability database but don't run a scan
--download-java-db-only download/update Java index database but don't run a scan
--exclude-kinds strings indicate the kinds exclude from scanning (example: node)

View File

@@ -15,19 +15,6 @@ var (
ConfigName: "kubernetes.kubeconfig",
Usage: "specify the kubeconfig file path to use",
}
ComponentsFlag = Flag[[]string]{
Name: "components",
ConfigName: "kubernetes.components",
Default: []string{
"workload",
"infra",
},
Values: []string{
"workload",
"infra",
},
Usage: "specify which components to scan",
}
K8sVersionFlag = Flag[string]{
Name: "k8s-version",
ConfigName: "kubernetes.k8s-version",
@@ -38,6 +25,11 @@ var (
ConfigName: "kubernetes.tolerations",
Usage: "specify node-collector job tolerations (example: key1=value1:NoExecute,key2=value2:NoSchedule)",
}
DisableNodeCollector = Flag[bool]{
Name: "disable-node-collector",
ConfigName: "kubernetes.disableNodeCollector",
Usage: "When the flag is activated, the node-collector job will not be executed, thus skipping misconfiguration findings on the node.",
}
NodeCollectorNamespace = Flag[string]{
Name: "node-collector-namespace",
ConfigName: "kubernetes.node-collector.namespace",
@@ -97,9 +89,9 @@ var (
type K8sFlagGroup struct {
KubeConfig *Flag[string]
Components *Flag[[]string]
K8sVersion *Flag[string]
Tolerations *Flag[[]string]
DisableNodeCollector *Flag[bool]
NodeCollectorImageRef *Flag[string]
NodeCollectorNamespace *Flag[string]
ExcludeOwned *Flag[bool]
@@ -114,12 +106,12 @@ type K8sFlagGroup struct {
type K8sOptions struct {
KubeConfig string
Components []string
K8sVersion string
Tolerations []corev1.Toleration
NodeCollectorImageRef string
NodeCollectorNamespace string
ExcludeOwned bool
DisableNodeCollector bool
ExcludeNodes map[string]string
ExcludeKinds []string
IncludeKinds []string
@@ -132,9 +124,9 @@ type K8sOptions struct {
func NewK8sFlagGroup() *K8sFlagGroup {
return &K8sFlagGroup{
KubeConfig: KubeConfigFlag.Clone(),
Components: ComponentsFlag.Clone(),
K8sVersion: K8sVersionFlag.Clone(),
Tolerations: TolerationsFlag.Clone(),
DisableNodeCollector: DisableNodeCollector.Clone(),
NodeCollectorNamespace: NodeCollectorNamespace.Clone(),
ExcludeOwned: ExcludeOwned.Clone(),
ExcludeNodes: ExcludeNodes.Clone(),
@@ -155,8 +147,8 @@ func (f *K8sFlagGroup) Name() string {
func (f *K8sFlagGroup) Flags() []Flagger {
return []Flagger{
f.KubeConfig,
f.Components,
f.K8sVersion,
f.DisableNodeCollector,
f.Tolerations,
f.NodeCollectorNamespace,
f.ExcludeOwned,
@@ -199,9 +191,9 @@ func (f *K8sFlagGroup) ToOptions() (K8sOptions, error) {
return K8sOptions{
KubeConfig: f.KubeConfig.Value(),
Components: f.Components.Value(),
K8sVersion: f.K8sVersion.Value(),
Tolerations: tolerations,
DisableNodeCollector: f.DisableNodeCollector.Value(),
NodeCollectorNamespace: f.NodeCollectorNamespace.Value(),
ExcludeOwned: f.ExcludeOwned.Value(),
ExcludeNodes: exludeNodeLabels,

View File

@@ -360,15 +360,10 @@ func (o *Options) Align() {
}
// Vulnerability scanning is disabled by default for CycloneDX.
if o.Format == types.FormatCycloneDX && !viper.IsSet(ScannersFlag.ConfigName) && len(o.K8sOptions.Components) == 0 { // remove K8sOptions.Components validation check when vuln scan is supported for k8s report with cycloneDX
if o.Format == types.FormatCycloneDX && !viper.IsSet(ScannersFlag.ConfigName) {
log.Info(`"--format cyclonedx" disables security scanning. Specify "--scanners vuln" explicitly if you want to include vulnerabilities in the CycloneDX report.`)
o.Scanners = nil
}
if o.Format == types.FormatCycloneDX && len(o.K8sOptions.Components) > 0 {
log.Info(`"k8s with --format cyclonedx" disable security scanning`)
o.Scanners = nil
}
}
// RegistryOpts returns options for OCI registries

View File

@@ -3,13 +3,13 @@ package commands
import (
"context"
"golang.org/x/exp/slices"
"golang.org/x/xerrors"
k8sArtifacts "github.com/aquasecurity/trivy-kubernetes/pkg/artifacts"
"github.com/aquasecurity/trivy-kubernetes/pkg/k8s"
"github.com/aquasecurity/trivy-kubernetes/pkg/trivyk8s"
"github.com/aquasecurity/trivy/pkg/flag"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
)
@@ -34,7 +34,7 @@ func clusterRun(ctx context.Context, opts flag.Options, cluster k8s.Cluster) err
trivyk8s.WithIncludeKinds(opts.IncludeKinds),
trivyk8s.WithExcludeOwned(opts.ExcludeOwned),
}
if opts.Scanners.AnyEnabled(types.MisconfigScanner) && slices.Contains(opts.Components, "infra") {
if opts.Scanners.AnyEnabled(types.MisconfigScanner) && !opts.DisableNodeCollector {
artifacts, err = trivyk8s.New(cluster, k8sOpts...).ListArtifactAndNodeInfo(ctx,
trivyk8s.WithScanJobNamespace(opts.NodeCollectorNamespace),
trivyk8s.WithIgnoreLabels(opts.ExcludeNodes),
@@ -53,6 +53,10 @@ func clusterRun(ctx context.Context, opts flag.Options, cluster k8s.Cluster) err
return xerrors.Errorf(`unknown format %q. Use "json" or "table" or "cyclonedx"`, opts.Format)
}
if !opts.DisableNodeCollector && !opts.Quiet {
log.InfoContext(ctx, "Node scanning is enabled")
log.InfoContext(ctx, "If you want to disable Node scanning via an in-cluster Job, please try '--disable-node-collector' to disable the Node-Collector job.")
}
runner := newRunner(opts, cluster.GetCurrentContext())
return runner.run(ctx, artifacts)
}

View File

@@ -115,7 +115,6 @@ func (r *runner) run(ctx context.Context, artifacts []*k8sArtifacts.Artifact) er
Report: r.flagOpts.ReportFormat,
Output: output,
Severities: r.flagOpts.Severities,
Components: r.flagOpts.Components,
Scanners: r.flagOpts.ScanOptions.Scanners,
APIVersion: r.flagOpts.AppVersion,
}); err != nil {

View File

@@ -33,7 +33,6 @@ type Option struct {
Severities []dbTypes.Severity
ColumnHeading []string
Scanners types.Scanners
Components []string
APIVersion string
}
@@ -134,12 +133,12 @@ type reports struct {
// - misconfiguration report
// - rbac report
// - infra checks report
func SeparateMisconfigReports(k8sReport Report, scanners types.Scanners, components []string) []reports {
func SeparateMisconfigReports(k8sReport Report, scanners types.Scanners) []reports {
var workloadMisconfig, infraMisconfig, rbacAssessment, workloadVulnerabilities, infraVulnerabilities, workloadResource []Resource
for _, resource := range k8sReport.Resources {
switch {
case vulnerabilitiesOrSecretResource(resource):
case vulnerabilitiesOrSecretResource(resource) && !infraResource(resource):
if resource.Namespace == infraNamespace || nodeInfoResource(resource) {
infraVulnerabilities = append(infraVulnerabilities, nodeKind(resource))
} else {
@@ -150,8 +149,7 @@ func SeparateMisconfigReports(k8sReport Report, scanners types.Scanners, compone
case infraResource(resource):
infraMisconfig = append(infraMisconfig, nodeKind(resource))
case scanners.Enabled(types.MisconfigScanner) &&
!rbacResource(resource) &&
slices.Contains(components, workloadComponent):
!rbacResource(resource):
workloadMisconfig = append(workloadMisconfig, resource)
}
}
@@ -159,22 +157,21 @@ func SeparateMisconfigReports(k8sReport Report, scanners types.Scanners, compone
var r []reports
workloadResource = append(workloadResource, workloadVulnerabilities...)
workloadResource = append(workloadResource, workloadMisconfig...)
if shouldAddToReport(scanners, components, workloadComponent) {
if shouldAddToReport(scanners) {
workloadReport := Report{
SchemaVersion: 0,
ClusterName: k8sReport.ClusterName,
Resources: workloadResource,
name: "Workload Assessment",
}
if slices.Contains(components, workloadComponent) {
r = append(r, reports{
Report: workloadReport,
Columns: WorkloadColumns(),
})
}
r = append(r, reports{
Report: workloadReport,
Columns: WorkloadColumns(),
})
}
infraMisconfig = append(infraMisconfig, infraVulnerabilities...)
if shouldAddToReport(scanners, components, infraComponent) {
if shouldAddToReport(scanners) {
r = append(r, reports{
Report: Report{
SchemaVersion: 0,
@@ -266,12 +263,11 @@ func (r Report) PrintErrors() {
}
}
func shouldAddToReport(scanners types.Scanners, components []string, componentType string) bool {
func shouldAddToReport(scanners types.Scanners) bool {
return scanners.AnyEnabled(
types.MisconfigScanner,
types.VulnerabilityScanner,
types.SecretScanner) &&
slices.Contains(components, componentType)
types.SecretScanner)
}
func vulnerabilitiesOrSecretResource(resource Resource) bool {

View File

@@ -515,7 +515,6 @@ func Test_separateMisconfigReports(t *testing.T) {
name string
k8sReport Report
scanners types.Scanners
components []string
expectedReports []Report
}{
{
@@ -525,10 +524,6 @@ func Test_separateMisconfigReports(t *testing.T) {
types.MisconfigScanner,
types.RBACScanner,
},
components: []string{
workloadComponent,
infraComponent,
},
expectedReports: []Report{
// the order matter for the test
{
@@ -545,10 +540,6 @@ func Test_separateMisconfigReports(t *testing.T) {
name: "Config and Infra for the same resource",
k8sReport: k8sReport,
scanners: types.Scanners{types.MisconfigScanner},
components: []string{
workloadComponent,
infraComponent,
},
expectedReports: []Report{
// the order matter for the test
{
@@ -569,10 +560,9 @@ func Test_separateMisconfigReports(t *testing.T) {
},
},
{
name: "Config Report Only",
k8sReport: k8sReport,
scanners: types.Scanners{types.MisconfigScanner},
components: []string{workloadComponent},
name: "Config Report Only",
k8sReport: k8sReport,
scanners: types.Scanners{types.MisconfigScanner},
expectedReports: []Report{
{
Resources: []Resource{
@@ -580,15 +570,29 @@ func Test_separateMisconfigReports(t *testing.T) {
{Kind: "StatefulSet"},
},
},
{
Resources: []Resource{
{Kind: "Pod"},
},
},
},
},
{
name: "Infra Report Only",
k8sReport: k8sReport,
scanners: types.Scanners{types.MisconfigScanner},
components: []string{infraComponent},
name: "Infra Report Only",
k8sReport: k8sReport,
scanners: types.Scanners{types.MisconfigScanner},
expectedReports: []Report{
{Resources: []Resource{{Kind: "Pod"}}},
{
Resources: []Resource{
{Kind: "Deployment"},
{Kind: "StatefulSet"},
},
},
{
Resources: []Resource{
{Kind: "Pod"},
},
},
},
},
@@ -597,7 +601,7 @@ func Test_separateMisconfigReports(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reports := SeparateMisconfigReports(tt.k8sReport, tt.scanners, tt.components)
reports := SeparateMisconfigReports(tt.k8sReport, tt.scanners)
assert.Equal(t, len(tt.expectedReports), len(reports))
for i := range reports {

View File

@@ -35,7 +35,7 @@ func NewSummaryWriter(output io.Writer, requiredSevs []dbTypes.Severity, columnH
}
}
func ColumnHeading(scanners types.Scanners, components, availableColumns []string) []string {
func ColumnHeading(scanners types.Scanners, availableColumns []string) []string {
columns := []string{
NamespaceColumn,
ResourceColumn,
@@ -47,12 +47,7 @@ func ColumnHeading(scanners types.Scanners, components, availableColumns []strin
case types.VulnerabilityScanner:
securityOptions[VulnerabilitiesColumn] = nil
case types.MisconfigScanner:
if slices.Contains(components, workloadComponent) {
securityOptions[MisconfigurationsColumn] = nil
}
if slices.Contains(components, infraComponent) {
securityOptions[MisconfigurationsColumn] = nil
}
securityOptions[MisconfigurationsColumn] = nil
case types.SecretScanner:
securityOptions[SecretsColumn] = nil
case types.RBACScanner:

View File

@@ -20,7 +20,6 @@ func TestReport_ColumnHeading(t *testing.T) {
tests := []struct {
name string
scanners types.Scanners
components []string
availableColumns []string
want []string
}{
@@ -28,10 +27,6 @@ func TestReport_ColumnHeading(t *testing.T) {
name: "filter workload columns",
scanners: allScanners,
availableColumns: WorkloadColumns(),
components: []string{
workloadComponent,
infraComponent,
},
want: []string{
NamespaceColumn,
ResourceColumn,
@@ -43,7 +38,6 @@ func TestReport_ColumnHeading(t *testing.T) {
{
name: "filter rbac columns",
scanners: allScanners,
components: []string{},
availableColumns: RoleColumns(),
want: []string{
NamespaceColumn,
@@ -52,12 +46,8 @@ func TestReport_ColumnHeading(t *testing.T) {
},
},
{
name: "filter infra columns",
scanners: allScanners,
components: []string{
workloadComponent,
infraComponent,
},
name: "filter infra columns",
scanners: allScanners,
availableColumns: InfraColumns(),
want: []string{
NamespaceColumn,
@@ -68,12 +58,8 @@ func TestReport_ColumnHeading(t *testing.T) {
},
},
{
name: "config column only",
scanners: types.Scanners{types.MisconfigScanner},
components: []string{
workloadComponent,
infraComponent,
},
name: "config column only",
scanners: types.Scanners{types.MisconfigScanner},
availableColumns: WorkloadColumns(),
want: []string{
NamespaceColumn,
@@ -84,7 +70,6 @@ func TestReport_ColumnHeading(t *testing.T) {
{
name: "secret column only",
scanners: types.Scanners{types.SecretScanner},
components: []string{},
availableColumns: WorkloadColumns(),
want: []string{
NamespaceColumn,
@@ -95,7 +80,6 @@ func TestReport_ColumnHeading(t *testing.T) {
{
name: "vuln column only",
scanners: types.Scanners{types.VulnerabilityScanner},
components: []string{},
availableColumns: WorkloadColumns(),
want: []string{
NamespaceColumn,
@@ -107,7 +91,7 @@ func TestReport_ColumnHeading(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
column := ColumnHeading(tt.scanners, tt.components, tt.availableColumns)
column := ColumnHeading(tt.scanners, tt.availableColumns)
if !assert.Equal(t, column, tt.want) {
t.Error(fmt.Errorf("TestReport_ColumnHeading want %v got %v", tt.want, column))
}

View File

@@ -23,7 +23,7 @@ func Write(ctx context.Context, k8sreport report.Report, option report.Option) e
}
return jwriter.Write(k8sreport)
case types.FormatTable:
separatedReports := report.SeparateMisconfigReports(k8sreport, option.Scanners, option.Components)
separatedReports := report.SeparateMisconfigReports(k8sreport, option.Scanners)
if option.Report == report.SummaryReport {
target := fmt.Sprintf("Summary Report for %s", k8sreport.ClusterName)
@@ -35,7 +35,7 @@ func Write(ctx context.Context, k8sreport report.Report, option report.Option) e
Output: option.Output,
Report: option.Report,
Severities: option.Severities,
ColumnHeading: report.ColumnHeading(option.Scanners, option.Components, r.Columns),
ColumnHeading: report.ColumnHeading(option.Scanners, r.Columns),
}
if err := writer.Write(ctx, r.Report); err != nil {

View File

@@ -23,9 +23,6 @@ const (
tableFormat = "table"
jsonFormat = "json"
cycloneDXFormat = "cyclonedx"
workloadComponent = "workload"
infraComponent = "infra"
)
var (
@@ -202,18 +199,16 @@ func TestReportWrite_Summary(t *testing.T) {
report report.Report
opt report.Option
scanners types.Scanners
components []string
severities []dbTypes.Severity
expectedOutput string
}{
{
name: "Only config, all severities",
name: "Only config, all serverities",
report: report.Report{
ClusterName: "test",
Resources: []report.Resource{deployOrionWithMisconfigs},
},
scanners: types.Scanners{types.MisconfigScanner},
components: []string{workloadComponent},
severities: allSeverities,
expectedOutput: `Summary Report for test
=======================
@@ -226,16 +221,24 @@ Workload Assessment
├───────────┼──────────────┼───┼───┼───┼───┼───┤
│ default │ Deploy/orion │ 1 │ 2 │ 1 │ 2 │ 1 │
└───────────┴──────────────┴───┴───┴───┴───┴───┘
Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN
Infra Assessment
┌───────────┬──────────┬───────────────────┐
│ Namespace │ Resource │ Misconfigurations │
│ │ ├───┬───┬───┬───┬───┤
│ │ │ C │ H │ M │ L │ U │
└───────────┴──────────┴───┴───┴───┴───┴───┘
Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
},
{
name: "Only vuln, all severities",
name: "Only vuln, all serverities",
report: report.Report{
ClusterName: "test",
Resources: []report.Resource{deployOrionWithVulns},
},
scanners: types.Scanners{types.VulnerabilityScanner},
components: []string{workloadComponent},
severities: allSeverities,
expectedOutput: `Summary Report for test
=======================
@@ -248,10 +251,19 @@ Workload Assessment
├───────────┼──────────────┼───┼───┼───┼───┼───┤
│ default │ Deploy/orion │ 2 │ 1 │ 2 │ 1 │ 1 │
└───────────┴──────────────┴───┴───┴───┴───┴───┘
Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN
Infra Assessment
┌───────────┬──────────┬───────────────────┐
│ Namespace │ Resource │ Vulnerabilities │
│ │ ├───┬───┬───┬───┬───┤
│ │ │ C │ H │ M │ L │ U │
└───────────┴──────────┴───┴───┴───┴───┴───┘
Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
},
{
name: "Only rbac, all severities",
name: "Only rbac, all serverities",
report: report.Report{
ClusterName: "test",
Resources: []report.Resource{roleWithMisconfig},
@@ -272,13 +284,12 @@ RBAC Assessment
Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
},
{
name: "Only secret, all severities",
name: "Only secret, all serverities",
report: report.Report{
ClusterName: "test",
Resources: []report.Resource{deployLuaWithSecrets},
},
scanners: types.Scanners{types.SecretScanner},
components: []string{workloadComponent},
severities: allSeverities,
expectedOutput: `Summary Report for test
=======================
@@ -291,20 +302,37 @@ Workload Assessment
├───────────┼────────────┼───┼───┼───┼───┼───┤
│ default │ Deploy/lua │ 1 │ │ 1 │ │ │
└───────────┴────────────┴───┴───┴───┴───┴───┘
Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN
Infra Assessment
┌───────────┬──────────┬───────────────────┐
│ Namespace │ Resource │ Secrets │
│ │ ├───┬───┬───┬───┬───┤
│ │ │ C │ H │ M │ L │ U │
└───────────┴──────────┴───┴───┴───┴───┴───┘
Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
},
{
name: "apiserver, only infra and severities",
name: "apiserver, only infra and serverities",
report: report.Report{
ClusterName: "test",
Resources: []report.Resource{apiseverPodWithMisconfigAndInfra},
},
scanners: types.Scanners{types.MisconfigScanner},
components: []string{infraComponent},
severities: allSeverities,
expectedOutput: `Summary Report for test
=======================
Workload Assessment
┌───────────┬──────────┬───────────────────┐
│ Namespace │ Resource │ Misconfigurations │
│ │ ├───┬───┬───┬───┬───┤
│ │ │ C │ H │ M │ L │ U │
└───────────┴──────────┴───┴───┴───┴───┴───┘
Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN
Infra Assessment
┌─────────────┬────────────────────┬───────────────────┐
│ Namespace │ Resource │ Misconfigurations │
@@ -316,7 +344,7 @@ Infra Assessment
Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
},
{
name: "apiserver, vuln,config,secret and severities",
name: "apiserver, vuln,config,secret and serverities",
report: report.Report{
ClusterName: "test",
Resources: []report.Resource{apiseverPodWithMisconfigAndInfra},
@@ -326,11 +354,19 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
types.MisconfigScanner,
types.SecretScanner,
},
components: []string{infraComponent},
severities: allSeverities,
expectedOutput: `Summary Report for test
=======================
Workload Assessment
┌───────────┬──────────┬───────────────────┬───────────────────┬───────────────────┐
│ Namespace │ Resource │ Vulnerabilities │ Misconfigurations │ Secrets │
│ │ ├───┬───┬───┬───┬───┼───┬───┬───┬───┬───┼───┬───┬───┬───┬───┤
│ │ │ C │ H │ M │ L │ U │ C │ H │ M │ L │ U │ C │ H │ M │ L │ U │
└───────────┴──────────┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN
Infra Assessment
┌─────────────┬────────────────────┬───────────────────┬───────────────────┬───────────────────┐
│ Namespace │ Resource │ Vulnerabilities │ Misconfigurations │ Secrets │
@@ -342,7 +378,7 @@ Infra Assessment
Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
},
{
name: "apiserver, all misconfig and vuln scanners and severities",
name: "apiserver, all misconfig and vuln scanners and serverities",
report: report.Report{
ClusterName: "test",
Resources: []report.Resource{apiseverPodWithMisconfigAndInfra},
@@ -351,10 +387,6 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
types.MisconfigScanner,
types.VulnerabilityScanner,
},
components: []string{
workloadComponent,
infraComponent,
},
severities: allSeverities,
expectedOutput: `Summary Report for test
=======================
@@ -390,7 +422,6 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
Output: &output,
Scanners: tc.scanners,
Severities: tc.severities,
Components: tc.components,
}
err := Write(context.Background(), tc.report, opt)