From 0a850e47df0459d6a1331b8903ee9b7c57565af7 Mon Sep 17 00:00:00 2001 From: dwertent Date: Sun, 24 Oct 2021 17:51:03 +0300 Subject: [PATCH 1/2] use interfaces --- cautils/scaninfo.go | 20 -- clihandler/cmd/control.go | 2 +- clihandler/cmd/framework.go | 2 +- clihandler/initcli.go | 81 +++++- opaprocessor/processorhandler.go | 12 - policyhandler/handlenotification.go | 28 +- .../filesloader.go | 27 +- .../filesloader_test.go | 2 +- .../k8sresources.go | 40 ++- .../k8sresourcesutils.go | 2 +- .../k8sresourcesutils_test.go | 2 +- .../repositoryscanner.go | 2 +- resourcehandler/resourceshandler.go | 13 + .../urlloader.go | 2 +- resultshandling/printer/jsonprinter.go | 29 ++ resultshandling/printer/junit.go | 27 ++ resultshandling/printer/prettyprinter.go | 215 ++++++++++++++ resultshandling/printer/printresults.go | 266 +----------------- resultshandling/printer/silentprinter.go | 11 + resultshandling/reporter/mockreporter.go | 17 ++ .../reporter/reporteventreceiver.go | 37 ++- .../reporter/reporteventreceiverutils.go | 7 +- resultshandling/results.go | 33 ++- 23 files changed, 509 insertions(+), 368 deletions(-) rename {policyhandler => resourcehandler}/filesloader.go (88%) rename {policyhandler => resourcehandler}/filesloader_test.go (98%) rename {policyhandler => resourcehandler}/k8sresources.go (61%) rename {policyhandler => resourcehandler}/k8sresourcesutils.go (98%) rename {policyhandler => resourcehandler}/k8sresourcesutils_test.go (98%) rename {policyhandler => resourcehandler}/repositoryscanner.go (99%) create mode 100644 resourcehandler/resourceshandler.go rename {policyhandler => resourcehandler}/urlloader.go (98%) create mode 100644 resultshandling/printer/jsonprinter.go create mode 100644 resultshandling/printer/prettyprinter.go create mode 100644 resultshandling/printer/silentprinter.go create mode 100644 resultshandling/reporter/mockreporter.go diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index 8ca73ca6..305b400c 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -3,7 +3,6 @@ package cautils import ( "path/filepath" - "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/kubescape/cautils/getter" "github.com/armosec/opa-utils/reporthandling" ) @@ -84,22 +83,3 @@ func (scanInfo *ScanInfo) setOutputFile() { func (scanInfo *ScanInfo) ScanRunningCluster() bool { return len(scanInfo.InputPatterns) == 0 } - -func (scanInfo *ScanInfo) SetClusterConfig() (IClusterConfig, *k8sinterface.KubernetesApi) { - var clusterConfig IClusterConfig - var k8s *k8sinterface.KubernetesApi - if !scanInfo.ScanRunningCluster() { - k8sinterface.ConnectedToCluster = false - clusterConfig = NewEmptyConfig() - } else { - k8s = k8sinterface.NewKubernetesApi() - // setup cluster config - clusterConfig = ClusterConfigSetup(scanInfo, k8s, getter.GetArmoAPIConnector()) - } - return clusterConfig, k8s -} - -// func (scanInfo *ScanInfo) ConnectedToCluster(k8s k8sinterface.) bool { -// _, err := k8s.KubernetesClient.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{}) -// return err == nil -// } diff --git a/clihandler/cmd/control.go b/clihandler/cmd/control.go index 0618e0c4..e89ba905 100644 --- a/clihandler/cmd/control.go +++ b/clihandler/cmd/control.go @@ -31,7 +31,7 @@ var controlCmd = &cobra.Command{ scanInfo.PolicyIdentifier.Kind = reporthandling.KindControl scanInfo.Init() cautils.SetSilentMode(scanInfo.Silent) - err := clihandler.CliSetup(scanInfo) + err := clihandler.CliSetup(&scanInfo) if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) diff --git a/clihandler/cmd/framework.go b/clihandler/cmd/framework.go index bd0bbfe6..9461dce5 100644 --- a/clihandler/cmd/framework.go +++ b/clihandler/cmd/framework.go @@ -54,7 +54,7 @@ var frameworkCmd = &cobra.Command{ } scanInfo.Init() cautils.SetSilentMode(scanInfo.Silent) - err := clihandler.CliSetup(scanInfo) + err := clihandler.CliSetup(&scanInfo) if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) diff --git a/clihandler/initcli.go b/clihandler/initcli.go index c335702d..9dd35a85 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -6,9 +6,12 @@ import ( "strings" "github.com/armosec/armoapi-go/armotypes" + "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/kubescape/cautils" + "github.com/armosec/kubescape/cautils/getter" "github.com/armosec/kubescape/opaprocessor" "github.com/armosec/kubescape/policyhandler" + "github.com/armosec/kubescape/resourcehandler" "github.com/armosec/kubescape/resultshandling" "github.com/armosec/kubescape/resultshandling/printer" "github.com/armosec/kubescape/resultshandling/reporter" @@ -23,25 +26,77 @@ type CLIHandler struct { var SupportedFrameworks = []string{"nsa", "mitre"} var ValidFrameworks = strings.Join(SupportedFrameworks, ", ") -func CliSetup(scanInfo cautils.ScanInfo) error { +type componentInterfaces struct { + clusterConfig cautils.IClusterConfig + resourceHandler resourcehandler.IResourceHandler + report reporter.IReport + printerHandler printer.IPrinter +} - clusterConfig, k8s := scanInfo.SetClusterConfig() +func getReporter(scanInfo *cautils.ScanInfo) reporter.IReport { + if scanInfo.Local { + return reporter.NewReportMock() + } + if !scanInfo.FrameworkScan { + return reporter.NewReportMock() + } + + return reporter.NewReportEventReceiver() +} +func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { + var resourceHandler resourcehandler.IResourceHandler + var clusterConfig cautils.IClusterConfig + var reportHandler reporter.IReport + + if !scanInfo.ScanRunningCluster() { + k8sinterface.ConnectedToCluster = false + clusterConfig = cautils.NewEmptyConfig() + + // load fom file + resourceHandler = resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns) + + // set mock report (do not send report) + reportHandler = reporter.NewReportMock() + } else { + k8s := k8sinterface.NewKubernetesApi() + resourceHandler = resourcehandler.NewK8sResourceHandler(k8s, scanInfo.ExcludedNamespaces) + clusterConfig = cautils.ClusterConfigSetup(scanInfo, k8s, getter.GetArmoAPIConnector()) + + // setup reporter + reportHandler = getReporter(scanInfo) + } + + // setup printer + printerHandler := printer.GetPrinter(scanInfo.Format) + printerHandler.SetWriter(scanInfo.Output) + + return componentInterfaces{ + clusterConfig: clusterConfig, + resourceHandler: resourceHandler, + report: reportHandler, + printerHandler: printerHandler, + } +} + +func CliSetup(scanInfo *cautils.ScanInfo) error { + + interfaces := getInterfaces(scanInfo) processNotification := make(chan *cautils.OPASessionObj) reportResults := make(chan *cautils.OPASessionObj) - // policy handler setup - policyHandler := policyhandler.NewPolicyHandler(&processNotification, k8s) - - if err := clusterConfig.SetConfig(scanInfo.Account); err != nil { + if err := interfaces.clusterConfig.SetConfig(scanInfo.Account); err != nil { fmt.Println(err) } - cautils.ClusterName = clusterConfig.GetClusterName() - cautils.CustomerGUID = clusterConfig.GetCustomerGUID() - + cautils.ClusterName = interfaces.clusterConfig.GetClusterName() // TODO - Deprecated + cautils.CustomerGUID = interfaces.clusterConfig.GetCustomerGUID() // TODO - Deprecated + interfaces.report.SetClusterName(interfaces.clusterConfig.GetClusterName()) + interfaces.report.SetCustomerGUID(interfaces.clusterConfig.GetCustomerGUID()) // cli handler setup go func() { + // policy handler setup + policyHandler := policyhandler.NewPolicyHandler(&processNotification, interfaces.resourceHandler) cli := NewCLIHandler(policyHandler, scanInfo) if err := cli.Scan(); err != nil { fmt.Println(err) @@ -55,12 +110,12 @@ func CliSetup(scanInfo cautils.ScanInfo) error { opaprocessorObj.ProcessRulesListenner() }() - resultsHandling := resultshandling.NewResultsHandler(&reportResults, reporter.NewReportEventReceiver(), printer.NewPrinter(scanInfo.Format, scanInfo.Output)) + resultsHandling := resultshandling.NewResultsHandler(&reportResults, interfaces.report, interfaces.printerHandler) score := resultsHandling.HandleResults(scanInfo) // print report url if scanInfo.FrameworkScan { - clusterConfig.GenerateURL() + interfaces.clusterConfig.GenerateURL() } adjustedFailThreshold := float32(scanInfo.FailThreshold) / 100 @@ -71,9 +126,9 @@ func CliSetup(scanInfo cautils.ScanInfo) error { return nil } -func NewCLIHandler(policyHandler *policyhandler.PolicyHandler, scanInfo cautils.ScanInfo) *CLIHandler { +func NewCLIHandler(policyHandler *policyhandler.PolicyHandler, scanInfo *cautils.ScanInfo) *CLIHandler { return &CLIHandler{ - scanInfo: &scanInfo, + scanInfo: scanInfo, policyHandler: policyHandler, } } diff --git a/opaprocessor/processorhandler.go b/opaprocessor/processorhandler.go index ab79456e..d4a6b759 100644 --- a/opaprocessor/processorhandler.go +++ b/opaprocessor/processorhandler.go @@ -8,7 +8,6 @@ import ( "github.com/armosec/kubescape/cautils" "github.com/armosec/opa-utils/exceptions" "github.com/armosec/opa-utils/reporthandling" - "github.com/armosec/opa-utils/score" "github.com/armosec/k8s-interface/k8sinterface" @@ -226,17 +225,6 @@ func (opap *OPAProcessor) regoEval(inputObj []map[string]interface{}, compiledRe return results, nil } -func (opap *OPAProcessor) updateScore() { - - if !k8sinterface.ConnectedToCluster { - return - } - - // calculate score - s := score.NewScore(k8sinterface.NewKubernetesApi(), ScoreConfigPath) - s.Calculate(opap.PostureReport.FrameworkReports) -} - func (opap *OPAProcessor) updateResults() { for f := range opap.PostureReport.FrameworkReports { // set exceptions diff --git a/policyhandler/handlenotification.go b/policyhandler/handlenotification.go index 76ecbb6e..3a3cfdd5 100644 --- a/policyhandler/handlenotification.go +++ b/policyhandler/handlenotification.go @@ -4,30 +4,25 @@ import ( "fmt" "github.com/armosec/kubescape/cautils" + "github.com/armosec/kubescape/resourcehandler" "github.com/armosec/opa-utils/reporthandling" "github.com/armosec/armoapi-go/armotypes" - "github.com/armosec/k8s-interface/k8sinterface" ) -var supportedFrameworks = []reporthandling.PolicyIdentifier{ - {Kind: "Framework", Name: "nsa"}, - {Kind: "Framework", Name: "mitre"}, -} - // PolicyHandler - type PolicyHandler struct { - k8s *k8sinterface.KubernetesApi + resourceHandler resourcehandler.IResourceHandler // we are listening on this chan in opaprocessor/processorhandler.go/ProcessRulesListenner func processPolicy *chan *cautils.OPASessionObj getters *cautils.Getters } // CreatePolicyHandler Create ws-handler obj -func NewPolicyHandler(processPolicy *chan *cautils.OPASessionObj, k8s *k8sinterface.KubernetesApi) *PolicyHandler { +func NewPolicyHandler(processPolicy *chan *cautils.OPASessionObj, resourceHandler resourcehandler.IResourceHandler) *PolicyHandler { return &PolicyHandler{ - k8s: k8s, - processPolicy: processPolicy, + resourceHandler: resourceHandler, + processPolicy: processPolicy, } } @@ -82,16 +77,7 @@ func (policyHandler *PolicyHandler) getPolicies(notification *reporthandling.Pol } func (policyHandler *PolicyHandler) getResources(notification *reporthandling.PolicyNotification, opaSessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) (*cautils.K8SResources, error) { - var k8sResources *cautils.K8SResources - var err error - if k8sinterface.ConnectedToCluster { // TODO - use interface - if opaSessionObj.PostureReport.ClusterAPIServerInfo, err = policyHandler.k8s.KubernetesClient.Discovery().ServerVersion(); err != nil { - cautils.ErrorDisplay(fmt.Sprintf("Failed to discover API server inforamtion: %v", err)) - } - k8sResources, err = policyHandler.getK8sResources(opaSessionObj.Frameworks, ¬ification.Designators, scanInfo.ExcludedNamespaces) - } else { - k8sResources, err = policyHandler.loadResources(opaSessionObj.Frameworks, scanInfo) - } - return k8sResources, err + opaSessionObj.PostureReport.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo() + return policyHandler.resourceHandler.GetResources(opaSessionObj.Frameworks, ¬ification.Designators) } diff --git a/policyhandler/filesloader.go b/resourcehandler/filesloader.go similarity index 88% rename from policyhandler/filesloader.go rename to resourcehandler/filesloader.go index 60f40f5a..eabb9109 100644 --- a/policyhandler/filesloader.go +++ b/resourcehandler/filesloader.go @@ -1,4 +1,4 @@ -package policyhandler +package resourcehandler import ( "bytes" @@ -8,7 +8,9 @@ import ( "path/filepath" "strings" + "github.com/armosec/armoapi-go/armotypes" "github.com/armosec/k8s-interface/workloadinterface" + "k8s.io/apimachinery/pkg/version" "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/kubescape/cautils" @@ -29,11 +31,22 @@ const ( JSON_FILE_FORMAT FileFormat = "json" ) -func (policyHandler *PolicyHandler) loadResources(frameworks []reporthandling.Framework, scanInfo *cautils.ScanInfo) (*cautils.K8SResources, error) { +// FileResourceHandler handle resources from files and URLs +type FileResourceHandler struct { + inputPatterns []string +} + +func NewFileResourceHandler(inputPatterns []string) *FileResourceHandler { + return &FileResourceHandler{ + inputPatterns: inputPatterns, + } +} + +func (fileHandler *FileResourceHandler) GetResources(frameworks []reporthandling.Framework, designator *armotypes.PortalDesignator) (*cautils.K8SResources, error) { workloads := []k8sinterface.IWorkload{} // load resource from local file system - w, err := loadResourcesFromFiles(scanInfo.InputPatterns) + w, err := loadResourcesFromFiles(fileHandler.inputPatterns) if err != nil { return nil, err } @@ -42,7 +55,7 @@ func (policyHandler *PolicyHandler) loadResources(frameworks []reporthandling.Fr } // load resources from url - w, err = loadResourcesFromUrl(scanInfo.InputPatterns) + w, err = loadResourcesFromUrl(fileHandler.inputPatterns) if err != nil { return nil, err } @@ -59,7 +72,7 @@ func (policyHandler *PolicyHandler) loadResources(frameworks []reporthandling.Fr // build resources map // map resources based on framework required resources: map["/group/version/kind"][] - k8sResources := setResourceMap(frameworks) + k8sResources := setResourceMap(frameworks) // TODO - support designators // save only relevant resources for i := range allResources { @@ -72,6 +85,10 @@ func (policyHandler *PolicyHandler) loadResources(frameworks []reporthandling.Fr } +func (fileHandler *FileResourceHandler) GetClusterAPIServerInfo() *version.Info { + return nil +} + func loadResourcesFromFiles(inputPatterns []string) ([]k8sinterface.IWorkload, error) { files, errs := listFiles(inputPatterns) if len(errs) > 0 { diff --git a/policyhandler/filesloader_test.go b/resourcehandler/filesloader_test.go similarity index 98% rename from policyhandler/filesloader_test.go rename to resourcehandler/filesloader_test.go index f2bea56b..04d64082 100644 --- a/policyhandler/filesloader_test.go +++ b/resourcehandler/filesloader_test.go @@ -1,4 +1,4 @@ -package policyhandler +package resourcehandler import ( "fmt" diff --git a/policyhandler/k8sresources.go b/resourcehandler/k8sresources.go similarity index 61% rename from policyhandler/k8sresources.go rename to resourcehandler/k8sresources.go index 28f17577..74bc4922 100644 --- a/policyhandler/k8sresources.go +++ b/resourcehandler/k8sresources.go @@ -1,6 +1,7 @@ -package policyhandler +package resourcehandler import ( + "context" "fmt" "strings" @@ -15,12 +16,23 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" k8slabels "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/version" "k8s.io/client-go/dynamic" ) -const SelectAllResources = "*" +type K8sResourceHandler struct { + k8s *k8sinterface.KubernetesApi + excludedNamespaces string // excluded namespaces (separated by comma) +} -func (policyHandler *PolicyHandler) getK8sResources(frameworks []reporthandling.Framework, designator *armotypes.PortalDesignator, excludedNamespaces string) (*cautils.K8SResources, error) { +func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, excludedNamespaces string) *K8sResourceHandler { + return &K8sResourceHandler{ + k8s: k8s, + excludedNamespaces: excludedNamespaces, + } +} + +func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.Framework, designator *armotypes.PortalDesignator) (*cautils.K8SResources, error) { // get k8s resources cautils.ProgressTextDisplay("Accessing Kubernetes objects") @@ -31,7 +43,7 @@ func (policyHandler *PolicyHandler) getK8sResources(frameworks []reporthandling. _, namespace, labels := armotypes.DigestPortalDesignator(designator) // pull k8s recourses - if err := policyHandler.pullResources(k8sResourcesMap, namespace, labels, excludedNamespaces); err != nil { + if err := k8sHandler.pullResources(k8sResourcesMap, namespace, labels, k8sHandler.excludedNamespaces); err != nil { return k8sResourcesMap, err } @@ -39,13 +51,21 @@ func (policyHandler *PolicyHandler) getK8sResources(frameworks []reporthandling. return k8sResourcesMap, nil } -func (policyHandler *PolicyHandler) pullResources(k8sResources *cautils.K8SResources, namespace string, labels map[string]string, excludedNamespaces string) error { +func (k8sHandler *K8sResourceHandler) GetClusterAPIServerInfo() *version.Info { + clusterAPIServerInfo, err := k8sHandler.k8s.KubernetesClient.Discovery().ServerVersion() + if err != nil { + cautils.ErrorDisplay(fmt.Sprintf("Failed to discover API server information: %v", err)) + return nil + } + return clusterAPIServerInfo +} +func (k8sHandler *K8sResourceHandler) pullResources(k8sResources *cautils.K8SResources, namespace string, labels map[string]string, excludedNamespaces string) error { var errs error for groupResource := range *k8sResources { apiGroup, apiVersion, resource := k8sinterface.StringToResourceGroup(groupResource) gvr := schema.GroupVersionResource{Group: apiGroup, Version: apiVersion, Resource: resource} - result, err := policyHandler.pullSingleResource(&gvr, namespace, labels, excludedNamespaces) + result, err := k8sHandler.pullSingleResource(&gvr, namespace, labels, excludedNamespaces) if err != nil { // handle error if errs == nil { @@ -61,7 +81,7 @@ func (policyHandler *PolicyHandler) pullResources(k8sResources *cautils.K8SResou return errs } -func (policyHandler *PolicyHandler) pullSingleResource(resource *schema.GroupVersionResource, namespace string, labels map[string]string, excludedNamespaces string) ([]unstructured.Unstructured, error) { +func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupVersionResource, namespace string, labels map[string]string, excludedNamespaces string) ([]unstructured.Unstructured, error) { // set labels listOptions := metav1.ListOptions{} @@ -76,13 +96,13 @@ func (policyHandler *PolicyHandler) pullSingleResource(resource *schema.GroupVer // set dynamic object var clientResource dynamic.ResourceInterface if namespace != "" && k8sinterface.IsNamespaceScope(resource.Group, resource.Resource) { - clientResource = policyHandler.k8s.DynamicClient.Resource(*resource).Namespace(namespace) + clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource).Namespace(namespace) } else { - clientResource = policyHandler.k8s.DynamicClient.Resource(*resource) + clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource) } // list resources - result, err := clientResource.List(policyHandler.k8s.Context, listOptions) + result, err := clientResource.List(context.Background(), listOptions) if err != nil { return nil, fmt.Errorf("failed to get resource: %v, namespace: %s, labelSelector: %v, reason: %s", resource, namespace, listOptions.LabelSelector, err.Error()) } diff --git a/policyhandler/k8sresourcesutils.go b/resourcehandler/k8sresourcesutils.go similarity index 98% rename from policyhandler/k8sresourcesutils.go rename to resourcehandler/k8sresourcesutils.go index 48932a42..2c14b05b 100644 --- a/policyhandler/k8sresourcesutils.go +++ b/resourcehandler/k8sresourcesutils.go @@ -1,4 +1,4 @@ -package policyhandler +package resourcehandler import ( "github.com/armosec/kubescape/cautils" diff --git a/policyhandler/k8sresourcesutils_test.go b/resourcehandler/k8sresourcesutils_test.go similarity index 98% rename from policyhandler/k8sresourcesutils_test.go rename to resourcehandler/k8sresourcesutils_test.go index 63239047..60f38936 100644 --- a/policyhandler/k8sresourcesutils_test.go +++ b/resourcehandler/k8sresourcesutils_test.go @@ -1,4 +1,4 @@ -package policyhandler +package resourcehandler import ( "github.com/armosec/k8s-interface/k8sinterface" diff --git a/policyhandler/repositoryscanner.go b/resourcehandler/repositoryscanner.go similarity index 99% rename from policyhandler/repositoryscanner.go rename to resourcehandler/repositoryscanner.go index a568e8c5..7b44017d 100644 --- a/policyhandler/repositoryscanner.go +++ b/resourcehandler/repositoryscanner.go @@ -1,4 +1,4 @@ -package policyhandler +package resourcehandler import ( "encoding/json" diff --git a/resourcehandler/resourceshandler.go b/resourcehandler/resourceshandler.go new file mode 100644 index 00000000..c2ddf15f --- /dev/null +++ b/resourcehandler/resourceshandler.go @@ -0,0 +1,13 @@ +package resourcehandler + +import ( + "github.com/armosec/armoapi-go/armotypes" + "github.com/armosec/kubescape/cautils" + "github.com/armosec/opa-utils/reporthandling" + "k8s.io/apimachinery/pkg/version" +) + +type IResourceHandler interface { + GetResources(frameworks []reporthandling.Framework, designator *armotypes.PortalDesignator) (*cautils.K8SResources, error) + GetClusterAPIServerInfo() *version.Info +} diff --git a/policyhandler/urlloader.go b/resourcehandler/urlloader.go similarity index 98% rename from policyhandler/urlloader.go rename to resourcehandler/urlloader.go index faa8f634..96fbdc0d 100644 --- a/policyhandler/urlloader.go +++ b/resourcehandler/urlloader.go @@ -1,4 +1,4 @@ -package policyhandler +package resourcehandler import ( "bytes" diff --git a/resultshandling/printer/jsonprinter.go b/resultshandling/printer/jsonprinter.go new file mode 100644 index 00000000..5a4a0f5b --- /dev/null +++ b/resultshandling/printer/jsonprinter.go @@ -0,0 +1,29 @@ +package printer + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/armosec/kubescape/cautils" +) + +type JsonPrinter struct { + writer *os.File +} + +func NewJsonPrinter() *JsonPrinter { + return &JsonPrinter{} +} + +func (jsonPrinter *JsonPrinter) SetWriter(outputFile string) { + jsonPrinter.writer = getWriter(outputFile) +} +func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { + postureReportStr, err := json.Marshal(opaSessionObj.PostureReport.FrameworkReports[0]) + if err != nil { + fmt.Println("Failed to convert posture report object!") + os.Exit(1) + } + jsonPrinter.writer.Write(postureReportStr) +} diff --git a/resultshandling/printer/junit.go b/resultshandling/printer/junit.go index 370a5d0a..cfa57003 100644 --- a/resultshandling/printer/junit.go +++ b/resultshandling/printer/junit.go @@ -3,10 +3,37 @@ package printer import ( "encoding/xml" "fmt" + "os" + "github.com/armosec/kubescape/cautils" "github.com/armosec/opa-utils/reporthandling" ) +type JunitPrinter struct { + writer *os.File +} + +func NewJunitPrinter() *JunitPrinter { + return &JunitPrinter{} +} + +func (junitPrinter *JunitPrinter) SetWriter(outputFile string) { + junitPrinter.writer = getWriter(outputFile) +} +func (junitPrinter *JunitPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { + junitResult, err := convertPostureReportToJunitResult(opaSessionObj.PostureReport) + if err != nil { + fmt.Println("Failed to convert posture report object!") + os.Exit(1) + } + postureReportStr, err := xml.Marshal(junitResult) + if err != nil { + fmt.Println("Failed to convert posture report object!") + os.Exit(1) + } + junitPrinter.writer.Write(postureReportStr) +} + type JUnitTestSuites struct { XMLName xml.Name `xml:"testsuites"` Suites []JUnitTestSuite `xml:"testsuite"` diff --git a/resultshandling/printer/prettyprinter.go b/resultshandling/printer/prettyprinter.go new file mode 100644 index 00000000..8b814fa9 --- /dev/null +++ b/resultshandling/printer/prettyprinter.go @@ -0,0 +1,215 @@ +package printer + +import ( + "fmt" + "os" + "sort" + + "github.com/armosec/kubescape/cautils" + "github.com/armosec/opa-utils/reporthandling" + "github.com/enescakir/emoji" + "github.com/olekukonko/tablewriter" +) + +type PrettyPrinter struct { + writer *os.File + summary Summary + sortedControlNames []string + frameworkSummary ControlSummary +} + +func NewPrettyPrinter() *PrettyPrinter { + return &PrettyPrinter{ + summary: NewSummary(), + } +} + +func (printer *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { + // score := calculatePostureScore(opaSessionObj.PostureReport) + + printer.summarySetup(opaSessionObj.PostureReport) + printer.printResults() + printer.printSummaryTable() + + // return score +} + +func (printer *PrettyPrinter) SetWriter(outputFile string) { + printer.writer = getWriter(outputFile) +} +func (printer *PrettyPrinter) summarySetup(postureReport *reporthandling.PostureReport) { + for _, fr := range postureReport.FrameworkReports { + printer.frameworkSummary = ControlSummary{ + TotalResources: fr.GetNumberOfResources(), + TotalFailed: fr.GetNumberOfFailedResources(), + TotalWarnign: fr.GetNumberOfWarningResources(), + } + for _, cr := range fr.ControlReports { + if len(cr.RuleReports) == 0 { + continue + } + workloadsSummary := listResultSummary(cr.RuleReports) + + printer.summary[cr.Name] = ControlSummary{ + 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(), + } + } + } + printer.sortedControlNames = printer.getSortedControlsNames() +} +func (printer *PrettyPrinter) printResults() { + for i := 0; i < len(printer.sortedControlNames); i++ { + controlSummary := printer.summary[printer.sortedControlNames[i]] + printer.printTitle(printer.sortedControlNames[i], &controlSummary) + printer.printResources(&controlSummary) + if printer.summary[printer.sortedControlNames[i]].TotalResources > 0 { + printer.printSummary(printer.sortedControlNames[i], &controlSummary) + } + + } +} + +func (printer *PrettyPrinter) printSummary(controlName string, controlSummary *ControlSummary) { + cautils.SimpleDisplay(printer.writer, "Summary - ") + cautils.SuccessDisplay(printer.writer, "Passed:%v ", controlSummary.TotalResources-controlSummary.TotalFailed-controlSummary.TotalWarnign) + cautils.WarningDisplay(printer.writer, "Excluded:%v ", controlSummary.TotalWarnign) + cautils.FailureDisplay(printer.writer, "Failed:%v ", controlSummary.TotalFailed) + cautils.InfoDisplay(printer.writer, "Total:%v\n", controlSummary.TotalResources) + if controlSummary.TotalFailed > 0 { + cautils.DescriptionDisplay(printer.writer, "Remediation: %v\n", controlSummary.Remediation) + } + cautils.DescriptionDisplay(printer.writer, "\n") + +} + +func (printer *PrettyPrinter) printTitle(controlName string, controlSummary *ControlSummary) { + cautils.InfoDisplay(printer.writer, "[control: %s] ", controlName) + 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) + } else if controlSummary.TotalWarnign != 0 { + cautils.WarningDisplay(printer.writer, "excluded %v\n", emoji.NeutralFace) + } else { + cautils.SuccessDisplay(printer.writer, "passed %v\n", emoji.ThumbsUp) + } + + cautils.DescriptionDisplay(printer.writer, "Description: %s\n", controlSummary.Description) + +} +func (printer *PrettyPrinter) 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 *PrettyPrinter) printGroupedResources(workloads map[string][]WorkloadSummary) { + + indent := INDENT + + for ns, rsc := range workloads { + preIndent := indent + if ns != "" { + cautils.SimpleDisplay(printer.writer, "%sNamespace %s\n", indent, ns) + } + preIndent2 := indent + for r := range rsc { + indent += indent + cautils.SimpleDisplay(printer.writer, fmt.Sprintf("%s%s - %s\n", indent, rsc[r].Kind, rsc[r].Name)) + indent = preIndent2 + } + indent = preIndent + } + +} + +func generateRow(control string, cs ControlSummary) []string { + row := []string{control} + row = append(row, cs.ToSlice()...) + if cs.TotalResources != 0 { + row = append(row, fmt.Sprintf("%d%s", percentage(cs.TotalResources, cs.TotalFailed), "%")) + } else { + row = append(row, EmptyPercentage) + } + return row +} + +func generateHeader() []string { + return []string{"Control Name", "Failed Resources", "Excluded Resources", "All Resources", "% success"} +} + +func percentage(big, small int) int { + if big == 0 { + if small == 0 { + return 100 + } + return 0 + } + return int(float64(float64(big-small)/float64(big)) * 100) +} +func generateFooter(numControlers, sumFailed, sumWarning, sumTotal int) []string { + // Control name | # failed resources | all resources | % success + row := []string{} + 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)) + if sumTotal != 0 { + row = append(row, fmt.Sprintf("%d%s", percentage(sumTotal, sumFailed), "%")) + } else { + row = append(row, EmptyPercentage) + } + return row +} +func (printer *PrettyPrinter) printSummaryTable() { + summaryTable := tablewriter.NewWriter(printer.writer) + summaryTable.SetAutoWrapText(false) + summaryTable.SetHeader(generateHeader()) + summaryTable.SetHeaderLine(true) + alignments := []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER} + summaryTable.SetColumnAlignment(alignments) + + for i := 0; i < len(printer.sortedControlNames); i++ { + controlSummary := printer.summary[printer.sortedControlNames[i]] + summaryTable.Append(generateRow(printer.sortedControlNames[i], controlSummary)) + } + summaryTable.SetFooter(generateFooter(len(printer.summary), printer.frameworkSummary.TotalFailed, printer.frameworkSummary.TotalWarnign, printer.frameworkSummary.TotalResources)) + summaryTable.Render() +} + +func (printer *PrettyPrinter) getSortedControlsNames() []string { + controlNames := make([]string, 0, len(printer.summary)) + for k := range printer.summary { + controlNames = append(controlNames, k) + } + sort.Strings(controlNames) + return controlNames +} + +func getWriter(outputFile string) *os.File { + os.Remove(outputFile) + if outputFile != "" { + f, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + fmt.Println("Error opening file") + return os.Stdout + } + return f + } + return os.Stdout + +} diff --git a/resultshandling/printer/printresults.go b/resultshandling/printer/printresults.go index 50a624c4..dcf4d4d7 100644 --- a/resultshandling/printer/printresults.go +++ b/resultshandling/printer/printresults.go @@ -1,17 +1,7 @@ package printer import ( - "encoding/json" - "encoding/xml" - "fmt" - "os" - "sort" - "github.com/armosec/kubescape/cautils" - "github.com/armosec/opa-utils/reporthandling" - - "github.com/enescakir/emoji" - "github.com/olekukonko/tablewriter" ) var INDENT = " " @@ -19,253 +9,23 @@ var INDENT = " " const EmptyPercentage = "NaN" const ( - PrettyPrinter string = "pretty-printer" - JsonPrinter string = "json" + PrettyFormat string = "pretty-printer" + JsonFormat string = "json" JunitResultPrinter string = "junit" ) -type Printer struct { - writer *os.File - summary Summary - sortedControlNames []string - printerType string - frameworkSummary ControlSummary +type IPrinter interface { + ActionPrint(opaSessionObj *cautils.OPASessionObj) + SetWriter(outputFile string) } -func NewPrinter(printerType, outputFile string) *Printer { - return &Printer{ - summary: NewSummary(), - writer: getWriter(outputFile), - printerType: printerType, +func GetPrinter(printFormat string) IPrinter { + switch printFormat { + case JsonFormat: + return NewJsonPrinter() + case JunitResultPrinter: + return NewJunitPrinter() + default: + return NewPrettyPrinter() } } - -func calculatePostureScore(postureReport *reporthandling.PostureReport) float32 { - totalResources := 0 - totalFailed := 0 - for _, frameworkReport := range postureReport.FrameworkReports { - totalFailed += frameworkReport.GetNumberOfFailedResources() - totalResources += frameworkReport.GetNumberOfResources() - } - if totalResources == 0 { - return float32(0) - } - return (float32(totalResources) - float32(totalFailed)) / float32(totalResources) -} - -func (printer *Printer) ActionPrint(opaSessionObj *cautils.OPASessionObj) float32 { - score := calculatePostureScore(opaSessionObj.PostureReport) - - if printer.printerType == PrettyPrinter { - printer.SummarySetup(opaSessionObj.PostureReport) - printer.PrintResults() - printer.PrintSummaryTable() - } else if printer.printerType == JsonPrinter { - postureReportStr, err := json.Marshal(opaSessionObj.PostureReport.FrameworkReports[0]) - if err != nil { - fmt.Println("Failed to convert posture report object!") - os.Exit(1) - } - printer.writer.Write(postureReportStr) - fmt.Printf("\nFinal score: %d\n", int(score*100)) - } else if printer.printerType == JunitResultPrinter { - junitResult, err := convertPostureReportToJunitResult(opaSessionObj.PostureReport) - if err != nil { - fmt.Println("Failed to convert posture report object!") - os.Exit(1) - } - postureReportStr, err := xml.Marshal(junitResult) - if err != nil { - fmt.Println("Failed to convert posture report object!") - os.Exit(1) - } - printer.writer.Write(postureReportStr) - fmt.Printf("\nFinal score: %d\n", int(score*100)) - } else if !cautils.IsSilent() { - fmt.Println("unknown output printer") - os.Exit(1) - } - - return score -} - -func (printer *Printer) SummarySetup(postureReport *reporthandling.PostureReport) { - for _, fr := range postureReport.FrameworkReports { - printer.frameworkSummary = ControlSummary{ - TotalResources: fr.GetNumberOfResources(), - TotalFailed: fr.GetNumberOfFailedResources(), - TotalWarnign: fr.GetNumberOfWarningResources(), - } - for _, cr := range fr.ControlReports { - if len(cr.RuleReports) == 0 { - continue - } - workloadsSummary := listResultSummary(cr.RuleReports) - - printer.summary[cr.Name] = ControlSummary{ - 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(), - } - } - } - printer.sortedControlNames = printer.getSortedControlsNames() -} -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.printResources(&controlSummary) - if printer.summary[printer.sortedControlNames[i]].TotalResources > 0 { - printer.printSummary(printer.sortedControlNames[i], &controlSummary) - } - - } -} - -func (printer *Printer) printSummary(controlName string, controlSummary *ControlSummary) { - cautils.SimpleDisplay(printer.writer, "Summary - ") - cautils.SuccessDisplay(printer.writer, "Passed:%v ", controlSummary.TotalResources-controlSummary.TotalFailed-controlSummary.TotalWarnign) - cautils.WarningDisplay(printer.writer, "Excluded:%v ", controlSummary.TotalWarnign) - cautils.FailureDisplay(printer.writer, "Failed:%v ", controlSummary.TotalFailed) - cautils.InfoDisplay(printer.writer, "Total:%v\n", controlSummary.TotalResources) - if controlSummary.TotalFailed > 0 { - cautils.DescriptionDisplay(printer.writer, "Remediation: %v\n", controlSummary.Remediation) - } - cautils.DescriptionDisplay(printer.writer, "\n") - -} - -func (printer *Printer) printTitle(controlName string, controlSummary *ControlSummary) { - cautils.InfoDisplay(printer.writer, "[control: %s] ", controlName) - 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) - } else if controlSummary.TotalWarnign != 0 { - cautils.WarningDisplay(printer.writer, "excluded %v\n", emoji.NeutralFace) - } else { - cautils.SuccessDisplay(printer.writer, "passed %v\n", emoji.ThumbsUp) - } - - cautils.DescriptionDisplay(printer.writer, "Description: %s\n", controlSummary.Description) - -} -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 workloads { - preIndent := indent - if ns != "" { - cautils.SimpleDisplay(printer.writer, "%sNamespace %s\n", indent, ns) - } - preIndent2 := indent - for r := range rsc { - indent += indent - cautils.SimpleDisplay(printer.writer, fmt.Sprintf("%s%s - %s\n", indent, rsc[r].Kind, rsc[r].Name)) - indent = preIndent2 - } - indent = preIndent - } - -} - -func (printer *Printer) PrintUrl(url string) { - cautils.InfoTextDisplay(printer.writer, url) -} - -func generateRow(control string, cs ControlSummary) []string { - row := []string{control} - row = append(row, cs.ToSlice()...) - if cs.TotalResources != 0 { - row = append(row, fmt.Sprintf("%d%s", percentage(cs.TotalResources, cs.TotalFailed), "%")) - } else { - row = append(row, EmptyPercentage) - } - return row -} - -func generateHeader() []string { - return []string{"Control Name", "Failed Resources", "Excluded Resources", "All Resources", "% success"} -} - -func percentage(big, small int) int { - if big == 0 { - if small == 0 { - return 100 - } - return 0 - } - return int(float64(float64(big-small)/float64(big)) * 100) -} -func generateFooter(numControlers, sumFailed, sumWarning, sumTotal int) []string { - // Control name | # failed resources | all resources | % success - row := []string{} - 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)) - if sumTotal != 0 { - row = append(row, fmt.Sprintf("%d%s", percentage(sumTotal, sumFailed), "%")) - } else { - row = append(row, EmptyPercentage) - } - return row -} -func (printer *Printer) PrintSummaryTable() { - summaryTable := tablewriter.NewWriter(printer.writer) - summaryTable.SetAutoWrapText(false) - summaryTable.SetHeader(generateHeader()) - summaryTable.SetHeaderLine(true) - alignments := []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER} - summaryTable.SetColumnAlignment(alignments) - - for i := 0; i < len(printer.sortedControlNames); i++ { - controlSummary := printer.summary[printer.sortedControlNames[i]] - summaryTable.Append(generateRow(printer.sortedControlNames[i], controlSummary)) - } - summaryTable.SetFooter(generateFooter(len(printer.summary), printer.frameworkSummary.TotalFailed, printer.frameworkSummary.TotalWarnign, printer.frameworkSummary.TotalResources)) - summaryTable.Render() -} - -func (printer *Printer) getSortedControlsNames() []string { - controlNames := make([]string, 0, len(printer.summary)) - for k := range printer.summary { - controlNames = append(controlNames, k) - } - sort.Strings(controlNames) - return controlNames -} - -func getWriter(outputFile string) *os.File { - os.Remove(outputFile) - if outputFile != "" { - f, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - fmt.Println("Error opening file") - return os.Stdout - } - return f - } - return os.Stdout - -} diff --git a/resultshandling/printer/silentprinter.go b/resultshandling/printer/silentprinter.go new file mode 100644 index 00000000..65737b5c --- /dev/null +++ b/resultshandling/printer/silentprinter.go @@ -0,0 +1,11 @@ +package printer + +import ( + "github.com/armosec/kubescape/cautils" +) + +type SilentPrinter struct { +} + +func (silentPrinter *SilentPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { +} diff --git a/resultshandling/reporter/mockreporter.go b/resultshandling/reporter/mockreporter.go new file mode 100644 index 00000000..21e37bb5 --- /dev/null +++ b/resultshandling/reporter/mockreporter.go @@ -0,0 +1,17 @@ +package reporter + +import "github.com/armosec/kubescape/cautils" + +type ReportMock struct { +} + +func NewReportMock() *ReportMock { + return &ReportMock{} +} +func (reportMock *ReportMock) ActionSendReport(opaSessionObj *cautils.OPASessionObj) {} + +func (reportMock *ReportMock) SetCustomerGUID(customerGUID string) { +} + +func (reportMock *ReportMock) SetClusterName(clusterName string) { +} diff --git a/resultshandling/reporter/reporteventreceiver.go b/resultshandling/reporter/reporteventreceiver.go index 99364328..dc27ad68 100644 --- a/resultshandling/reporter/reporteventreceiver.go +++ b/resultshandling/reporter/reporteventreceiver.go @@ -11,41 +11,52 @@ import ( "github.com/armosec/opa-utils/reporthandling" ) +type IReport interface { + ActionSendReport(opaSessionObj *cautils.OPASessionObj) + SetCustomerGUID(customerGUID string) + SetClusterName(clusterName string) +} + type ReportEventReceiver struct { - httpClient http.Client - host url.URL + httpClient http.Client + host url.URL + clusterName string + customerGUID string } func NewReportEventReceiver() *ReportEventReceiver { - hostURL := initEventReceiverURL() return &ReportEventReceiver{ httpClient: http.Client{}, - host: *hostURL, + // host: *hostURL, } } -func (report *ReportEventReceiver) ActionSendReportListenner(opaSessionObj *cautils.OPASessionObj) { - if cautils.CustomerGUID == "" { - return - } - //Add score - +func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASessionObj) { // Remove data before reporting keepFields := []string{"kind", "apiVersion", "metadata"} keepMetadataFields := []string{"name", "namespace", "labels"} opaSessionObj.PostureReport.RemoveData(keepFields, keepMetadataFields) - if err := report.Send(opaSessionObj.PostureReport); err != nil { + if err := report.send(opaSessionObj.PostureReport); err != nil { fmt.Println(err) } } -func (report *ReportEventReceiver) Send(postureReport *reporthandling.PostureReport) error { + +func (report *ReportEventReceiver) SetCustomerGUID(customerGUID string) { + report.customerGUID = customerGUID +} + +func (report *ReportEventReceiver) SetClusterName(clusterName string) { + report.clusterName = clusterName +} + +func (report *ReportEventReceiver) send(postureReport *reporthandling.PostureReport) error { reqBody, err := json.Marshal(*postureReport) if err != nil { return fmt.Errorf("in 'Send' failed to json.Marshal, reason: %v", err) } - host := hostToString(&report.host, postureReport.ReportID) + host := hostToString(report.initEventReceiverURL(), postureReport.ReportID) req, err := http.NewRequest("POST", host, bytes.NewReader(reqBody)) if err != nil { diff --git a/resultshandling/reporter/reporteventreceiverutils.go b/resultshandling/reporter/reporteventreceiverutils.go index 96fa75be..a32a3c22 100644 --- a/resultshandling/reporter/reporteventreceiverutils.go +++ b/resultshandling/reporter/reporteventreceiverutils.go @@ -7,7 +7,6 @@ import ( "net/url" "strings" - "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/cautils/getter" "github.com/gofrs/uuid" ) @@ -33,15 +32,15 @@ func httpRespToString(resp *http.Response) (string, error) { return strBuilder.String(), err } -func initEventReceiverURL() *url.URL { +func (report *ReportEventReceiver) initEventReceiverURL() *url.URL { urlObj := url.URL{} urlObj.Scheme = "https" urlObj.Host = getter.GetArmoAPIConnector().GetReportReceiverURL() urlObj.Path = "/k8s/postureReport" q := urlObj.Query() - q.Add("customerGUID", uuid.FromStringOrNil(cautils.CustomerGUID).String()) - q.Add("clusterName", cautils.ClusterName) + q.Add("customerGUID", uuid.FromStringOrNil(report.customerGUID).String()) + q.Add("clusterName", report.clusterName) urlObj.RawQuery = q.Encode() diff --git a/resultshandling/results.go b/resultshandling/results.go index 2db5e8f0..f3c6bbab 100644 --- a/resultshandling/results.go +++ b/resultshandling/results.go @@ -4,15 +4,16 @@ import ( "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/resultshandling/printer" "github.com/armosec/kubescape/resultshandling/reporter" + "github.com/armosec/opa-utils/reporthandling" ) type ResultsHandler struct { opaSessionObj *chan *cautils.OPASessionObj - reporterObj *reporter.ReportEventReceiver - printerObj *printer.Printer + reporterObj reporter.IReport + printerObj printer.IPrinter } -func NewResultsHandler(opaSessionObj *chan *cautils.OPASessionObj, reporterObj *reporter.ReportEventReceiver, printerObj *printer.Printer) *ResultsHandler { +func NewResultsHandler(opaSessionObj *chan *cautils.OPASessionObj, reporterObj reporter.IReport, printerObj printer.IPrinter) *ResultsHandler { return &ResultsHandler{ opaSessionObj: opaSessionObj, reporterObj: reporterObj, @@ -20,16 +21,28 @@ func NewResultsHandler(opaSessionObj *chan *cautils.OPASessionObj, reporterObj * } } -func (resultsHandler *ResultsHandler) HandleResults(scanInfo cautils.ScanInfo) float32 { +func (resultsHandler *ResultsHandler) HandleResults(scanInfo *cautils.ScanInfo) float32 { opaSessionObj := <-*resultsHandler.opaSessionObj - score := resultsHandler.printerObj.ActionPrint(opaSessionObj) + resultsHandler.printerObj.ActionPrint(opaSessionObj) - // Don't send report for control scan - if scanInfo.FrameworkScan { // TODO - use interface for ActionSendReportListenner - resultsHandler.reporterObj.ActionSendReportListenner(opaSessionObj) - } + resultsHandler.reporterObj.ActionSendReport(opaSessionObj) - return score + // TODO - get score from table + return CalculatePostureScore(opaSessionObj.PostureReport) +} + +// CalculatePostureScore calculate final score +func CalculatePostureScore(postureReport *reporthandling.PostureReport) float32 { + totalResources := 0 + totalFailed := 0 + for _, frameworkReport := range postureReport.FrameworkReports { + totalFailed += frameworkReport.GetNumberOfFailedResources() + totalResources += frameworkReport.GetNumberOfResources() + } + if totalResources == 0 { + return float32(0) + } + return (float32(totalResources) - float32(totalFailed)) / float32(totalResources) } From aec8198131700ed4779a96e78510d7aa7cbb0ed1 Mon Sep 17 00:00:00 2001 From: dwertent Date: Mon, 25 Oct 2021 08:41:15 +0300 Subject: [PATCH 2/2] adding score to interface --- clihandler/initcli.go | 4 +--- resultshandling/printer/jsonprinter.go | 5 +++++ resultshandling/printer/junit.go | 5 +++++ resultshandling/printer/prettyprinter.go | 4 ++++ resultshandling/printer/printresults.go | 1 + resultshandling/reporter/reporteventreceiver.go | 3 --- resultshandling/results.go | 5 ++++- 7 files changed, 20 insertions(+), 7 deletions(-) diff --git a/clihandler/initcli.go b/clihandler/initcli.go index 9dd35a85..0ac9289e 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -114,9 +114,7 @@ func CliSetup(scanInfo *cautils.ScanInfo) error { score := resultsHandling.HandleResults(scanInfo) // print report url - if scanInfo.FrameworkScan { - interfaces.clusterConfig.GenerateURL() - } + interfaces.clusterConfig.GenerateURL() adjustedFailThreshold := float32(scanInfo.FailThreshold) / 100 if score < adjustedFailThreshold { diff --git a/resultshandling/printer/jsonprinter.go b/resultshandling/printer/jsonprinter.go index 5a4a0f5b..26c4958d 100644 --- a/resultshandling/printer/jsonprinter.go +++ b/resultshandling/printer/jsonprinter.go @@ -19,6 +19,11 @@ func NewJsonPrinter() *JsonPrinter { func (jsonPrinter *JsonPrinter) SetWriter(outputFile string) { jsonPrinter.writer = getWriter(outputFile) } + +func (jsonPrinter *JsonPrinter) Score(score float32) { + fmt.Printf("\nFinal score: %d\n", int(score)) +} + func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { postureReportStr, err := json.Marshal(opaSessionObj.PostureReport.FrameworkReports[0]) if err != nil { diff --git a/resultshandling/printer/junit.go b/resultshandling/printer/junit.go index cfa57003..1a9a7956 100644 --- a/resultshandling/printer/junit.go +++ b/resultshandling/printer/junit.go @@ -20,6 +20,11 @@ func NewJunitPrinter() *JunitPrinter { func (junitPrinter *JunitPrinter) SetWriter(outputFile string) { junitPrinter.writer = getWriter(outputFile) } + +func (junitPrinter *JunitPrinter) Score(score float32) { + fmt.Printf("\nFinal score: %d\n", int(score)) +} + func (junitPrinter *JunitPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { junitResult, err := convertPostureReportToJunitResult(opaSessionObj.PostureReport) if err != nil { diff --git a/resultshandling/printer/prettyprinter.go b/resultshandling/printer/prettyprinter.go index 8b814fa9..1d4b366b 100644 --- a/resultshandling/printer/prettyprinter.go +++ b/resultshandling/printer/prettyprinter.go @@ -37,6 +37,10 @@ func (printer *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) func (printer *PrettyPrinter) SetWriter(outputFile string) { printer.writer = getWriter(outputFile) } + +func (printer *PrettyPrinter) Score(score float32) { +} + func (printer *PrettyPrinter) summarySetup(postureReport *reporthandling.PostureReport) { for _, fr := range postureReport.FrameworkReports { printer.frameworkSummary = ControlSummary{ diff --git a/resultshandling/printer/printresults.go b/resultshandling/printer/printresults.go index dcf4d4d7..8452c19f 100644 --- a/resultshandling/printer/printresults.go +++ b/resultshandling/printer/printresults.go @@ -17,6 +17,7 @@ const ( type IPrinter interface { ActionPrint(opaSessionObj *cautils.OPASessionObj) SetWriter(outputFile string) + Score(score float32) } func GetPrinter(printFormat string) IPrinter { diff --git a/resultshandling/reporter/reporteventreceiver.go b/resultshandling/reporter/reporteventreceiver.go index dc27ad68..070ba745 100644 --- a/resultshandling/reporter/reporteventreceiver.go +++ b/resultshandling/reporter/reporteventreceiver.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "net/http" - "net/url" "github.com/armosec/kubescape/cautils" "github.com/armosec/opa-utils/reporthandling" @@ -19,7 +18,6 @@ type IReport interface { type ReportEventReceiver struct { httpClient http.Client - host url.URL clusterName string customerGUID string } @@ -27,7 +25,6 @@ type ReportEventReceiver struct { func NewReportEventReceiver() *ReportEventReceiver { return &ReportEventReceiver{ httpClient: http.Client{}, - // host: *hostURL, } } diff --git a/resultshandling/results.go b/resultshandling/results.go index f3c6bbab..25ddd3b2 100644 --- a/resultshandling/results.go +++ b/resultshandling/results.go @@ -30,7 +30,10 @@ func (resultsHandler *ResultsHandler) HandleResults(scanInfo *cautils.ScanInfo) resultsHandler.reporterObj.ActionSendReport(opaSessionObj) // TODO - get score from table - return CalculatePostureScore(opaSessionObj.PostureReport) + score := CalculatePostureScore(opaSessionObj.PostureReport) + resultsHandler.printerObj.Score(score) + + return score } // CalculatePostureScore calculate final score