diff --git a/pkg/k8s/report/report.go b/pkg/k8s/report/report.go index df68e7d5ad0c..dc0b44c7ab73 100644 --- a/pkg/k8s/report/report.go +++ b/pkg/k8s/report/report.go @@ -22,6 +22,7 @@ const ( workloadComponent = "workload" infraComponent = "infra" + infraNamespace = "kube-system" ) type Option struct { @@ -134,79 +135,65 @@ type reports struct { // - infra checks report func SeparateMisconfigReports(k8sReport Report, scanners types.Scanners, components []string) []reports { - var workloadMisconfig, infraMisconfig, rbacAssessment, workloadVulnerabilities, workloadResource []Resource + var workloadMisconfig, infraMisconfig, rbacAssessment, workloadVulnerabilities, infraVulnerabilities, workloadResource []Resource for _, resource := range k8sReport.Resources { - if vulnerabilitiesOrSecretResource(resource) { - workloadVulnerabilities = append(workloadVulnerabilities, resource) - continue - } - switch { + case vulnerabilitiesOrSecretResource(resource): + if resource.Namespace == infraNamespace || nodeInfoResource(resource) { + infraVulnerabilities = append(infraVulnerabilities, nodeKind(resource)) + } else { + workloadVulnerabilities = append(workloadVulnerabilities, resource) + } 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) - } - - if slices.Contains(components, workloadComponent) { - workloadMisconfig = append(workloadMisconfig, workload) - } - - case scanners.Enabled(types.MisconfigScanner) && !rbacResource(resource): - if slices.Contains(components, workloadComponent) { - workloadMisconfig = append(workloadMisconfig, resource) - } + infraMisconfig = append(infraMisconfig, nodeKind(resource)) + case scanners.Enabled(types.MisconfigScanner) && + !rbacResource(resource) && + slices.Contains(components, workloadComponent): + workloadMisconfig = append(workloadMisconfig, resource) } } var r []reports workloadResource = append(workloadResource, workloadVulnerabilities...) workloadResource = append(workloadResource, workloadMisconfig...) - if shouldAddWorkloadReport(scanners) { + if shouldAddToReport(scanners, components, workloadComponent) { workloadReport := Report{ SchemaVersion: 0, ClusterName: k8sReport.ClusterName, Resources: workloadResource, name: "Workload Assessment", } - - if (slices.Contains(components, workloadComponent) && - len(workloadMisconfig) > 0) || - len(workloadVulnerabilities) > 0 { + if slices.Contains(components, workloadComponent) { r = append(r, reports{ Report: workloadReport, Columns: WorkloadColumns(), }) } } - - if scanners.Enabled(types.RBACScanner) && len(rbacAssessment) > 0 { + infraMisconfig = append(infraMisconfig, infraVulnerabilities...) + if shouldAddToReport(scanners, components, infraComponent) { r = append(r, reports{ Report: Report{ SchemaVersion: 0, ClusterName: k8sReport.ClusterName, - Resources: rbacAssessment, - name: "RBAC Assessment", + Resources: infraMisconfig, + name: "Infra Assessment", }, - Columns: RoleColumns(), + Columns: InfraColumns(), }) } - if scanners.Enabled(types.MisconfigScanner) && - slices.Contains(components, infraComponent) && - len(infraMisconfig) > 0 { - + if scanners.Enabled(types.RBACScanner) { r = append(r, reports{ Report: Report{ SchemaVersion: 0, ClusterName: k8sReport.ClusterName, - Resources: infraMisconfig, - name: "Infra Assessment", + Resources: rbacAssessment, + name: "RBAC Assessment", }, - Columns: InfraColumns(), + Columns: RoleColumns(), }) } @@ -214,11 +201,11 @@ func SeparateMisconfigReports(k8sReport Report, scanners types.Scanners, compone } func rbacResource(misConfig Resource) bool { - return misConfig.Kind == "Role" || misConfig.Kind == "RoleBinding" || misConfig.Kind == "ClusterRole" || misConfig.Kind == "ClusterRoleBinding" + return slices.Contains([]string{"Role", "RoleBinding", "ClusterRole", "ClusterRoleBinding"}, misConfig.Kind) } func infraResource(misConfig Resource) bool { - return (misConfig.Kind == "Pod" && misConfig.Namespace == "kube-system") || misConfig.Kind == "NodeInfo" + return !rbacResource(misConfig) && (misConfig.Namespace == infraNamespace) || nodeInfoResource(misConfig) } func CreateResource(artifact *artifacts.Artifact, report types.Report, err error) Resource { @@ -234,6 +221,10 @@ func CreateResource(artifact *artifacts.Artifact, report types.Report, err error return r } +func nodeInfoResource(nodeInfo Resource) bool { + return nodeInfo.Kind == "NodeInfo" || nodeInfo.Kind == "NodeComponents" +} + func createK8sResource(artifact *artifacts.Artifact, scanResults types.Results) Resource { results := make([]types.Result, 0, len(scanResults)) // fix target name @@ -269,68 +260,21 @@ func (r Report) PrintErrors() { } } -func splitInfraAndWorkloadResources(misconfig Resource) (Resource, Resource) { - workload := copyResource(misconfig) - infra := copyResource(misconfig) - - workloadResults := make(types.Results, 0) - infraResults := make(types.Results, 0) - - for _, result := range misconfig.Results { - var workloadMisconfigs, infraMisconfigs []types.DetectedMisconfiguration - - for _, m := range result.Misconfigurations { - if strings.HasPrefix(m.ID, "KCV") { - infraMisconfigs = append(infraMisconfigs, m) - continue - } - - workloadMisconfigs = append(workloadMisconfigs, m) - } - - if len(workloadMisconfigs) > 0 { - workloadResults = append(workloadResults, copyResult(result, workloadMisconfigs)) - } - - if len(infraMisconfigs) > 0 { - infraResults = append(infraResults, copyResult(result, infraMisconfigs)) - } - } - - workload.Results = workloadResults - workload.Report.Results = workloadResults - - infra.Results = infraResults - infra.Report.Results = infraResults - - return workload, infra +func shouldAddToReport(scanners types.Scanners, components []string, componentType string) bool { + return scanners.AnyEnabled( + types.MisconfigScanner, + types.VulnerabilityScanner, + types.SecretScanner) && + slices.Contains(components, componentType) } -func copyResource(r Resource) Resource { - return Resource{ - Namespace: r.Namespace, - Kind: r.Kind, - Name: r.Name, - Metadata: r.Metadata, - Error: r.Error, - Report: r.Report, - } +func vulnerabilitiesOrSecretResource(resource Resource) bool { + return len(resource.Results) > 0 && (len(resource.Results[0].Vulnerabilities) > 0 || len(resource.Results[0].Secrets) > 0) } -func copyResult(r types.Result, misconfigs []types.DetectedMisconfiguration) types.Result { - return types.Result{ - Target: r.Target, - Class: r.Class, - Type: r.Type, - MisconfSummary: r.MisconfSummary, - Misconfigurations: misconfigs, +func nodeKind(resource Resource) Resource { + if nodeInfoResource(resource) { + resource.Kind = "Node" } -} - -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) + return resource } diff --git a/pkg/k8s/report/report_test.go b/pkg/k8s/report/report_test.go index 311fc650832d..6d14b52e12a9 100644 --- a/pkg/k8s/report/report_test.go +++ b/pkg/k8s/report/report_test.go @@ -535,11 +535,10 @@ func Test_separateMisconfigReports(t *testing.T) { Resources: []Resource{ {Kind: "Deployment"}, {Kind: "StatefulSet"}, - {Kind: "Pod"}, }, }, - {Resources: []Resource{{Kind: "Role"}}}, {Resources: []Resource{{Kind: "Pod"}}}, + {Resources: []Resource{{Kind: "Role"}}}, }, }, { @@ -556,7 +555,6 @@ func Test_separateMisconfigReports(t *testing.T) { Resources: []Resource{ {Kind: "Deployment"}, {Kind: "StatefulSet"}, - {Kind: "Pod"}, }, }, {Resources: []Resource{{Kind: "Pod"}}}, @@ -580,7 +578,6 @@ func Test_separateMisconfigReports(t *testing.T) { Resources: []Resource{ {Kind: "Deployment"}, {Kind: "StatefulSet"}, - {Kind: "Pod"}, }, }, }, diff --git a/pkg/k8s/report/summary.go b/pkg/k8s/report/summary.go index 420fba38b954..f35a1b3f6624 100644 --- a/pkg/k8s/report/summary.go +++ b/pkg/k8s/report/summary.go @@ -51,7 +51,7 @@ func ColumnHeading(scanners types.Scanners, components, availableColumns []strin securityOptions[MisconfigurationsColumn] = nil } if slices.Contains(components, infraComponent) { - securityOptions[InfraAssessmentColumn] = nil + securityOptions[MisconfigurationsColumn] = nil } case types.SecretScanner: securityOptions[SecretsColumn] = nil @@ -107,8 +107,7 @@ func (s SummaryWriter) Write(report Report) error { } if slices.Contains(s.ColumnsHeading, MisconfigurationsColumn) || - slices.Contains(s.ColumnsHeading, RbacAssessmentColumn) || - slices.Contains(s.ColumnsHeading, InfraAssessmentColumn) { + slices.Contains(s.ColumnsHeading, RbacAssessmentColumn) { rowParts = append(rowParts, s.generateSummary(mCount)...) } diff --git a/pkg/k8s/report/summary_test.go b/pkg/k8s/report/summary_test.go index a622232e012f..8744db1c6233 100644 --- a/pkg/k8s/report/summary_test.go +++ b/pkg/k8s/report/summary_test.go @@ -62,7 +62,9 @@ func TestReport_ColumnHeading(t *testing.T) { want: []string{ NamespaceColumn, ResourceColumn, - InfraAssessmentColumn, + VulnerabilitiesColumn, + MisconfigurationsColumn, + SecretsColumn, }, }, { diff --git a/pkg/k8s/report/table.go b/pkg/k8s/report/table.go index 1edb44879156..13dbe026912a 100644 --- a/pkg/k8s/report/table.go +++ b/pkg/k8s/report/table.go @@ -26,7 +26,6 @@ const ( MisconfigurationsColumn = "Misconfigurations" SecretsColumn = "Secrets" RbacAssessmentColumn = "RBAC Assessment" - InfraAssessmentColumn = "Kubernetes Infra Assessment" ) func WorkloadColumns() []string { @@ -42,7 +41,11 @@ func RoleColumns() []string { } func InfraColumns() []string { - return []string{InfraAssessmentColumn} + return []string{ + VulnerabilitiesColumn, + MisconfigurationsColumn, + SecretsColumn, + } } func (tw TableWriter) Write(ctx context.Context, report Report) error { diff --git a/pkg/k8s/writer_test.go b/pkg/k8s/writer_test.go index 11abcdb35a21..e07c35044ae8 100644 --- a/pkg/k8s/writer_test.go +++ b/pkg/k8s/writer_test.go @@ -3,11 +3,12 @@ package k8s import ( "bytes" "context" - "github.com/stretchr/testify/require" "regexp" "strings" "testing" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" @@ -234,6 +235,7 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, Resources: []report.Resource{deployOrionWithVulns}, }, scanners: types.Scanners{types.VulnerabilityScanner}, + components: []string{workloadComponent}, severities: allSeverities, expectedOutput: `Summary Report for test ======================= @@ -276,6 +278,7 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, Resources: []report.Resource{deployLuaWithSecrets}, }, scanners: types.Scanners{types.SecretScanner}, + components: []string{workloadComponent}, severities: allSeverities, expectedOutput: `Summary Report for test ======================= @@ -303,13 +306,13 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, ======================= Infra Assessment -┌─────────────┬────────────────────┬─────────────────────────────┐ -│ Namespace │ Resource │ Kubernetes Infra Assessment │ -│ │ ├─────┬─────┬─────┬─────┬─────┤ -│ │ │ C │ H │ M │ L │ U │ -├─────────────┼────────────────────┼─────┼─────┼─────┼─────┼─────┤ -│ kube-system │ Pod/kube-apiserver │ │ │ 1 │ 1 │ │ -└─────────────┴────────────────────┴─────┴─────┴─────┴─────┴─────┘ +┌─────────────┬────────────────────┬───────────────────┐ +│ Namespace │ Resource │ Misconfigurations │ +│ │ ├───┬───┬───┬───┬───┤ +│ │ │ C │ H │ M │ L │ U │ +├─────────────┼────────────────────┼───┼───┼───┼───┼───┤ +│ kube-system │ Pod/kube-apiserver │ │ 1 │ 2 │ 2 │ │ +└─────────────┴────────────────────┴───┴───┴───┴───┴───┘ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, }, { @@ -323,23 +326,23 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, types.MisconfigScanner, types.SecretScanner, }, - components: []string{workloadComponent}, + components: []string{infraComponent}, severities: allSeverities, expectedOutput: `Summary Report for test ======================= -Workload Assessment +Infra Assessment ┌─────────────┬────────────────────┬───────────────────┬───────────────────┬───────────────────┐ │ Namespace │ Resource │ Vulnerabilities │ Misconfigurations │ Secrets │ │ │ ├───┬───┬───┬───┬───┼───┬───┬───┬───┬───┼───┬───┬───┬───┬───┤ │ │ │ C │ H │ M │ L │ U │ C │ H │ M │ L │ U │ C │ H │ M │ L │ U │ ├─────────────┼────────────────────┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ -│ kube-system │ Pod/kube-apiserver │ │ │ │ │ │ │ 1 │ 1 │ 1 │ │ │ │ │ │ │ +│ kube-system │ Pod/kube-apiserver │ │ │ │ │ │ │ 1 │ 2 │ 2 │ │ │ │ │ │ │ └─────────────┴────────────────────┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, }, { - name: "apiserver, all scanners and serverities", + name: "apiserver, all misconfig and vuln scanners and serverities", report: report.Report{ ClusterName: "test", Resources: []report.Resource{apiseverPodWithMisconfigAndInfra}, @@ -347,8 +350,6 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, scanners: types.Scanners{ types.MisconfigScanner, types.VulnerabilityScanner, - types.RBACScanner, - types.SecretScanner, }, components: []string{ workloadComponent, @@ -359,24 +360,22 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, ======================= Workload Assessment -┌─────────────┬────────────────────┬───────────────────┬───────────────────┬───────────────────┐ -│ Namespace │ Resource │ Vulnerabilities │ Misconfigurations │ Secrets │ -│ │ ├───┬───┬───┬───┬───┼───┬───┬───┬───┬───┼───┬───┬───┬───┬───┤ -│ │ │ C │ H │ M │ L │ U │ C │ H │ M │ L │ U │ C │ H │ M │ L │ U │ -├─────────────┼────────────────────┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ -│ kube-system │ Pod/kube-apiserver │ │ │ │ │ │ │ 1 │ 1 │ 1 │ │ │ │ │ │ │ -└─────────────┴────────────────────┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ +┌───────────┬──────────┬───────────────────┬───────────────────┐ +│ Namespace │ Resource │ Vulnerabilities │ Misconfigurations │ +│ │ ├───┬───┬───┬───┬───┼───┬───┬───┬───┬───┤ +│ │ │ 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 │ Kubernetes Infra Assessment │ -│ │ ├─────┬─────┬─────┬─────┬─────┤ -│ │ │ C │ H │ M │ L │ U │ -├─────────────┼────────────────────┼─────┼─────┼─────┼─────┼─────┤ -│ kube-system │ Pod/kube-apiserver │ │ │ 1 │ 1 │ │ -└─────────────┴────────────────────┴─────┴─────┴─────┴─────┴─────┘ +┌─────────────┬────────────────────┬───────────────────┬───────────────────┐ +│ Namespace │ Resource │ Vulnerabilities │ Misconfigurations │ +│ │ ├───┬───┬───┬───┬───┼───┬───┬───┬───┬───┤ +│ │ │ C │ H │ M │ L │ U │ C │ H │ M │ L │ U │ +├─────────────┼────────────────────┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ +│ kube-system │ Pod/kube-apiserver │ │ │ │ │ │ │ 1 │ 2 │ 2 │ │ +└─────────────┴────────────────────┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, }, }