diff --git a/clihandler/initcli.go b/clihandler/initcli.go index b870f364..d8ccf8b1 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -37,6 +37,18 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { setSubmitBehavior(scanInfo, tenantConfig) hostSensorHandler := getHostSensorHandler(scanInfo, k8s) + if err := hostSensorHandler.Init(); err != nil { + errMsg := "failed to init host sensor" + if scanInfo.VerboseMode { + errMsg = fmt.Sprintf("%s: %v", errMsg, err) + } + cautils.ErrorDisplay(errMsg) + hostSensorHandler = &hostsensorutils.HostSensorHandlerMock{} + } + // excluding hostsensor namespace + if len(scanInfo.IncludeNamespaces) == 0 && hostSensorHandler.GetNamespace() != "" { + scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, hostSensorHandler.GetNamespace()) + } resourceHandler := getResourceHandler(scanInfo, tenantConfig, k8s, hostSensorHandler) @@ -73,28 +85,19 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error { interfaces.report.SetClusterName(interfaces.tenantConfig.GetClusterName()) interfaces.report.SetCustomerGUID(interfaces.tenantConfig.GetCustomerGUID()) - if err := interfaces.hostSensorHandler.Init(); err != nil { - errMsg := "failed to init host sensor" - if scanInfo.VerboseMode { - errMsg = fmt.Sprintf("%s: %v", errMsg, err) - } - cautils.ErrorDisplay(errMsg) - } else if len(scanInfo.IncludeNamespaces) == 0 && interfaces.hostSensorHandler.GetNamespace() != "" { - scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, interfaces.hostSensorHandler) - defer func() { - if err := interfaces.hostSensorHandler.TearDown(); err != nil { - errMsg := "failed to tear down host sensor" - if scanInfo.VerboseMode { - errMsg = fmt.Sprintf("%s: %v", errMsg, err) - } - cautils.ErrorDisplay(errMsg) - } - }() - } - // set policy getter only after setting the customerGUID setPolicyGetter(scanInfo, interfaces.tenantConfig.GetCustomerGUID()) + defer func() { + if err := interfaces.hostSensorHandler.TearDown(); err != nil { + errMsg := "failed to tear down host sensor" + if scanInfo.VerboseMode { + errMsg = fmt.Sprintf("%s: %v", errMsg, err) + } + cautils.ErrorDisplay(errMsg) + } + }() + // cli handler setup go func() { // policy handler setup diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index 9521d937..cc7ebb35 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -70,7 +70,6 @@ func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.Kubernet } return &hostsensorutils.HostSensorHandlerMock{} } - func getFieldSelector(scanInfo *cautils.ScanInfo) resourcehandler.IFieldSelector { if scanInfo.IncludeNamespaces != "" { return resourcehandler.NewIncludeSelector(scanInfo.IncludeNamespaces) diff --git a/go.mod b/go.mod index ac4d14f5..72545313 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,11 @@ go 1.17 require ( github.com/armosec/armoapi-go v0.0.23 - github.com/armosec/k8s-interface v0.0.45 - github.com/armosec/opa-utils v0.0.68 + github.com/armosec/k8s-interface v0.0.50 + github.com/armosec/opa-utils v0.0.75 github.com/armosec/rbac-utils v0.0.9 github.com/armosec/utils-go v0.0.3 - github.com/briandowns/spinner v1.16.0 + github.com/briandowns/spinner v1.18.0 github.com/enescakir/emoji v1.0.0 github.com/fatih/color v1.13.0 github.com/gofrs/uuid v4.1.0+incompatible diff --git a/go.sum b/go.sum index 21f29534..eea11167 100644 --- a/go.sum +++ b/go.sum @@ -88,12 +88,11 @@ github.com/armosec/armoapi-go v0.0.23 h1:jqoLIWM5CR7DCD9fpFgN0ePqtHvOCoZv/XzCwsU github.com/armosec/armoapi-go v0.0.23/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw= github.com/armosec/k8s-interface v0.0.8/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM= github.com/armosec/k8s-interface v0.0.37/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= -github.com/armosec/k8s-interface v0.0.43/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= -github.com/armosec/k8s-interface v0.0.45 h1:LnncyBfarM29vXB0JwteSiLphVsiu7ONrNmmWSi377M= -github.com/armosec/k8s-interface v0.0.45/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= +github.com/armosec/k8s-interface v0.0.50 h1:iLPGI0j85vwKANr9QDAnba4Efjg3DyIJg15jRJdvOnc= +github.com/armosec/k8s-interface v0.0.50/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c= -github.com/armosec/opa-utils v0.0.68 h1:qsmDjEVPdfJ6+hlSeoBZF5sgeR63HdtPfpVQIg0r1G0= -github.com/armosec/opa-utils v0.0.68/go.mod h1:NVJEQ7695VyylqC7dHw2nD7MHIGJrRIXLgyej1onF64= +github.com/armosec/opa-utils v0.0.75 h1:GBI3K18xc3WXJHIorIu4bGNAsfMYHUc1x7zueDz2ZbY= +github.com/armosec/opa-utils v0.0.75/go.mod h1:L7d+uiIIXAZ3LEyKtmEIbMcI1hWgWaXGpn5zVCqzwSU= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= github.com/armosec/rbac-utils v0.0.9 h1:rIOWp4K7BELUNX32ktSjVbb8d/0SpH7W76W6Tf+8rzw= github.com/armosec/rbac-utils v0.0.9/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0= @@ -118,8 +117,8 @@ github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqO github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/briandowns/spinner v1.16.0 h1:DFmp6hEaIx2QXXuqSJmtfSBSAjRmpGiKG6ip2Wm/yOs= -github.com/briandowns/spinner v1.16.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= +github.com/briandowns/spinner v1.18.0 h1:SJs0maNOs4FqhBwiJ3Gr7Z1D39/rukIVGQvpNZVHVcM= +github.com/briandowns/spinner v1.18.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bytecodealliance/wasmtime-go v0.30.0 h1:WfYpr4WdqInt8m5/HvYinf+HrSEAIhItKIcth+qb1h4= github.com/bytecodealliance/wasmtime-go v0.30.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI= diff --git a/hostsensorutils/hostsensordeploy.go b/hostsensorutils/hostsensordeploy.go index 83a6d020..06e1c008 100644 --- a/hostsensorutils/hostsensordeploy.go +++ b/hostsensorutils/hostsensordeploy.go @@ -201,5 +201,8 @@ func (hsh *HostSensorHandler) TearDown() error { } func (hsh *HostSensorHandler) GetNamespace() string { + if hsh.DaemonSet == nil { + return "" + } return hsh.DaemonSet.Namespace } diff --git a/hostsensorutils/hostsensorgetfrompod.go b/hostsensorutils/hostsensorgetfrompod.go index c82f3d33..69d81301 100644 --- a/hostsensorutils/hostsensorgetfrompod.go +++ b/hostsensorutils/hostsensorgetfrompod.go @@ -9,7 +9,6 @@ import ( "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/kubescape/cautils" "github.com/armosec/opa-utils/objectsenvelopes/hostsensor" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" ) @@ -158,7 +157,7 @@ func (hsh *HostSensorHandler) GetOsReleaseFile() ([]hostsensor.HostSensorDataEnv // return list of func (hsh *HostSensorHandler) GetKubeletConfigurations() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them - res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletConfigurations", "") // empty kind, will be overridden + res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletConfigurations", "KubeletConfigurations") // empty kind, will be overridden for resIdx := range res { jsonBytes, err := yaml.YAMLToJSON(res[resIdx].Data) if err != nil { @@ -166,14 +165,6 @@ func (hsh *HostSensorHandler) GetKubeletConfigurations() ([]hostsensor.HostSenso continue } res[resIdx].SetData(jsonBytes) - - kindDet := metav1.TypeMeta{} - if err = json.Unmarshal(jsonBytes, &kindDet); err != nil { - fmt.Printf("In GetKubeletConfigurations failed to Unmarshal GroupVersionKind: %v;\n%v", err, jsonBytes) - continue - } - res[resIdx].SetKind(kindDet.Kind) - res[resIdx].SetApiVersion(k8sinterface.JoinGroupVersion(kindDet.GroupVersionKind().Group, kindDet.GroupVersionKind().Version)) } return res, err } diff --git a/opaprocessor/processorhandlerutils.go b/opaprocessor/processorhandlerutils.go index 2bdb65e8..beb2c208 100644 --- a/opaprocessor/processorhandlerutils.go +++ b/opaprocessor/processorhandlerutils.go @@ -107,7 +107,7 @@ func isRuleKubescapeVersionCompatible(rule *reporthandling.PolicyRule) bool { } func removeData(obj workloadinterface.IMetadata) { - if !workloadinterface.IsTypeWorkload(obj.GetObject()) { + if !k8sinterface.IsTypeWorkload(obj.GetObject()) { return // remove data only from kubernetes objects } workload := workloadinterface.NewWorkloadObj(obj.GetObject()) diff --git a/opaprocessor/processorhandlerutils_test.go b/opaprocessor/processorhandlerutils_test.go index ac1c74a1..3dc82ee2 100644 --- a/opaprocessor/processorhandlerutils_test.go +++ b/opaprocessor/processorhandlerutils_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/armosec/armoapi-go/armotypes" + "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/k8s-interface/workloadinterface" "github.com/armosec/kubescape/cautils" "github.com/armosec/opa-utils/reporthandling" @@ -57,6 +58,8 @@ func TestIsRuleKubescapeVersionCompatible(t *testing.T) { } func TestRemoveData(t *testing.T) { + k8sinterface.InitializeMapResourcesMock() + w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"demoservice-server"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"demoservice-server"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}}}` obj, _ := workloadinterface.NewWorkload([]byte(w)) removeData(obj) diff --git a/policyhandler/handlenotification.go b/policyhandler/handlenotification.go index 2c27bdda..be7cf0ab 100644 --- a/policyhandler/handlenotification.go +++ b/policyhandler/handlenotification.go @@ -59,6 +59,5 @@ func (policyHandler *PolicyHandler) getResources(notification *reporthandling.Po opaSessionObj.K8SResources = resourcesMap opaSessionObj.AllResources = allResources - cautils.SuccessTextDisplay("Let’s start!!!") return nil } diff --git a/resourcehandler/filesloader.go b/resourcehandler/filesloader.go index 28cce904..25cc7e74 100644 --- a/resourcehandler/filesloader.go +++ b/resourcehandler/filesloader.go @@ -125,7 +125,7 @@ func mapResources(workloads []workloadinterface.IMetadata) map[string][]workload continue } - if workloadinterface.IsTypeWorkload(workloads[i].GetObject()) { + if k8sinterface.IsTypeWorkload(workloads[i].GetObject()) { w := workloadinterface.NewWorkloadObj(workloads[i].GetObject()) if groupVersionResource.Group != w.GetGroup() || groupVersionResource.Version != w.GetVersion() { // TODO - print warning diff --git a/resourcehandler/k8sresources.go b/resourcehandler/k8sresources.go index 5e56a696..3aa926ad 100644 --- a/resourcehandler/k8sresources.go +++ b/resourcehandler/k8sresources.go @@ -47,6 +47,8 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F // get k8s resources cautils.ProgressTextDisplay("Accessing Kubernetes objects") + cautils.StartSpinner() + // build resources map // map resources based on framework required resources: map["/group/version/kind"][] k8sResourcesMap := setResourceMap(frameworks) @@ -69,6 +71,8 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F cautils.WarningDisplay(os.Stdout, fmt.Sprintf("Warning: %v\n", err.Error())) } + cautils.StopSpinner() + cautils.SuccessTextDisplay("Accessed successfully to Kubernetes objects") return k8sResourcesMap, allResources, nil } diff --git a/resultshandling/printer/prettyprinter.go b/resultshandling/printer/prettyprinter.go index 02148df7..b3a2ca36 100644 --- a/resultshandling/printer/prettyprinter.go +++ b/resultshandling/printer/prettyprinter.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "sort" + "strings" "github.com/armosec/k8s-interface/workloadinterface" "github.com/armosec/kubescape/cautils" @@ -81,6 +82,7 @@ func (printer *PrettyPrinter) summarySetup(fr reporthandling.FrameworkReport, al //controlSummary printer.summary[cr.Name] = ResultSummary{ + ID: cr.ControlID, RiskScore: cr.Score, TotalResources: cr.GetNumberOfResources(), TotalFailed: cr.GetNumberOfFailedResources(), @@ -120,11 +122,10 @@ func (printer *PrettyPrinter) printSummary(controlName string, controlSummary *R cautils.DescriptionDisplay(printer.writer, "\n") } - func (printer *PrettyPrinter) printTitle(controlName string, controlSummary *ResultSummary) { - cautils.InfoDisplay(printer.writer, "[control: %s] ", controlName) + cautils.InfoDisplay(printer.writer, "[control: %s - %s] ", controlName, getControlURL(controlSummary.ID)) if controlSummary.TotalResources == 0 { - cautils.InfoDisplay(printer.writer, "resources not found %v\n", emoji.ConfusedFace) + cautils.InfoDisplay(printer.writer, "skipped %v\n", emoji.ConfusedFace) } else if controlSummary.TotalFailed != 0 { cautils.FailureDisplay(printer.writer, "failed %v\n", emoji.SadButRelievedFace) } else if controlSummary.TotalWarning != 0 { @@ -155,32 +156,21 @@ func (printer *PrettyPrinter) printResources(controlSummary *ResultSummary) { func (printer *PrettyPrinter) printGroupedResources(workloads map[string][]WorkloadSummary) { indent := INDENT - for ns, rsc := range workloads { - if !isKindToBeGrouped(ns) { - printer.printGroupedResource(indent, ns, rsc) - } - } - if rsc, ok := workloads["User"]; ok { - printer.printGroupedResource(indent, "User", rsc) - } - if rsc, ok := workloads["Group"]; ok { - printer.printGroupedResource(indent, "Group", rsc) + for title, rsc := range workloads { + printer.printGroupedResource(indent, title, rsc) } } -func (printer *PrettyPrinter) printGroupedResource(indent string, ns string, rsc []WorkloadSummary) { +func (printer *PrettyPrinter) printGroupedResource(indent string, title string, rsc []WorkloadSummary) { preIndent := indent - if isKindToBeGrouped(ns) { - cautils.SimpleDisplay(printer.writer, "%s%ss\n", indent, ns) - } else if ns != "" { - cautils.SimpleDisplay(printer.writer, "%sNamespace %s\n", indent, ns) - } - preIndent2 := indent - for r := range rsc { + if title != "" { + cautils.SimpleDisplay(printer.writer, "%s%s\n", indent, title) indent += indent + } + + for r := range rsc { relatedObjectsStr := generateRelatedObjectsStr(rsc[r]) cautils.SimpleDisplay(printer.writer, fmt.Sprintf("%s%s - %s %s\n", indent, rsc[r].resource.GetKind(), rsc[r].resource.GetName(), relatedObjectsStr)) - indent = preIndent2 } indent = preIndent } @@ -206,9 +196,9 @@ func generateRow(control string, cs ResultSummary) []string { row := []string{control} row = append(row, cs.ToSlice()...) if cs.TotalResources != 0 { - row = append(row, fmt.Sprintf("%.2f%s", cs.RiskScore, "%")) + row = append(row, fmt.Sprintf("%d", int(cs.RiskScore))+"%") } else { - row = append(row, EmptyPercentage) + row = append(row, "skipped") } return row } @@ -217,16 +207,6 @@ func generateHeader() []string { return []string{"Control Name", "Failed Resources", "Excluded Resources", "All Resources", "% risk-score"} } -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(printer *PrettyPrinter) []string { // Control name | # failed resources | all resources | % success row := []string{} @@ -295,3 +275,7 @@ func getWriter(outputFile string) *os.File { return os.Stdout } + +func getControlURL(controlID string) string { + return fmt.Sprintf("https://hub.armo.cloud/docs/%s", strings.ToLower(controlID)) +} diff --git a/resultshandling/printer/printresults.go b/resultshandling/printer/printresults.go index bd82b4ed..5c4a2746 100644 --- a/resultshandling/printer/printresults.go +++ b/resultshandling/printer/printresults.go @@ -6,8 +6,6 @@ import ( var INDENT = " " -const EmptyPercentage = "NaN" - const ( PrettyFormat string = "pretty-printer" JsonFormat string = "json" diff --git a/resultshandling/printer/summary.go b/resultshandling/printer/summary.go index 696ad406..d91b2a11 100644 --- a/resultshandling/printer/summary.go +++ b/resultshandling/printer/summary.go @@ -14,6 +14,7 @@ func NewSummary() Summary { } type ResultSummary struct { + ID string RiskScore float32 TotalResources int TotalFailed int diff --git a/resultshandling/printer/summeryhelpers.go b/resultshandling/printer/summeryhelpers.go index a2053278..dee39235 100644 --- a/resultshandling/printer/summeryhelpers.go +++ b/resultshandling/printer/summeryhelpers.go @@ -1,7 +1,9 @@ package printer import ( + "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/k8s-interface/workloadinterface" + "github.com/armosec/opa-utils/objectsenvelopes" "github.com/armosec/opa-utils/reporthandling" ) @@ -9,19 +11,40 @@ import ( func groupByNamespaceOrKind(resources []WorkloadSummary, status func(workloadSummary *WorkloadSummary) bool) map[string][]WorkloadSummary { mapResources := make(map[string][]WorkloadSummary) for i := range resources { - if status(&resources[i]) { - if isKindToBeGrouped(resources[i].resource.GetKind()) { - if r, ok := mapResources[resources[i].resource.GetKind()]; ok { - r = append(r, resources[i]) - mapResources[resources[i].resource.GetKind()] = r - } else { - mapResources[resources[i].resource.GetKind()] = []WorkloadSummary{resources[i]} - } - } else if r, ok := mapResources[resources[i].resource.GetNamespace()]; ok { + if !status(&resources[i]) { + continue + } + t := resources[i].resource.GetObjectType() + if t == objectsenvelopes.TypeRegoResponseVectorObject && !isKindToBeGrouped(resources[i].resource.GetKind()) { + t = workloadinterface.TypeWorkloadObject + } + switch t { // TODO - find a better way to defind the groups + case workloadinterface.TypeWorkloadObject: + ns := "" + if resources[i].resource.GetNamespace() != "" { + ns = "Namescape " + resources[i].resource.GetNamespace() + } + if r, ok := mapResources[ns]; ok { r = append(r, resources[i]) - mapResources[resources[i].resource.GetNamespace()] = r + mapResources[ns] = r } else { - mapResources[resources[i].resource.GetNamespace()] = []WorkloadSummary{resources[i]} + mapResources[ns] = []WorkloadSummary{resources[i]} + } + case objectsenvelopes.TypeRegoResponseVectorObject: + group := resources[i].resource.GetKind() + "s" + if r, ok := mapResources[group]; ok { + r = append(r, resources[i]) + mapResources[group] = r + } else { + mapResources[group] = []WorkloadSummary{resources[i]} + } + default: + group, _ := k8sinterface.SplitApiVersion(resources[i].resource.GetApiVersion()) + if r, ok := mapResources[group]; ok { + r = append(r, resources[i]) + mapResources[group] = r + } else { + mapResources[group] = []WorkloadSummary{resources[i]} } } } diff --git a/resultshandling/reporter/mockreporter.go b/resultshandling/reporter/mockreporter.go index 6c4dc6a1..22983fef 100644 --- a/resultshandling/reporter/mockreporter.go +++ b/resultshandling/reporter/mockreporter.go @@ -25,6 +25,6 @@ func (reportMock *ReportMock) SetClusterName(clusterName string) { } func (reportMock *ReportMock) DisplayReportURL() { - message := fmt.Sprintf("\nYou 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\n", getter.GetArmoAPIConnector().GetFrontendURL()) + message := fmt.Sprintf("\nYou 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/cli-signup \n", getter.GetArmoAPIConnector().GetFrontendURL()) cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n%s\n", message)) } diff --git a/resultshandling/reporter/reporteventreceiver.go b/resultshandling/reporter/reporteventreceiver.go index 05ef1fe9..fc01232d 100644 --- a/resultshandling/reporter/reporteventreceiver.go +++ b/resultshandling/reporter/reporteventreceiver.go @@ -61,6 +61,9 @@ func (report *ReportEventReceiver) prepareReport(postureReport *reporthandling.P report.initEventReceiverURL() host := hostToString(report.eventReceiverURL, postureReport.ReportID) + cautils.StartSpinner() + defer cautils.StopSpinner() + // send framework results if err := report.sendReport(host, postureReport); err != nil { return err @@ -116,14 +119,14 @@ func (report *ReportEventReceiver) sendReport(host string, postureReport *report } func (report *ReportEventReceiver) DisplayReportURL() { - message := "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: " + message := "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:" u := url.URL{} u.Scheme = "https" u.Host = getter.GetArmoAPIConnector().GetFrontendURL() if report.customerAdminEMail != "" { - cautils.InfoTextDisplay(os.Stdout, "\n\n"+message+u.String()+"\n\n") + cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n\n%s %s/risk/%s\n(Account: %s)\n\n", message, u.String(), report.clusterName, report.customerGUID)) return } u.Path = "account/sign-up" @@ -132,5 +135,5 @@ func (report *ReportEventReceiver) DisplayReportURL() { q.Add("customerGUID", report.customerGUID) u.RawQuery = q.Encode() - cautils.InfoTextDisplay(os.Stdout, "\n\n"+message+u.String()+"\n\n") + cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n\n%s %s\n\n", message, u.String())) }