mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-23 07:29:00 -08:00
feat: unified k8s scan resources (#4188)
This commit is contained in:
@@ -95,10 +95,7 @@ func (r *runner) run(ctx context.Context, artifacts []*artifacts.Artifact) error
|
||||
|
||||
if r.flagOpts.Compliance.Spec.ID != "" {
|
||||
var scanResults []types.Results
|
||||
for _, rss := range rpt.Vulnerabilities {
|
||||
scanResults = append(scanResults, rss.Results)
|
||||
}
|
||||
for _, rss := range rpt.Misconfigurations {
|
||||
for _, rss := range rpt.Resources {
|
||||
scanResults = append(scanResults, rss.Results)
|
||||
}
|
||||
complianceReport, err := cr.BuildComplianceReport(scanResults, r.flagOpts.Compliance)
|
||||
|
||||
@@ -40,11 +40,10 @@ type Option struct {
|
||||
|
||||
// Report represents a kubernetes scan report
|
||||
type Report struct {
|
||||
SchemaVersion int `json:",omitempty"`
|
||||
ClusterName string
|
||||
Vulnerabilities []Resource `json:",omitempty"`
|
||||
Misconfigurations []Resource `json:",omitempty"`
|
||||
name string
|
||||
SchemaVersion int `json:",omitempty"`
|
||||
ClusterName string
|
||||
Resources []Resource `json:",omitempty"`
|
||||
name string
|
||||
}
|
||||
|
||||
// ConsolidatedReport represents a kubernetes scan report with consolidated findings
|
||||
@@ -74,18 +73,11 @@ func (r Resource) fullname() string {
|
||||
|
||||
// Failed returns whether the k8s report includes any vulnerabilities or misconfigurations
|
||||
func (r Report) Failed() bool {
|
||||
for _, v := range r.Vulnerabilities {
|
||||
for _, v := range r.Resources {
|
||||
if v.Results.Failed() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range r.Misconfigurations {
|
||||
if m.Results.Failed() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -96,12 +88,16 @@ func (r Report) consolidate() ConsolidatedReport {
|
||||
}
|
||||
|
||||
index := make(map[string]Resource)
|
||||
|
||||
for _, m := range r.Misconfigurations {
|
||||
index[m.fullname()] = m
|
||||
vulnerabilities := make([]Resource, 0)
|
||||
for _, m := range r.Resources {
|
||||
if vulnerabilitiesOrSecretResource(m) {
|
||||
vulnerabilities = append(vulnerabilities, m)
|
||||
} else {
|
||||
index[m.fullname()] = m
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range r.Vulnerabilities {
|
||||
for _, v := range vulnerabilities {
|
||||
key := v.fullname()
|
||||
|
||||
if res, ok := index[key]; ok {
|
||||
@@ -181,13 +177,19 @@ func separateMisconfigReports(k8sReport Report, scanners types.Scanners, compone
|
||||
workloadMisconfig := make([]Resource, 0)
|
||||
infraMisconfig := make([]Resource, 0)
|
||||
rbacAssessment := make([]Resource, 0)
|
||||
workloadVulnerabilities := make([]Resource, 0)
|
||||
workloadResource := make([]Resource, 0)
|
||||
for _, resource := range k8sReport.Resources {
|
||||
if vulnerabilitiesOrSecretResource(resource) {
|
||||
workloadVulnerabilities = append(workloadVulnerabilities, resource)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, misConfig := range k8sReport.Misconfigurations {
|
||||
switch {
|
||||
case scanners.Enabled(types.RBACScanner) && rbacResource(misConfig):
|
||||
rbacAssessment = append(rbacAssessment, misConfig)
|
||||
case infraResource(misConfig):
|
||||
workload, infra := splitInfraAndWorkloadResources(misConfig)
|
||||
case scanners.Enabled(types.RBACScanner) && rbacResource(resource):
|
||||
rbacAssessment = append(rbacAssessment, resource)
|
||||
case infraResource(resource):
|
||||
workload, infra := splitInfraAndWorkloadResources(resource)
|
||||
|
||||
if slices.Contains(components, infraComponent) {
|
||||
infraMisconfig = append(infraMisconfig, infra)
|
||||
@@ -197,27 +199,27 @@ func separateMisconfigReports(k8sReport Report, scanners types.Scanners, compone
|
||||
workloadMisconfig = append(workloadMisconfig, workload)
|
||||
}
|
||||
|
||||
case scanners.Enabled(types.MisconfigScanner) && !rbacResource(misConfig):
|
||||
case scanners.Enabled(types.MisconfigScanner) && !rbacResource(resource):
|
||||
if slices.Contains(components, workloadComponent) {
|
||||
workloadMisconfig = append(workloadMisconfig, misConfig)
|
||||
workloadMisconfig = append(workloadMisconfig, resource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r := make([]reports, 0)
|
||||
|
||||
workloadResource = append(workloadResource, workloadVulnerabilities...)
|
||||
workloadResource = append(workloadResource, workloadMisconfig...)
|
||||
if shouldAddWorkloadReport(scanners) {
|
||||
workloadReport := Report{
|
||||
SchemaVersion: 0,
|
||||
ClusterName: k8sReport.ClusterName,
|
||||
Misconfigurations: workloadMisconfig,
|
||||
Vulnerabilities: k8sReport.Vulnerabilities,
|
||||
name: "Workload Assessment",
|
||||
SchemaVersion: 0,
|
||||
ClusterName: k8sReport.ClusterName,
|
||||
Resources: workloadResource,
|
||||
name: "Workload Assessment",
|
||||
}
|
||||
|
||||
if (slices.Contains(components, workloadComponent) &&
|
||||
len(workloadMisconfig) > 0) ||
|
||||
len(k8sReport.Vulnerabilities) > 0 {
|
||||
len(workloadVulnerabilities) > 0 {
|
||||
r = append(r, reports{
|
||||
report: workloadReport,
|
||||
columns: WorkloadColumns(),
|
||||
@@ -228,10 +230,10 @@ func separateMisconfigReports(k8sReport Report, scanners types.Scanners, compone
|
||||
if scanners.Enabled(types.RBACScanner) && len(rbacAssessment) > 0 {
|
||||
r = append(r, reports{
|
||||
report: Report{
|
||||
SchemaVersion: 0,
|
||||
ClusterName: k8sReport.ClusterName,
|
||||
Misconfigurations: rbacAssessment,
|
||||
name: "RBAC Assessment",
|
||||
SchemaVersion: 0,
|
||||
ClusterName: k8sReport.ClusterName,
|
||||
Resources: rbacAssessment,
|
||||
name: "RBAC Assessment",
|
||||
},
|
||||
columns: RoleColumns(),
|
||||
})
|
||||
@@ -243,10 +245,10 @@ func separateMisconfigReports(k8sReport Report, scanners types.Scanners, compone
|
||||
|
||||
r = append(r, reports{
|
||||
report: Report{
|
||||
SchemaVersion: 0,
|
||||
ClusterName: k8sReport.ClusterName,
|
||||
Misconfigurations: infraMisconfig,
|
||||
name: "Infra Assessment",
|
||||
SchemaVersion: 0,
|
||||
ClusterName: k8sReport.ClusterName,
|
||||
Resources: infraMisconfig,
|
||||
name: "Infra Assessment",
|
||||
},
|
||||
columns: InfraColumns(),
|
||||
})
|
||||
@@ -292,15 +294,9 @@ func CreateResource(artifact *artifacts.Artifact, report types.Report, err error
|
||||
}
|
||||
|
||||
func (r Report) printErrors() {
|
||||
for _, resource := range r.Vulnerabilities {
|
||||
for _, resource := range r.Resources {
|
||||
if resource.Error != "" {
|
||||
log.Logger.Errorf("Error during vulnerabilities scan: %s", resource.Error)
|
||||
}
|
||||
}
|
||||
|
||||
for _, resource := range r.Misconfigurations {
|
||||
if resource.Error != "" {
|
||||
log.Logger.Errorf("Error during misconfiguration scan: %s", resource.Error)
|
||||
log.Logger.Errorf("Error during vulnerabilities or misconfiguration scan: %s", resource.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -366,3 +362,7 @@ func copyResult(r types.Result, misconfigs []types.DetectedMisconfiguration) typ
|
||||
func shouldAddWorkloadReport(scanners types.Scanners) bool {
|
||||
return scanners.AnyEnabled(types.MisconfigScanner, types.VulnerabilityScanner, types.SecretScanner)
|
||||
}
|
||||
|
||||
func vulnerabilitiesOrSecretResource(resource Resource) bool {
|
||||
return len(resource.Results) > 0 && (len(resource.Results[0].Vulnerabilities) > 0 || len(resource.Results[0].Secrets) > 0)
|
||||
}
|
||||
|
||||
@@ -283,11 +283,9 @@ func TestReport_consolidate(t *testing.T) {
|
||||
{
|
||||
name: "report with both misconfigs and vulnerabilities",
|
||||
report: Report{
|
||||
Vulnerabilities: []Resource{
|
||||
Resources: []Resource{
|
||||
deployOrionWithVulns,
|
||||
cronjobHelloWithVulns,
|
||||
},
|
||||
Misconfigurations: []Resource{
|
||||
deployOrionWithMisconfigs,
|
||||
podPrometheusWithMisconfigs,
|
||||
},
|
||||
@@ -301,7 +299,7 @@ func TestReport_consolidate(t *testing.T) {
|
||||
{
|
||||
name: "report with only misconfigurations",
|
||||
report: Report{
|
||||
Misconfigurations: []Resource{
|
||||
Resources: []Resource{
|
||||
deployOrionWithMisconfigs,
|
||||
podPrometheusWithMisconfigs,
|
||||
},
|
||||
@@ -314,7 +312,7 @@ func TestReport_consolidate(t *testing.T) {
|
||||
{
|
||||
name: "report with only vulnerabilities",
|
||||
report: Report{
|
||||
Vulnerabilities: []Resource{
|
||||
Resources: []Resource{
|
||||
deployOrionWithVulns,
|
||||
cronjobHelloWithVulns,
|
||||
},
|
||||
@@ -382,11 +380,9 @@ func TestResourceFailed(t *testing.T) {
|
||||
{
|
||||
name: "report with both misconfigs and vulnerabilities",
|
||||
report: Report{
|
||||
Vulnerabilities: []Resource{
|
||||
Resources: []Resource{
|
||||
deployOrionWithVulns,
|
||||
cronjobHelloWithVulns,
|
||||
},
|
||||
Misconfigurations: []Resource{
|
||||
deployOrionWithMisconfigs,
|
||||
podPrometheusWithMisconfigs,
|
||||
},
|
||||
@@ -396,7 +392,7 @@ func TestResourceFailed(t *testing.T) {
|
||||
{
|
||||
name: "report with only misconfigurations",
|
||||
report: Report{
|
||||
Misconfigurations: []Resource{
|
||||
Resources: []Resource{
|
||||
deployOrionWithMisconfigs,
|
||||
podPrometheusWithMisconfigs,
|
||||
},
|
||||
@@ -406,7 +402,7 @@ func TestResourceFailed(t *testing.T) {
|
||||
{
|
||||
name: "report with only vulnerabilities",
|
||||
report: Report{
|
||||
Vulnerabilities: []Resource{
|
||||
Resources: []Resource{
|
||||
deployOrionWithVulns,
|
||||
cronjobHelloWithVulns,
|
||||
},
|
||||
@@ -464,7 +460,7 @@ func Test_rbacResource(t *testing.T) {
|
||||
|
||||
func Test_separateMisconfigReports(t *testing.T) {
|
||||
k8sReport := Report{
|
||||
Misconfigurations: []Resource{
|
||||
Resources: []Resource{
|
||||
{Kind: "Role"},
|
||||
{Kind: "Deployment"},
|
||||
{Kind: "StatefulSet"},
|
||||
@@ -500,14 +496,14 @@ func Test_separateMisconfigReports(t *testing.T) {
|
||||
expectedReports: []Report{
|
||||
// the order matter for the test
|
||||
{
|
||||
Misconfigurations: []Resource{
|
||||
Resources: []Resource{
|
||||
{Kind: "Deployment"},
|
||||
{Kind: "StatefulSet"},
|
||||
{Kind: "Pod"},
|
||||
},
|
||||
},
|
||||
{Misconfigurations: []Resource{{Kind: "Role"}}},
|
||||
{Misconfigurations: []Resource{{Kind: "Pod"}}},
|
||||
{Resources: []Resource{{Kind: "Role"}}},
|
||||
{Resources: []Resource{{Kind: "Pod"}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -521,13 +517,13 @@ func Test_separateMisconfigReports(t *testing.T) {
|
||||
expectedReports: []Report{
|
||||
// the order matter for the test
|
||||
{
|
||||
Misconfigurations: []Resource{
|
||||
Resources: []Resource{
|
||||
{Kind: "Deployment"},
|
||||
{Kind: "StatefulSet"},
|
||||
{Kind: "Pod"},
|
||||
},
|
||||
},
|
||||
{Misconfigurations: []Resource{{Kind: "Pod"}}},
|
||||
{Resources: []Resource{{Kind: "Pod"}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -535,7 +531,7 @@ func Test_separateMisconfigReports(t *testing.T) {
|
||||
k8sReport: k8sReport,
|
||||
scanners: types.Scanners{types.RBACScanner},
|
||||
expectedReports: []Report{
|
||||
{Misconfigurations: []Resource{{Kind: "Role"}}},
|
||||
{Resources: []Resource{{Kind: "Role"}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -545,7 +541,7 @@ func Test_separateMisconfigReports(t *testing.T) {
|
||||
components: []string{workloadComponent},
|
||||
expectedReports: []Report{
|
||||
{
|
||||
Misconfigurations: []Resource{
|
||||
Resources: []Resource{
|
||||
{Kind: "Deployment"},
|
||||
{Kind: "StatefulSet"},
|
||||
{Kind: "Pod"},
|
||||
@@ -559,7 +555,7 @@ func Test_separateMisconfigReports(t *testing.T) {
|
||||
scanners: types.Scanners{types.MisconfigScanner},
|
||||
components: []string{infraComponent},
|
||||
expectedReports: []Report{
|
||||
{Misconfigurations: []Resource{{Kind: "Pod"}}},
|
||||
{Resources: []Resource{{Kind: "Pod"}}},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -572,9 +568,9 @@ func Test_separateMisconfigReports(t *testing.T) {
|
||||
assert.Equal(t, len(tt.expectedReports), len(reports))
|
||||
|
||||
for i := range reports {
|
||||
assert.Equal(t, len(tt.expectedReports[i].Misconfigurations), len(reports[i].report.Misconfigurations))
|
||||
for j, m := range tt.expectedReports[i].Misconfigurations {
|
||||
assert.Equal(t, m.Kind, reports[i].report.Misconfigurations[j].Kind)
|
||||
assert.Equal(t, len(tt.expectedReports[i].Resources), len(reports[i].report.Resources))
|
||||
for j, m := range tt.expectedReports[i].Resources {
|
||||
assert.Equal(t, m.Kind, reports[i].report.Resources[j].Kind)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -602,8 +598,8 @@ func TestReportWrite_Summary(t *testing.T) {
|
||||
{
|
||||
name: "Only config, all serverities",
|
||||
report: Report{
|
||||
ClusterName: "test",
|
||||
Misconfigurations: []Resource{deployOrionWithMisconfigs},
|
||||
ClusterName: "test",
|
||||
Resources: []Resource{deployOrionWithMisconfigs},
|
||||
},
|
||||
scanners: types.Scanners{types.MisconfigScanner},
|
||||
components: []string{workloadComponent},
|
||||
@@ -624,8 +620,8 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
|
||||
{
|
||||
name: "Only vuln, all serverities",
|
||||
report: Report{
|
||||
ClusterName: "test",
|
||||
Vulnerabilities: []Resource{deployOrionWithVulns},
|
||||
ClusterName: "test",
|
||||
Resources: []Resource{deployOrionWithVulns},
|
||||
},
|
||||
scanners: types.Scanners{types.VulnerabilityScanner},
|
||||
severities: allSeverities,
|
||||
@@ -645,8 +641,8 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
|
||||
{
|
||||
name: "Only rbac, all serverities",
|
||||
report: Report{
|
||||
ClusterName: "test",
|
||||
Misconfigurations: []Resource{roleWithMisconfig},
|
||||
ClusterName: "test",
|
||||
Resources: []Resource{roleWithMisconfig},
|
||||
},
|
||||
scanners: types.Scanners{types.RBACScanner},
|
||||
severities: allSeverities,
|
||||
@@ -666,8 +662,8 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
|
||||
{
|
||||
name: "Only secret, all serverities",
|
||||
report: Report{
|
||||
ClusterName: "test",
|
||||
Vulnerabilities: []Resource{deployLuaWithSecrets},
|
||||
ClusterName: "test",
|
||||
Resources: []Resource{deployLuaWithSecrets},
|
||||
},
|
||||
scanners: types.Scanners{types.SecretScanner},
|
||||
severities: allSeverities,
|
||||
@@ -687,8 +683,8 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
|
||||
{
|
||||
name: "apiserver, only infra and serverities",
|
||||
report: Report{
|
||||
ClusterName: "test",
|
||||
Misconfigurations: []Resource{apiseverPodWithMisconfigAndInfra},
|
||||
ClusterName: "test",
|
||||
Resources: []Resource{apiseverPodWithMisconfigAndInfra},
|
||||
},
|
||||
scanners: types.Scanners{types.MisconfigScanner},
|
||||
components: []string{infraComponent},
|
||||
@@ -709,8 +705,8 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
|
||||
{
|
||||
name: "apiserver, vuln,config,secret and serverities",
|
||||
report: Report{
|
||||
ClusterName: "test",
|
||||
Misconfigurations: []Resource{apiseverPodWithMisconfigAndInfra},
|
||||
ClusterName: "test",
|
||||
Resources: []Resource{apiseverPodWithMisconfigAndInfra},
|
||||
},
|
||||
scanners: types.Scanners{
|
||||
types.VulnerabilityScanner,
|
||||
@@ -735,8 +731,8 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
|
||||
{
|
||||
name: "apiserver, all scanners and serverities",
|
||||
report: Report{
|
||||
ClusterName: "test",
|
||||
Misconfigurations: []Resource{apiseverPodWithMisconfigAndInfra},
|
||||
ClusterName: "test",
|
||||
Resources: []Resource{apiseverPodWithMisconfigAndInfra},
|
||||
},
|
||||
scanners: types.Scanners{
|
||||
types.MisconfigScanner,
|
||||
|
||||
@@ -43,15 +43,7 @@ func (tw TableWriter) Write(report Report) error {
|
||||
switch tw.Report {
|
||||
case allReport:
|
||||
t := pkgReport.Writer{Output: tw.Output, Severities: tw.Severities, ShowMessageOnce: &sync.Once{}}
|
||||
for _, r := range report.Vulnerabilities {
|
||||
if r.Report.Results.Failed() {
|
||||
err := t.Write(r.Report)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, r := range report.Misconfigurations {
|
||||
for _, r := range report.Resources {
|
||||
if r.Report.Results.Failed() {
|
||||
err := t.Write(r.Report)
|
||||
if err != nil {
|
||||
|
||||
@@ -47,7 +47,7 @@ func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact)
|
||||
log.Fatal(xerrors.Errorf("can't enable logger error: %w", err))
|
||||
}
|
||||
}()
|
||||
var vulns, misconfigs []report.Resource
|
||||
var resources []report.Resource
|
||||
|
||||
type scanResult struct {
|
||||
vulns []report.Resource
|
||||
@@ -74,8 +74,8 @@ func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact)
|
||||
}
|
||||
|
||||
onResult := func(result scanResult) error {
|
||||
vulns = append(vulns, result.vulns...)
|
||||
misconfigs = append(misconfigs, result.misconfig)
|
||||
resources = append(resources, result.vulns...)
|
||||
resources = append(resources, result.misconfig)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -84,12 +84,10 @@ func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact)
|
||||
if err != nil {
|
||||
return report.Report{}, err
|
||||
}
|
||||
|
||||
return report.Report{
|
||||
SchemaVersion: 0,
|
||||
ClusterName: s.cluster,
|
||||
Vulnerabilities: vulns,
|
||||
Misconfigurations: misconfigs,
|
||||
SchemaVersion: 0,
|
||||
ClusterName: s.cluster,
|
||||
Resources: resources,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user