feat: unified k8s scan resources (#4188)

This commit is contained in:
chenk
2023-05-09 16:52:02 +03:00
committed by GitHub
parent f2188eb56d
commit 090a00e717
5 changed files with 87 additions and 104 deletions

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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
}