From 009d8275c10f6bb1a06e0ec24e120ab899bf78a1 Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 19 Oct 2021 14:22:09 +0300 Subject: [PATCH 1/3] Distinct exclude and failed resources --- README.md | 2 +- resultshandling/printer/printresults.go | 41 ++++++++++++++++--------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 2b82689a..5afce369 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --form kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --format junit --output results.xml ``` -* Scan with exceptions, objects with exceptions will be presented as `warning` and not `fail` +* Scan with exceptions, objects with exceptions will be presented as `exclude` and not `fail` ``` kubescape scan framework nsa --exceptions examples/exceptions.json ``` diff --git a/resultshandling/printer/printresults.go b/resultshandling/printer/printresults.go index 8d8fc080..97602e05 100644 --- a/resultshandling/printer/printresults.go +++ b/resultshandling/printer/printresults.go @@ -101,16 +101,16 @@ func (printer *Printer) SummarySetup(postureReport *reporthandling.PostureReport continue } workloadsSummary := listResultSummary(cr.RuleReports) - mapResources := groupByNamespace(workloadsSummary) printer.summary[cr.Name] = ControlSummary{ - TotalResources: cr.GetNumberOfResources(), - TotalFailed: cr.GetNumberOfFailedResources(), - TotalWarnign: cr.GetNumberOfWarningResources(), - WorkloadSummary: mapResources, - Description: cr.Description, - Remediation: cr.Remediation, - ListInputKinds: cr.ListControlsInputKinds(), + TotalResources: cr.GetNumberOfResources(), + TotalFailed: cr.GetNumberOfFailedResources(), + TotalWarnign: cr.GetNumberOfWarningResources(), + FailedWorkloads: groupByNamespace(workloadsSummary, workloadSummaryFailed), + ExcludedWorkloads: groupByNamespace(workloadsSummary, workloadSummaryExclude), + Description: cr.Description, + Remediation: cr.Remediation, + ListInputKinds: cr.ListControlsInputKinds(), } } } @@ -120,8 +120,7 @@ func (printer *Printer) PrintResults() { for i := 0; i < len(printer.sortedControlNames); i++ { controlSummary := printer.summary[printer.sortedControlNames[i]] printer.printTitle(printer.sortedControlNames[i], &controlSummary) - printer.printResult(printer.sortedControlNames[i], &controlSummary) - + printer.printResources(&controlSummary) if printer.summary[printer.sortedControlNames[i]].TotalResources > 0 { printer.printSummary(printer.sortedControlNames[i], &controlSummary) } @@ -144,7 +143,7 @@ func (printer *Printer) printSummary(controlName string, controlSummary *Control func (printer *Printer) printTitle(controlName string, controlSummary *ControlSummary) { cautils.InfoDisplay(printer.writer, "[control: %s] ", controlName) - if controlSummary.TotalResources == 0 && len(controlSummary.ListInputKinds) > 0 { + if controlSummary.TotalResources == 0 { cautils.InfoDisplay(printer.writer, "resources not found %v\n", emoji.ConfusedFace) } else if controlSummary.TotalFailed != 0 { cautils.FailureDisplay(printer.writer, "failed %v\n", emoji.SadButRelievedFace) @@ -157,10 +156,24 @@ func (printer *Printer) printTitle(controlName string, controlSummary *ControlSu cautils.DescriptionDisplay(printer.writer, "Description: %s\n", controlSummary.Description) } -func (printer *Printer) printResult(controlName string, controlSummary *ControlSummary) { +func (printer *Printer) printResources(controlSummary *ControlSummary) { + + if len(controlSummary.FailedWorkloads) > 0 { + cautils.FailureDisplay(printer.writer, "Failed:\n") + printer.printGroupedResources(controlSummary.FailedWorkloads) + } + if len(controlSummary.ExcludedWorkloads) > 0 { + cautils.WarningDisplay(printer.writer, "Excluded:\n") + printer.printGroupedResources(controlSummary.ExcludedWorkloads) + } + +} + +func (printer *Printer) printGroupedResources(workloads map[string][]WorkloadSummary) { indent := INDENT - for ns, rsc := range controlSummary.WorkloadSummary { + + for ns, rsc := range workloads { preIndent := indent if ns != "" { cautils.SimpleDisplay(printer.writer, "%sNamespace %s\n", indent, ns) @@ -207,7 +220,7 @@ func percentage(big, small int) int { func generateFooter(numControlers, sumFailed, sumWarning, sumTotal int) []string { // Control name | # failed resources | all resources | % success row := []string{} - row = append(row, fmt.Sprintf("%d", numControlers)) + row = append(row, "Resource Summary") //fmt.Sprintf(""%d", numControlers")) row = append(row, fmt.Sprintf("%d", sumFailed)) row = append(row, fmt.Sprintf("%d", sumWarning)) row = append(row, fmt.Sprintf("%d", sumTotal)) From b371fbad0128a8bfaac8e562e7c398a8003fbe73 Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 19 Oct 2021 15:08:00 +0300 Subject: [PATCH 2/3] adding summary --- docs/run-options.md | 2 +- resultshandling/printer/summary.go | 23 ++++++++++++++++------- resultshandling/printer/summeryhelpers.go | 14 ++++++++------ 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/docs/run-options.md b/docs/run-options.md index 4a7a0a80..8a94afc0 100644 --- a/docs/run-options.md +++ b/docs/run-options.md @@ -51,7 +51,7 @@ kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --form kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --format junit --output results.xml ``` -* Scan with exceptions, objects with exceptions will be presented as `warning` and not `fail` +* Scan with exceptions, objects with exceptions will be presented as `warning` and not `fail` ``` kubescape scan framework nsa --exceptions examples/exceptions.json ``` diff --git a/resultshandling/printer/summary.go b/resultshandling/printer/summary.go index 1bc90911..9df245f6 100644 --- a/resultshandling/printer/summary.go +++ b/resultshandling/printer/summary.go @@ -13,13 +13,14 @@ func NewSummary() Summary { } type ControlSummary struct { - TotalResources int - TotalFailed int - TotalWarnign int - Description string - Remediation string - ListInputKinds []string - WorkloadSummary map[string][]WorkloadSummary // :[] + TotalResources int + TotalFailed int + TotalWarnign int + Description string + Remediation string + ListInputKinds []string + FailedWorkloads map[string][]WorkloadSummary // :[] + ExcludedWorkloads map[string][]WorkloadSummary // :[] } type WorkloadSummary struct { @@ -41,3 +42,11 @@ func (controlSummary *ControlSummary) ToSlice() []string { func (workloadSummary *WorkloadSummary) ToString() string { return fmt.Sprintf("/%s/%s/%s/%s", workloadSummary.Group, workloadSummary.Namespace, workloadSummary.Kind, workloadSummary.Name) } + +func workloadSummaryFailed(workloadSummary *WorkloadSummary) bool { + return workloadSummary.Exception == nil +} + +func workloadSummaryExclude(workloadSummary *WorkloadSummary) bool { + return workloadSummary.Exception != nil && workloadSummary.Exception.IsAlertOnly() +} diff --git a/resultshandling/printer/summeryhelpers.go b/resultshandling/printer/summeryhelpers.go index 3cd11edb..cf7a4e43 100644 --- a/resultshandling/printer/summeryhelpers.go +++ b/resultshandling/printer/summeryhelpers.go @@ -8,14 +8,16 @@ import ( ) // Group workloads by namespace - return {"namespace": <[]WorkloadSummary>} -func groupByNamespace(resources []WorkloadSummary) map[string][]WorkloadSummary { +func groupByNamespace(resources []WorkloadSummary, status func(workloadSummary *WorkloadSummary) bool) map[string][]WorkloadSummary { mapResources := make(map[string][]WorkloadSummary) for i := range resources { - if r, ok := mapResources[resources[i].Namespace]; ok { - r = append(r, resources[i]) - mapResources[resources[i].Namespace] = r - } else { - mapResources[resources[i].Namespace] = []WorkloadSummary{resources[i]} + if status(&resources[i]) { + if r, ok := mapResources[resources[i].Namespace]; ok { + r = append(r, resources[i]) + mapResources[resources[i].Namespace] = r + } else { + mapResources[resources[i].Namespace] = []WorkloadSummary{resources[i]} + } } } return mapResources From 87e79110a23c2c67aff4b68b17deda3639a1190a Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 19 Oct 2021 16:25:54 +0300 Subject: [PATCH 3/3] fallback customer guid from configMap --- cautils/customerloader.go | 92 +++++++++++++++++++++++++++------------ cmd/framework.go | 4 +- 2 files changed, 65 insertions(+), 31 deletions(-) diff --git a/cautils/customerloader.go b/cautils/customerloader.go index c97acdb1..8e723b20 100644 --- a/cautils/customerloader.go +++ b/cautils/customerloader.go @@ -30,6 +30,7 @@ type ConfigObj struct { CustomerGUID string `json:"customerGUID"` Token string `json:"invitationParam"` CustomerAdminEMail string `json:"adminMail"` + ClusterName string `json:"clusterName"` } func (co *ConfigObj) Json() []byte { @@ -39,14 +40,30 @@ func (co *ConfigObj) Json() []byte { return []byte{} } +// Config - convert ConfigObj to config file +func (co *ConfigObj) Config() []byte { + clusterName := co.ClusterName + co.ClusterName = "" // remove cluster name before saving to file + b, err := json.Marshal(co) + co.ClusterName = clusterName + + if err == nil { + return b + } + + return []byte{} +} + // ====================================================================================== // =============================== interface ============================================ // ====================================================================================== type IClusterConfig interface { - // setters - SetCustomerGUID(customerGUID string) error + + // set + SetConfig(customerGUID string) error // getters + GetClusterName() string GetCustomerGUID() string GetConfigObj() *ConfigObj GetK8sAPI() *k8sinterface.KubernetesApi @@ -103,13 +120,14 @@ func ClusterConfigSetup(scanInfo *ScanInfo, k8s *k8sinterface.KubernetesApi, beA type EmptyConfig struct { } -func NewEmptyConfig() *EmptyConfig { return &EmptyConfig{} } -func (c *EmptyConfig) GetConfigObj() *ConfigObj { return &ConfigObj{} } -func (c *EmptyConfig) SetCustomerGUID(customerGUID string) error { return nil } -func (c *EmptyConfig) GetCustomerGUID() string { return "" } -func (c *EmptyConfig) GetK8sAPI() *k8sinterface.KubernetesApi { return nil } // TODO: return mock obj -func (c *EmptyConfig) GetDefaultNS() string { return k8sinterface.GetDefaultNamespace() } -func (c *EmptyConfig) GetBackendAPI() getter.IBackend { return nil } // TODO: return mock obj +func NewEmptyConfig() *EmptyConfig { return &EmptyConfig{} } +func (c *EmptyConfig) SetConfig(customerGUID string) error { return nil } +func (c *EmptyConfig) GetConfigObj() *ConfigObj { return &ConfigObj{} } +func (c *EmptyConfig) GetCustomerGUID() string { return "" } +func (c *EmptyConfig) GetK8sAPI() *k8sinterface.KubernetesApi { return nil } // TODO: return mock obj +func (c *EmptyConfig) GetDefaultNS() string { return k8sinterface.GetDefaultNamespace() } +func (c *EmptyConfig) GetBackendAPI() getter.IBackend { return nil } // TODO: return mock obj +func (c *EmptyConfig) GetClusterName() string { return "unknown" } func (c *EmptyConfig) GenerateURL() { message := fmt.Sprintf("You can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more by registering here: https://%s", getter.GetArmoAPIConnector().GetFrontendURL()) InfoTextDisplay(os.Stdout, message+"\n") @@ -169,10 +187,19 @@ func (c *ClusterConfig) GetCustomerGUID() string { return "" } -func (c *ClusterConfig) SetCustomerGUID(customerGUID string) error { +func (c *ClusterConfig) SetConfig(customerGUID string) error { + if c.configObj == nil { + c.configObj = &ConfigObj{} + } + // cluster name + if c.GetClusterName() == "" { + c.setClusterName(k8sinterface.GetClusterName()) + } + + // ARMO customer GUID if customerGUID != "" && c.GetCustomerGUID() != customerGUID { - c.configObj.CustomerGUID = customerGUID // override config customerGUID + c.setCustomerGUID(customerGUID) // override config customerGUID } customerGUID = c.GetCustomerGUID() @@ -181,10 +208,10 @@ func (c *ClusterConfig) SetCustomerGUID(customerGUID string) error { tenantResponse, err := c.backendAPI.GetCustomerGUID(customerGUID) if err == nil && tenantResponse != nil { if tenantResponse.AdminMail != "" { // this customer already belongs to some user - c.configObj.CustomerAdminEMail = tenantResponse.AdminMail + c.setCustomerAdminEMail(tenantResponse.AdminMail) } else { - c.configObj.Token = tenantResponse.Token - c.configObj.CustomerGUID = tenantResponse.TenantID + c.setToken(tenantResponse.Token) + c.setCustomerGUID(tenantResponse.TenantID) } } else { if err != nil && !strings.Contains(err.Error(), "already exists") { @@ -198,15 +225,28 @@ func (c *ClusterConfig) SetCustomerGUID(customerGUID string) error { } else { c.createConfigMap() } - if existsConfigFile() { - c.updateConfigFile() - } else { - c.createConfigFile() - } + c.updateConfigFile() return nil } +func (c *ClusterConfig) setToken(token string) { + c.configObj.Token = token +} + +func (c *ClusterConfig) setCustomerAdminEMail(customerAdminEMail string) { + c.configObj.CustomerAdminEMail = customerAdminEMail +} +func (c *ClusterConfig) setCustomerGUID(customerGUID string) { + c.configObj.CustomerGUID = customerGUID +} + +func (c *ClusterConfig) setClusterName(clusterName string) { + c.configObj.ClusterName = clusterName +} +func (c *ClusterConfig) GetClusterName() string { + return c.configObj.ClusterName +} func (c *ClusterConfig) LoadConfig() { // get from configMap if c.existsConfigMap() { @@ -220,8 +260,9 @@ func (c *ClusterConfig) LoadConfig() { func (c *ClusterConfig) ToMapString() map[string]interface{} { m := map[string]interface{}{} - bc, _ := json.Marshal(c.configObj) - json.Unmarshal(bc, &m) + if bc, err := json.Marshal(c.configObj); err == nil { + json.Unmarshal(bc, &m) + } return m } func (c *ClusterConfig) loadConfigFromConfigMap() (*ConfigObj, error) { @@ -360,14 +401,7 @@ func (c *ClusterConfig) updateConfigMap() error { } func (c *ClusterConfig) updateConfigFile() error { - if err := os.WriteFile(ConfigFileFullPath(), c.configObj.Json(), 0664); err != nil { - return err - } - return nil -} - -func (c *ClusterConfig) createConfigFile() error { - if err := os.WriteFile(ConfigFileFullPath(), c.configObj.Json(), 0664); err != nil { + if err := os.WriteFile(ConfigFileFullPath(), c.configObj.Config(), 0664); err != nil { return err } return nil diff --git a/cmd/framework.go b/cmd/framework.go index c24a4fdf..954ef634 100644 --- a/cmd/framework.go +++ b/cmd/framework.go @@ -120,12 +120,12 @@ func CliSetup() error { // policy handler setup policyHandler := policyhandler.NewPolicyHandler(&processNotification, k8s) - if err := clusterConfig.SetCustomerGUID(scanInfo.Account); err != nil { + if err := clusterConfig.SetConfig(scanInfo.Account); err != nil { fmt.Println(err) } + cautils.ClusterName = clusterConfig.GetClusterName() cautils.CustomerGUID = clusterConfig.GetCustomerGUID() - cautils.ClusterName = k8sinterface.GetClusterName() // cli handler setup go func() {