From 3b8825e5d20b87af1f416ed9dc7a5dc3c5fa8789 Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Mon, 25 Oct 2021 17:41:04 +0300 Subject: [PATCH 1/9] scan multiple frameworks and controls --- cautils/scaninfo.go | 4 +- clihandler/cmd/control.go | 45 ++++++++++++++++++---- clihandler/cmd/framework.go | 49 +++++++++++++++++------- clihandler/cmd/version.go | 2 +- clihandler/initcli.go | 6 +-- main.go | 6 ++- resultshandling/printer/prettyprinter.go | 49 ++++++++++++------------ 7 files changed, 108 insertions(+), 53 deletions(-) diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index 305b400c..6c6c31fa 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -9,7 +9,7 @@ import ( type ScanInfo struct { Getters - PolicyIdentifier reporthandling.PolicyIdentifier + PolicyIdentifier []reporthandling.PolicyIdentifier UseExceptions string // Load exceptions configuration UseFrom string // Load framework from local file (instead of download). Use when running offline UseDefault bool // Load framework from cached file (instead of download). Use when running offline @@ -52,7 +52,7 @@ func (scanInfo *ScanInfo) setUseFrom() { return } if scanInfo.UseDefault { - scanInfo.UseFrom = getter.GetDefaultPath(scanInfo.PolicyIdentifier.Name + ".json") + scanInfo.UseFrom = getter.GetDefaultPath(scanInfo.PolicyIdentifier[0].Name + ".json") } } func (scanInfo *ScanInfo) setGetter() { diff --git a/clihandler/cmd/control.go b/clihandler/cmd/control.go index e89ba905..967906a1 100644 --- a/clihandler/cmd/control.go +++ b/clihandler/cmd/control.go @@ -13,22 +13,40 @@ import ( // controlCmd represents the control command var controlCmd = &cobra.Command{ - Use: "control /", + Use: "control /", Short: fmt.Sprintf("The control you wish to use for scan. It must be present in at least one of the folloiwng frameworks: %s", clihandler.ValidFrameworks), Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 && !(cmd.Flags().Lookup("use-from").Changed) { - return fmt.Errorf("requires at least one argument") + if len(args) > 0 { + controls := strings.Split(args[0], ",") + if len(controls) > 1 { + if controls[1] == "" { + return fmt.Errorf("usage: ,") + } + } } return nil }, RunE: func(cmd *cobra.Command, args []string) error { flagValidationControl() - scanInfo.PolicyIdentifier = reporthandling.PolicyIdentifier{} - if !(cmd.Flags().Lookup("use-from").Changed) { - scanInfo.PolicyIdentifier.Name = strings.ToLower(args[0]) + scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} + + if len(args) < 1 && !(cmd.Flags().Lookup("use-from").Changed) { + scanInfo.PolicyIdentifier = SetScanForGivenFrameworks(clihandler.SupportedFrameworks) + } else { + controls := strings.Split(args[0], ",") + scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} + newPolicy := reporthandling.PolicyIdentifier{} + newPolicy.Kind = reporthandling.KindControl + newPolicy.Name = controls[0] + scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy) + + if !(cmd.Flags().Lookup("use-from").Changed) { + if len(controls) > 1 { + scanInfo.PolicyIdentifier = SetScanForGivenControls(controls[1:]) + } + } } scanInfo.FrameworkScan = false - scanInfo.PolicyIdentifier.Kind = reporthandling.KindControl scanInfo.Init() cautils.SetSilentMode(scanInfo.Silent) err := clihandler.CliSetup(&scanInfo) @@ -51,3 +69,16 @@ func flagValidationControl() { os.Exit(1) } } + +func SetScanForGivenControls(controls []string) []reporthandling.PolicyIdentifier { + for _, control := range controls { + if control[0] == ' ' { + control = control[1:] + } + newPolicy := reporthandling.PolicyIdentifier{} + newPolicy.Kind = reporthandling.KindControl + newPolicy.Name = control + scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy) + } + return scanInfo.PolicyIdentifier +} diff --git a/clihandler/cmd/framework.go b/clihandler/cmd/framework.go index 9461dce5..2bbd1d0e 100644 --- a/clihandler/cmd/framework.go +++ b/clihandler/cmd/framework.go @@ -9,32 +9,46 @@ import ( "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/clihandler" "github.com/armosec/opa-utils/reporthandling" - "github.com/spf13/cobra" ) var frameworkCmd = &cobra.Command{ - - Use: fmt.Sprintf("framework [``/`-`] [flags]\nSupported frameworks: %s", clihandler.ValidFrameworks), + Use: fmt.Sprintf("framework [``/`-`] [flags]\nSupported frameworks: %s", clihandler.ValidFrameworks), Short: fmt.Sprintf("The framework you wish to use. Supported frameworks: %s", strings.Join(clihandler.SupportedFrameworks, ", ")), Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin", ValidArgs: clihandler.SupportedFrameworks, Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 && !(cmd.Flags().Lookup("use-from").Changed) { - return fmt.Errorf("requires at least one argument") - } else if len(args) > 0 { - if !isValidFramework(strings.ToLower(args[0])) { - return fmt.Errorf(fmt.Sprintf("supported frameworks: %s", strings.Join(clihandler.SupportedFrameworks, ", "))) + if len(args) > 0 { + // "nsa, mitre" -> ["nsa", "mitre"] and nsa,mitre -> ["nsa", "mitre"] + frameworks := strings.Split(strings.Join(strings.Fields(args[0]), ""), ",") + for _, framework := range frameworks { + if !isValidFramework(strings.ToLower(framework)) { + return fmt.Errorf(fmt.Sprintf("supported frameworks: %s", strings.Join(clihandler.SupportedFrameworks, ", "))) + } } } return nil }, RunE: func(cmd *cobra.Command, args []string) error { - scanInfo.PolicyIdentifier = reporthandling.PolicyIdentifier{} - scanInfo.PolicyIdentifier.Kind = reporthandling.KindFramework flagValidationFramework() - if !(cmd.Flags().Lookup("use-from").Changed) { - scanInfo.PolicyIdentifier.Name = strings.ToLower(args[0]) + scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} + // If no framework provided, use all + if len(args) < 1 && !(cmd.Flags().Lookup("use-from").Changed) { + scanInfo.PolicyIdentifier = SetScanForGivenFrameworks(clihandler.SupportedFrameworks) + } else { + // Read frameworks from input args + frameworks := strings.Split(strings.Join(strings.Fields(args[0]), ""), ",") + scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} + newPolicy := reporthandling.PolicyIdentifier{} + newPolicy.Kind = reporthandling.KindFramework + newPolicy.Name = frameworks[0] + scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy) + + if !(cmd.Flags().Lookup("use-from").Changed) && !(cmd.Flags().Lookup("use-default").Changed) { + if len(frameworks) > 1 { + scanInfo.PolicyIdentifier = SetScanForGivenFrameworks(frameworks[1:]) + } + } } if len(args) > 0 { if len(args[1:]) == 0 || args[1] != "-" { @@ -77,8 +91,17 @@ func init() { } -func flagValidationFramework() { +func SetScanForGivenFrameworks(frameworks []string) []reporthandling.PolicyIdentifier { + for _, framework := range frameworks { + newPolicy := reporthandling.PolicyIdentifier{} + newPolicy.Kind = reporthandling.KindFramework + newPolicy.Name = framework + scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy) + } + return scanInfo.PolicyIdentifier +} +func flagValidationFramework() { if scanInfo.Submit && scanInfo.Local { fmt.Println("You can use `keep-local` or `submit`, but not both") os.Exit(1) diff --git a/clihandler/cmd/version.go b/clihandler/cmd/version.go index 45ba5bc2..f4623813 100644 --- a/clihandler/cmd/version.go +++ b/clihandler/cmd/version.go @@ -29,7 +29,7 @@ func GetLatestVersion() (string, error) { } defer resp.Body.Close() if resp.StatusCode < 200 || 301 < resp.StatusCode { - return "unknown", fmt.Errorf("failed to download file, status code: %s", resp.Status) + return "unknown", nil } body, err := io.ReadAll(resp.Body) diff --git a/clihandler/initcli.go b/clihandler/initcli.go index 1fdaae44..a502cc74 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -135,10 +135,8 @@ func (clihandler *CLIHandler) Scan() error { cautils.ScanStartDisplay() policyNotification := &reporthandling.PolicyNotification{ NotificationType: reporthandling.TypeExecPostureScan, - Rules: []reporthandling.PolicyIdentifier{ - clihandler.scanInfo.PolicyIdentifier, - }, - Designators: armotypes.PortalDesignator{}, + Rules: clihandler.scanInfo.PolicyIdentifier, + Designators: armotypes.PortalDesignator{}, } switch policyNotification.NotificationType { case reporthandling.TypeExecPostureScan: diff --git a/main.go b/main.go index 956b6622..dd5e7bff 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,11 @@ func CheckLatestVersion() { if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) } else if latest != cmd.BuildNumber { - fmt.Println("Warning: You are not updated to the latest release: " + latest) + if latest == "unknown" { + fmt.Println("Warning: You are not updated to the latest release!") + } else { + fmt.Println("Warning: You are not updated to the latest release: " + latest) + } } } diff --git a/resultshandling/printer/prettyprinter.go b/resultshandling/printer/prettyprinter.go index 1d4b366b..2d2c1361 100644 --- a/resultshandling/printer/prettyprinter.go +++ b/resultshandling/printer/prettyprinter.go @@ -26,10 +26,11 @@ func NewPrettyPrinter() *PrettyPrinter { func (printer *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { // score := calculatePostureScore(opaSessionObj.PostureReport) - - printer.summarySetup(opaSessionObj.PostureReport) - printer.printResults() - printer.printSummaryTable() + for _, report := range opaSessionObj.PostureReport.FrameworkReports { + printer.summarySetup(report) + printer.printResults() + printer.printSummaryTable() + } // return score } @@ -41,29 +42,27 @@ func (printer *PrettyPrinter) SetWriter(outputFile string) { func (printer *PrettyPrinter) Score(score float32) { } -func (printer *PrettyPrinter) summarySetup(postureReport *reporthandling.PostureReport) { - for _, fr := range postureReport.FrameworkReports { - printer.frameworkSummary = ControlSummary{ - TotalResources: fr.GetNumberOfResources(), - TotalFailed: fr.GetNumberOfFailedResources(), - TotalWarnign: fr.GetNumberOfWarningResources(), +func (printer *PrettyPrinter) summarySetup(fr reporthandling.FrameworkReport) { + printer.frameworkSummary = ControlSummary{ + TotalResources: fr.GetNumberOfResources(), + TotalFailed: fr.GetNumberOfFailedResources(), + TotalWarnign: fr.GetNumberOfWarningResources(), + } + for _, cr := range fr.ControlReports { + if len(cr.RuleReports) == 0 { + continue } - for _, cr := range fr.ControlReports { - if len(cr.RuleReports) == 0 { - continue - } - workloadsSummary := listResultSummary(cr.RuleReports) + 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.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() From a0ca68cc41f1c7cdb8bdd63e79e965bfca99b729 Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Tue, 26 Oct 2021 13:55:12 +0300 Subject: [PATCH 2/9] update json and junit for multiple frameworks --- clihandler/cmd/framework.go | 2 -- clihandler/cmd/scan.go | 15 ++++++++++----- resultshandling/printer/jsonprinter.go | 10 +++++++++- resultshandling/printer/prettyprinter.go | 20 ++++++++++++++++++-- 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/clihandler/cmd/framework.go b/clihandler/cmd/framework.go index 2bbd1d0e..4110f028 100644 --- a/clihandler/cmd/framework.go +++ b/clihandler/cmd/framework.go @@ -43,7 +43,6 @@ var frameworkCmd = &cobra.Command{ newPolicy.Kind = reporthandling.KindFramework newPolicy.Name = frameworks[0] scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy) - if !(cmd.Flags().Lookup("use-from").Changed) && !(cmd.Flags().Lookup("use-default").Changed) { if len(frameworks) > 1 { scanInfo.PolicyIdentifier = SetScanForGivenFrameworks(frameworks[1:]) @@ -88,7 +87,6 @@ func init() { frameworkCmd.Flags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Send the scan results to Armo management portal where 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 default the results are not submitted") frameworkCmd.Flags().BoolVarP(&scanInfo.Local, "keep-local", "", false, "If you do not want your Kubescape results reported to Armo backend. Use this flag if you ran with the '--submit' flag in the past and you do not want to submit your current scan results") frameworkCmd.Flags().StringVarP(&scanInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file") - } func SetScanForGivenFrameworks(frameworks []string) []reporthandling.PolicyIdentifier { diff --git a/clihandler/cmd/scan.go b/clihandler/cmd/scan.go index e86e5a02..9b0e46bb 100644 --- a/clihandler/cmd/scan.go +++ b/clihandler/cmd/scan.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/armosec/kubescape/cautils" + "github.com/armosec/kubescape/clihandler" "github.com/spf13/cobra" ) @@ -16,15 +17,19 @@ var scanCmd = &cobra.Command{ Short: "Scan the current running cluster or yaml files", Long: `The action you want to perform`, Args: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return fmt.Errorf("requires one argument: framework/control") - } - if !strings.EqualFold(args[0], "framework") && !strings.EqualFold(args[0], "control") { - return fmt.Errorf("invalid parameter '%s'. Supported parameters: framework, control", args[0]) + if len(args) > 0 { + if !strings.EqualFold(args[0], "framework") && !strings.EqualFold(args[0], "control") { + return fmt.Errorf("invalid parameter '%s'. Supported parameters: framework, control", args[0]) + } } return nil }, Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + frameworkArgs := []string{clihandler.ValidFrameworks} + frameworkArgs = append(frameworkArgs, args...) + frameworkCmd.RunE(cmd, frameworkArgs) + } }, } diff --git a/resultshandling/printer/jsonprinter.go b/resultshandling/printer/jsonprinter.go index ff80feb8..01acad7e 100644 --- a/resultshandling/printer/jsonprinter.go +++ b/resultshandling/printer/jsonprinter.go @@ -25,7 +25,15 @@ func (jsonPrinter *JsonPrinter) Score(score float32) { } func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { - postureReportStr, err := json.Marshal(opaSessionObj.PostureReport.FrameworkReports[0]) + var postureReportStr []byte + var err error + + if len(opaSessionObj.PostureReport.FrameworkReports) == 1 { + postureReportStr, err = json.Marshal(opaSessionObj.PostureReport.FrameworkReports[0]) + } else { + postureReportStr, err = json.Marshal(opaSessionObj.PostureReport.FrameworkReports) + } + if err != nil { fmt.Println("Failed to convert posture report object!") os.Exit(1) diff --git a/resultshandling/printer/prettyprinter.go b/resultshandling/printer/prettyprinter.go index 2d2c1361..0752fa00 100644 --- a/resultshandling/printer/prettyprinter.go +++ b/resultshandling/printer/prettyprinter.go @@ -24,12 +24,24 @@ func NewPrettyPrinter() *PrettyPrinter { } } +// Initializes empty printer for new table +func (printer *PrettyPrinter) init() *PrettyPrinter { + printer.frameworkSummary = ControlSummary{} + printer.summary = Summary{} + printer.sortedControlNames = []string{} + return printer +} + func (printer *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { // score := calculatePostureScore(opaSessionObj.PostureReport) for _, report := range opaSessionObj.PostureReport.FrameworkReports { + // Print summary table together for control scan + if report.Name != "" { + printer = printer.init() + } printer.summarySetup(report) printer.printResults() - printer.printSummaryTable() + printer.printSummaryTable(report.Name) } // return score @@ -178,7 +190,11 @@ func generateFooter(numControlers, sumFailed, sumWarning, sumTotal int) []string } return row } -func (printer *PrettyPrinter) printSummaryTable() { +func (printer *PrettyPrinter) printSummaryTable(framework string) { + // For control scan framework will be nil + if framework != "" { + cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("%s FRAMEWORK\n", framework)) + } summaryTable := tablewriter.NewWriter(printer.writer) summaryTable.SetAutoWrapText(false) summaryTable.SetHeader(generateHeader()) From 8a7cda5dd1111cdece4b0252642c1839a28b9e77 Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 26 Oct 2021 20:27:33 +0300 Subject: [PATCH 3/9] adopt cluster name --- cautils/customerloader.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cautils/customerloader.go b/cautils/customerloader.go index 44b79930..c77347f0 100644 --- a/cautils/customerloader.go +++ b/cautils/customerloader.go @@ -243,7 +243,7 @@ func (c *ClusterConfig) setCustomerGUID(customerGUID string) { } func (c *ClusterConfig) setClusterName(clusterName string) { - c.configObj.ClusterName = clusterName + c.configObj.ClusterName = adoptClusterName(clusterName) } func (c *ClusterConfig) GetClusterName() string { return c.configObj.ClusterName @@ -472,3 +472,7 @@ func DeleteConfigMap(k8s *k8sinterface.KubernetesApi) error { func DeleteConfigFile() error { return os.Remove(ConfigFileFullPath()) } + +func adoptClusterName(clusterName string) string { + return strings.ReplaceAll(clusterName, "/", "-") +} From 66068757e1b5a78b3fa79b2e62848a3953312285 Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 26 Oct 2021 20:39:18 +0300 Subject: [PATCH 4/9] update cluster name in mock struct --- cautils/customerloader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cautils/customerloader.go b/cautils/customerloader.go index c77347f0..dce4a9db 100644 --- a/cautils/customerloader.go +++ b/cautils/customerloader.go @@ -129,7 +129,7 @@ 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 k8sinterface.GetClusterName() } +func (c *EmptyConfig) GetClusterName() string { return adoptClusterName(k8sinterface.GetClusterName()) } func (c *EmptyConfig) GenerateURL() { message := fmt.Sprintf("\nCheckout for more cool features: https://%s\n", getter.GetArmoAPIConnector().GetFrontendURL()) InfoTextDisplay(os.Stdout, fmt.Sprintf("\n%s\n", message)) From 151175c40f2e2189f797537131ea1eaf030663d4 Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Wed, 27 Oct 2021 08:49:40 +0300 Subject: [PATCH 5/9] read single control from framework file --- cautils/getter/loadpolicy.go | 13 +++++++++++-- clihandler/cmd/control.go | 15 +++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/cautils/getter/loadpolicy.go b/cautils/getter/loadpolicy.go index 223ee7ec..87d01fa5 100644 --- a/cautils/getter/loadpolicy.go +++ b/cautils/getter/loadpolicy.go @@ -38,9 +38,18 @@ func (lp *LoadPolicy) GetControl(controlName string) (*reporthandling.Control, e if err = json.Unmarshal(f, control); err != nil { return control, err } - if controlName != "" && !strings.EqualFold(controlName, control.Name) && !strings.EqualFold(controlName, control.ControlID) { - return nil, fmt.Errorf("control from file not matching") + framework, err := lp.GetFramework(control.PortalBase.Name) + if err != nil { + return nil, fmt.Errorf("control from file not matching") + } else { + for _, ctrl := range framework.Controls { + if strings.EqualFold(ctrl.Name, controlName) || strings.EqualFold(ctrl.ControlID, controlName) { + control = &ctrl + break + } + } + } } return control, err } diff --git a/clihandler/cmd/control.go b/clihandler/cmd/control.go index 967906a1..585f481d 100644 --- a/clihandler/cmd/control.go +++ b/clihandler/cmd/control.go @@ -33,12 +33,15 @@ var controlCmd = &cobra.Command{ if len(args) < 1 && !(cmd.Flags().Lookup("use-from").Changed) { scanInfo.PolicyIdentifier = SetScanForGivenFrameworks(clihandler.SupportedFrameworks) } else { - controls := strings.Split(args[0], ",") - scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} - newPolicy := reporthandling.PolicyIdentifier{} - newPolicy.Kind = reporthandling.KindControl - newPolicy.Name = controls[0] - scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy) + var controls []string + if len(args) > 0 { + controls = strings.Split(args[0], ",") + scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} + newPolicy := reporthandling.PolicyIdentifier{} + newPolicy.Kind = reporthandling.KindControl + newPolicy.Name = controls[0] + scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy) + } if !(cmd.Flags().Lookup("use-from").Changed) { if len(controls) > 1 { From 3301907864d2c9491a54ef6bba76d3babf6f9882 Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Wed, 27 Oct 2021 10:25:26 +0300 Subject: [PATCH 6/9] print only one table for controls & enhance help msg --- clihandler/cmd/control.go | 6 +-- clihandler/cmd/framework.go | 2 +- policyhandler/handlepullpolicies.go | 63 ++++++++++++++---------- resultshandling/printer/prettyprinter.go | 11 +++-- 4 files changed, 49 insertions(+), 33 deletions(-) diff --git a/clihandler/cmd/control.go b/clihandler/cmd/control.go index 585f481d..f5911b1d 100644 --- a/clihandler/cmd/control.go +++ b/clihandler/cmd/control.go @@ -13,7 +13,7 @@ import ( // controlCmd represents the control command var controlCmd = &cobra.Command{ - Use: "control /", + Use: "control /.\nExamples:\nscan control C-0058,C-0057 [flags]\nscan contol C-0058 [flags]\nscan control 'privileged container,allowed hostpath' [flags]", Short: fmt.Sprintf("The control you wish to use for scan. It must be present in at least one of the folloiwng frameworks: %s", clihandler.ValidFrameworks), Args: func(cmd *cobra.Command, args []string) error { if len(args) > 0 { @@ -75,9 +75,7 @@ func flagValidationControl() { func SetScanForGivenControls(controls []string) []reporthandling.PolicyIdentifier { for _, control := range controls { - if control[0] == ' ' { - control = control[1:] - } + control := strings.TrimLeft(control, " ") newPolicy := reporthandling.PolicyIdentifier{} newPolicy.Kind = reporthandling.KindControl newPolicy.Name = control diff --git a/clihandler/cmd/framework.go b/clihandler/cmd/framework.go index 4110f028..0b7d0dbe 100644 --- a/clihandler/cmd/framework.go +++ b/clihandler/cmd/framework.go @@ -13,7 +13,7 @@ import ( ) var frameworkCmd = &cobra.Command{ - Use: fmt.Sprintf("framework [``/`-`] [flags]\nSupported frameworks: %s", clihandler.ValidFrameworks), + Use: fmt.Sprintf("framework [``/`-`] [flags]\nExamples:\nscan framework nsa [flags]\nscanframework mitre,nsa [flags]\nSupported frameworks: %s", clihandler.ValidFrameworks), Short: fmt.Sprintf("The framework you wish to use. Supported frameworks: %s", strings.Join(clihandler.SupportedFrameworks, ", ")), Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin", ValidArgs: clihandler.SupportedFrameworks, diff --git a/policyhandler/handlepullpolicies.go b/policyhandler/handlepullpolicies.go index 0762a05e..579c05db 100644 --- a/policyhandler/handlepullpolicies.go +++ b/policyhandler/handlepullpolicies.go @@ -14,9 +14,11 @@ func (policyHandler *PolicyHandler) GetPoliciesFromBackend(notification *reporth frameworks := []reporthandling.Framework{} exceptionPolicies := []armotypes.PostureExceptionPolicy{} // Get - cacli opa get - for _, rule := range notification.Rules { - switch rule.Kind { - case reporthandling.KindFramework: + rule := GetScanKind(notification) + + switch rule.Kind { + case reporthandling.KindFramework: + for _, rule := range notification.Rules { receivedFramework, recExceptionPolicies, err := policyHandler.getFrameworkPolicies(rule.Name) if receivedFramework != nil { frameworks = append(frameworks, *receivedFramework) @@ -29,27 +31,32 @@ func (policyHandler *PolicyHandler) GetPoliciesFromBackend(notification *reporth } return nil, nil, fmt.Errorf("kind: %v, name: %s, error: %s", rule.Kind, rule.Name, err.Error()) } - case reporthandling.KindControl: - receivedControls, recExceptionPolicies, err := policyHandler.getControl(rule.Name) - if receivedControls != nil { - f := reporthandling.Framework{ - Controls: receivedControls, - } - frameworks = append(frameworks, f) + } + case reporthandling.KindControl: + f := reporthandling.Framework{} + var receivedControl *reporthandling.Control + var recExceptionPolicies []armotypes.PostureExceptionPolicy + var err error + for _, rule := range notification.Rules { + receivedControl, recExceptionPolicies, err = policyHandler.getControl(rule.Name) + if receivedControl != nil { + f.Controls = append(f.Controls, *receivedControl) if recExceptionPolicies != nil { exceptionPolicies = append(exceptionPolicies, recExceptionPolicies...) } + } else if err != nil { if strings.Contains(err.Error(), "unsupported protocol scheme") { err = fmt.Errorf("failed to download from GitHub release, try running with `--use-default` flag") } return nil, nil, fmt.Errorf("error: %s", err.Error()) } - // TODO: add case for control from file - default: - err := fmt.Errorf("missing rule kind, expected: %s", reporthandling.KindFramework) - errs = fmt.Errorf("%s", err.Error()) } + frameworks = append(frameworks, f) + // TODO: add case for control from file + default: + err := fmt.Errorf("missing rule kind, expected: %s", reporthandling.KindFramework) + errs = fmt.Errorf("%s", err.Error()) } return frameworks, exceptionPolicies, errs } @@ -68,24 +75,30 @@ func (policyHandler *PolicyHandler) getFrameworkPolicies(policyName string) (*re return receivedFramework, receivedException, nil } +func GetScanKind(notification *reporthandling.PolicyNotification) *reporthandling.PolicyIdentifier { + if len(notification.Rules) > 0 { + return ¬ification.Rules[0] + } + return nil +} + // Get control by name -func (policyHandler *PolicyHandler) getControl(policyName string) ([]reporthandling.Control, []armotypes.PostureExceptionPolicy, error) { +func (policyHandler *PolicyHandler) getControl(policyName string) (*reporthandling.Control, []armotypes.PostureExceptionPolicy, error) { - controls := []reporthandling.Control{} - - control, err := policyHandler.getters.PolicyGetter.GetControl(policyName) + control := &reporthandling.Control{} + var err error + control, err = policyHandler.getters.PolicyGetter.GetControl(policyName) if err != nil { - return nil, nil, err + return control, nil, err } - if control == nil { - return nil, nil, fmt.Errorf("control not found") - } - controls = append(controls, *control) + // if control == nil { + // return control, nil, fmt.Errorf("control not found") + // } exceptions, err := policyHandler.getters.ExceptionsGetter.GetExceptions(cautils.CustomerGUID, cautils.ClusterName) if err != nil { - return controls, nil, err + return control, nil, err } - return controls, exceptions, nil + return control, exceptions, nil } diff --git a/resultshandling/printer/prettyprinter.go b/resultshandling/printer/prettyprinter.go index 0752fa00..225b6844 100644 --- a/resultshandling/printer/prettyprinter.go +++ b/resultshandling/printer/prettyprinter.go @@ -192,9 +192,8 @@ func generateFooter(numControlers, sumFailed, sumWarning, sumTotal int) []string } func (printer *PrettyPrinter) printSummaryTable(framework string) { // For control scan framework will be nil - if framework != "" { - cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("%s FRAMEWORK\n", framework)) - } + printer.printFramework(framework) + summaryTable := tablewriter.NewWriter(printer.writer) summaryTable.SetAutoWrapText(false) summaryTable.SetHeader(generateHeader()) @@ -210,6 +209,12 @@ func (printer *PrettyPrinter) printSummaryTable(framework string) { summaryTable.Render() } +func (printer *PrettyPrinter) printFramework(framework string) { + if framework != "" { + cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("%s FRAMEWORK\n", framework)) + } +} + func (printer *PrettyPrinter) getSortedControlsNames() []string { controlNames := make([]string, 0, len(printer.summary)) for k := range printer.summary { From 5095e62961b49c4e7f8133d462f6eb3ed675b27c Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Wed, 27 Oct 2021 13:02:45 +0300 Subject: [PATCH 7/9] support scanning multiple frameworks from multiple files --- cautils/getter/loadpolicy.go | 42 ++++++++++++++++++++++++------------ cautils/scaninfo.go | 13 ++++++----- clihandler/cmd/framework.go | 22 ++++++++++--------- clihandler/cmd/scan.go | 2 +- 4 files changed, 47 insertions(+), 32 deletions(-) diff --git a/cautils/getter/loadpolicy.go b/cautils/getter/loadpolicy.go index 87d01fa5..cd667b5b 100644 --- a/cautils/getter/loadpolicy.go +++ b/cautils/getter/loadpolicy.go @@ -17,12 +17,12 @@ const DefaultLocalStore = ".kubescape" // Load policies from a local repository type LoadPolicy struct { - filePath string + filePaths []string } -func NewLoadPolicy(filePath string) *LoadPolicy { +func NewLoadPolicy(filePaths []string) *LoadPolicy { return &LoadPolicy{ - filePath: filePath, + filePaths: filePaths, } } @@ -30,7 +30,8 @@ func NewLoadPolicy(filePath string) *LoadPolicy { func (lp *LoadPolicy) GetControl(controlName string) (*reporthandling.Control, error) { control := &reporthandling.Control{} - f, err := os.ReadFile(lp.filePath) + filePath := lp.getFileForControl() + f, err := os.ReadFile(filePath) if err != nil { return nil, err } @@ -55,27 +56,32 @@ func (lp *LoadPolicy) GetControl(controlName string) (*reporthandling.Control, e } func (lp *LoadPolicy) GetFramework(frameworkName string) (*reporthandling.Framework, error) { - framework := &reporthandling.Framework{} - f, err := os.ReadFile(lp.filePath) - if err != nil { - return nil, err - } + var err error + for _, filePath := range lp.filePaths { + f, err := os.ReadFile(filePath) + if err != nil { + return nil, err + } - if err = json.Unmarshal(f, framework); err != nil { - return framework, err + if err = json.Unmarshal(f, framework); err != nil { + return framework, err + } + if strings.EqualFold(frameworkName, framework.Name) { + break + } } - if frameworkName != "" && !strings.EqualFold(frameworkName, framework.Name) { + return nil, fmt.Errorf("framework from file not matching") } return framework, err } func (lp *LoadPolicy) GetExceptions(customerGUID, clusterName string) ([]armotypes.PostureExceptionPolicy, error) { - + filePath := lp.getFileForException() exception := []armotypes.PostureExceptionPolicy{} - f, err := os.ReadFile(lp.filePath) + f, err := os.ReadFile(filePath) if err != nil { return nil, err } @@ -83,3 +89,11 @@ func (lp *LoadPolicy) GetExceptions(customerGUID, clusterName string) ([]armotyp err = json.Unmarshal(f, &exception) return exception, err } + +func (lp *LoadPolicy) getFileForException() string { + return lp.filePaths[0] +} + +func (lp *LoadPolicy) getFileForControl() string { + return lp.filePaths[0] +} diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index 6c6c31fa..ca0b064d 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -11,7 +11,7 @@ type ScanInfo struct { Getters PolicyIdentifier []reporthandling.PolicyIdentifier UseExceptions string // Load exceptions configuration - UseFrom string // Load framework from local file (instead of download). Use when running offline + UseFrom []string // Load framework from local file (instead of download). Use when running offline UseDefault bool // Load framework from cached file (instead of download). Use when running offline Format string // Format results (table, json, junit ...) Output string // Store results in an output file, Output file name @@ -41,22 +41,21 @@ func (scanInfo *ScanInfo) Init() { func (scanInfo *ScanInfo) setUseExceptions() { if scanInfo.UseExceptions != "" { // load exceptions from file - scanInfo.ExceptionsGetter = getter.NewLoadPolicy(scanInfo.UseExceptions) + scanInfo.ExceptionsGetter = getter.NewLoadPolicy([]string{scanInfo.UseExceptions}) } else { scanInfo.ExceptionsGetter = getter.GetArmoAPIConnector() } } func (scanInfo *ScanInfo) setUseFrom() { - if scanInfo.UseFrom != "" { - return - } if scanInfo.UseDefault { - scanInfo.UseFrom = getter.GetDefaultPath(scanInfo.PolicyIdentifier[0].Name + ".json") + for _, policy := range scanInfo.PolicyIdentifier { + scanInfo.UseFrom = append(scanInfo.UseFrom, getter.GetDefaultPath(policy.Name+".json")) + } } } func (scanInfo *ScanInfo) setGetter() { - if scanInfo.UseFrom != "" { + if len(scanInfo.UseFrom) > 0 { // load from file scanInfo.PolicyGetter = getter.NewLoadPolicy(scanInfo.UseFrom) } else { diff --git a/clihandler/cmd/framework.go b/clihandler/cmd/framework.go index 0b7d0dbe..bac9d768 100644 --- a/clihandler/cmd/framework.go +++ b/clihandler/cmd/framework.go @@ -37,16 +37,11 @@ var frameworkCmd = &cobra.Command{ scanInfo.PolicyIdentifier = SetScanForGivenFrameworks(clihandler.SupportedFrameworks) } else { // Read frameworks from input args - frameworks := strings.Split(strings.Join(strings.Fields(args[0]), ""), ",") scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} - newPolicy := reporthandling.PolicyIdentifier{} - newPolicy.Kind = reporthandling.KindFramework - newPolicy.Name = frameworks[0] - scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy) - if !(cmd.Flags().Lookup("use-from").Changed) && !(cmd.Flags().Lookup("use-default").Changed) { - if len(frameworks) > 1 { - scanInfo.PolicyIdentifier = SetScanForGivenFrameworks(frameworks[1:]) - } + frameworks := strings.Split(strings.Join(strings.Fields(args[0]), ""), ",") + scanInfo.PolicyIdentifier = SetScanForFirstFramework(frameworks) + if len(frameworks) > 1 { + scanInfo.PolicyIdentifier = SetScanForGivenFrameworks(frameworks[1:]) } } if len(args) > 0 { @@ -88,7 +83,6 @@ func init() { frameworkCmd.Flags().BoolVarP(&scanInfo.Local, "keep-local", "", false, "If you do not want your Kubescape results reported to Armo backend. Use this flag if you ran with the '--submit' flag in the past and you do not want to submit your current scan results") frameworkCmd.Flags().StringVarP(&scanInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file") } - func SetScanForGivenFrameworks(frameworks []string) []reporthandling.PolicyIdentifier { for _, framework := range frameworks { newPolicy := reporthandling.PolicyIdentifier{} @@ -99,6 +93,14 @@ func SetScanForGivenFrameworks(frameworks []string) []reporthandling.PolicyIdent return scanInfo.PolicyIdentifier } +func SetScanForFirstFramework(frameworks []string) []reporthandling.PolicyIdentifier { + newPolicy := reporthandling.PolicyIdentifier{} + newPolicy.Kind = reporthandling.KindFramework + newPolicy.Name = frameworks[0] + scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy) + return scanInfo.PolicyIdentifier +} + func flagValidationFramework() { if scanInfo.Submit && scanInfo.Local { fmt.Println("You can use `keep-local` or `submit`, but not both") diff --git a/clihandler/cmd/scan.go b/clihandler/cmd/scan.go index 9b0e46bb..3b16fd0d 100644 --- a/clihandler/cmd/scan.go +++ b/clihandler/cmd/scan.go @@ -40,7 +40,7 @@ func init() { scanCmd.PersistentFlags().StringVarP(&scanInfo.Output, "output", "o", "", "Output file. Print output to file and not stdout") scanCmd.PersistentFlags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages") scanCmd.PersistentFlags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 0, "Failure threshold is the percent bellow which the command fails and returns exit code 1") - scanCmd.PersistentFlags().StringVar(&scanInfo.UseFrom, "use-from", "", "Load local framework object from specified path. If not used will download latest") + scanCmd.PersistentFlags().StringSliceVar(&scanInfo.UseFrom, "use-from", nil, "Load local framework object from specified path. If not used will download latest") scanCmd.PersistentFlags().BoolVar(&scanInfo.UseDefault, "use-default", false, "Load local framework object from default path. If not used will download latest") scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from Armo management portal") } From 9439f407da7e7ae449efdd3e7eb79a233d9bd522 Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Wed, 27 Oct 2021 13:39:03 +0300 Subject: [PATCH 8/9] add env var to not check latest release --- main.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index dd5e7bff..84329f92 100644 --- a/main.go +++ b/main.go @@ -5,23 +5,25 @@ import ( "os" "github.com/armosec/kubescape/clihandler/cmd" + pkgutils "github.com/armosec/utils-go/utils" ) +const SKIP_VERSION_CHECK = "KUBESCAPE_SKIP_UPDATE_CHECK" + func main() { CheckLatestVersion() cmd.Execute() } func CheckLatestVersion() { + if v, ok := os.LookupEnv(SKIP_VERSION_CHECK); ok && pkgutils.StringToBool(v) { + return + } latest, err := cmd.GetLatestVersion() - if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - } else if latest != cmd.BuildNumber { - if latest == "unknown" { - fmt.Println("Warning: You are not updated to the latest release!") - } else { - fmt.Println("Warning: You are not updated to the latest release: " + latest) - } + if err != nil || latest == "unknown" { + return + } + if latest != cmd.BuildNumber { + fmt.Println("Warning: You are not updated to the latest release: " + latest) } - } From de8bcfa0d2444e463de05eeb565ac92bfc93f6dd Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Wed, 27 Oct 2021 14:44:25 +0300 Subject: [PATCH 9/9] enhance help msgs --- cautils/getter/loadpolicy.go | 2 +- clihandler/cmd/control.go | 25 +++++++++++++++---------- clihandler/cmd/framework.go | 6 ++++-- clihandler/cmd/scan.go | 4 ++-- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/cautils/getter/loadpolicy.go b/cautils/getter/loadpolicy.go index cd667b5b..e71451d5 100644 --- a/cautils/getter/loadpolicy.go +++ b/cautils/getter/loadpolicy.go @@ -40,7 +40,7 @@ func (lp *LoadPolicy) GetControl(controlName string) (*reporthandling.Control, e return control, err } if controlName != "" && !strings.EqualFold(controlName, control.Name) && !strings.EqualFold(controlName, control.ControlID) { - framework, err := lp.GetFramework(control.PortalBase.Name) + framework, err := lp.GetFramework(controlName) if err != nil { return nil, fmt.Errorf("control from file not matching") } else { diff --git a/clihandler/cmd/control.go b/clihandler/cmd/control.go index f5911b1d..18a18853 100644 --- a/clihandler/cmd/control.go +++ b/clihandler/cmd/control.go @@ -13,7 +13,7 @@ import ( // controlCmd represents the control command var controlCmd = &cobra.Command{ - Use: "control /.\nExamples:\nscan control C-0058,C-0057 [flags]\nscan contol C-0058 [flags]\nscan control 'privileged container,allowed hostpath' [flags]", + Use: "control /.\nExamples:\n$ kubescape scan control C-0058,C-0057 [flags]\n$ kubescape scan contol C-0058 [flags]\n$ kubescape scan control 'privileged container,allowed hostpath' [flags]", Short: fmt.Sprintf("The control you wish to use for scan. It must be present in at least one of the folloiwng frameworks: %s", clihandler.ValidFrameworks), Args: func(cmd *cobra.Command, args []string) error { if len(args) > 0 { @@ -23,6 +23,8 @@ var controlCmd = &cobra.Command{ return fmt.Errorf("usage: ,") } } + } else { + return fmt.Errorf("requires at least one control name") } return nil }, @@ -30,23 +32,18 @@ var controlCmd = &cobra.Command{ flagValidationControl() scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} - if len(args) < 1 && !(cmd.Flags().Lookup("use-from").Changed) { + if len(args) < 1 { scanInfo.PolicyIdentifier = SetScanForGivenFrameworks(clihandler.SupportedFrameworks) } else { var controls []string if len(args) > 0 { controls = strings.Split(args[0], ",") scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} - newPolicy := reporthandling.PolicyIdentifier{} - newPolicy.Kind = reporthandling.KindControl - newPolicy.Name = controls[0] - scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy) + scanInfo.PolicyIdentifier = setScanForFirstControl(controls) } - if !(cmd.Flags().Lookup("use-from").Changed) { - if len(controls) > 1 { - scanInfo.PolicyIdentifier = SetScanForGivenControls(controls[1:]) - } + if len(controls) > 1 { + scanInfo.PolicyIdentifier = SetScanForGivenControls(controls[1:]) } } scanInfo.FrameworkScan = false @@ -73,6 +70,14 @@ func flagValidationControl() { } } +func setScanForFirstControl(controls []string) []reporthandling.PolicyIdentifier { + newPolicy := reporthandling.PolicyIdentifier{} + newPolicy.Kind = reporthandling.KindControl + newPolicy.Name = controls[0] + scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy) + return scanInfo.PolicyIdentifier +} + func SetScanForGivenControls(controls []string) []reporthandling.PolicyIdentifier { for _, control := range controls { control := strings.TrimLeft(control, " ") diff --git a/clihandler/cmd/framework.go b/clihandler/cmd/framework.go index bac9d768..9b75e224 100644 --- a/clihandler/cmd/framework.go +++ b/clihandler/cmd/framework.go @@ -13,7 +13,7 @@ import ( ) var frameworkCmd = &cobra.Command{ - Use: fmt.Sprintf("framework [``/`-`] [flags]\nExamples:\nscan framework nsa [flags]\nscanframework mitre,nsa [flags]\nSupported frameworks: %s", clihandler.ValidFrameworks), + Use: fmt.Sprintf("framework [``/`-`] [flags]\nExamples:\n$ kubescape scan framework nsa [flags]\n$ kubescape scan framework mitre,nsa [flags]\n$ kubescape scan framework 'nsa, mitre' [flags]\nSupported frameworks: %s", clihandler.ValidFrameworks), Short: fmt.Sprintf("The framework you wish to use. Supported frameworks: %s", strings.Join(clihandler.SupportedFrameworks, ", ")), Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin", ValidArgs: clihandler.SupportedFrameworks, @@ -26,6 +26,8 @@ var frameworkCmd = &cobra.Command{ return fmt.Errorf(fmt.Sprintf("supported frameworks: %s", strings.Join(clihandler.SupportedFrameworks, ", "))) } } + } else { + return fmt.Errorf("requires at least one framework name") } return nil }, @@ -33,7 +35,7 @@ var frameworkCmd = &cobra.Command{ flagValidationFramework() scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} // If no framework provided, use all - if len(args) < 1 && !(cmd.Flags().Lookup("use-from").Changed) { + if len(args) < 1 { scanInfo.PolicyIdentifier = SetScanForGivenFrameworks(clihandler.SupportedFrameworks) } else { // Read frameworks from input args diff --git a/clihandler/cmd/scan.go b/clihandler/cmd/scan.go index 3b16fd0d..f9c2e65b 100644 --- a/clihandler/cmd/scan.go +++ b/clihandler/cmd/scan.go @@ -40,7 +40,7 @@ func init() { scanCmd.PersistentFlags().StringVarP(&scanInfo.Output, "output", "o", "", "Output file. Print output to file and not stdout") scanCmd.PersistentFlags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages") scanCmd.PersistentFlags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 0, "Failure threshold is the percent bellow which the command fails and returns exit code 1") - scanCmd.PersistentFlags().StringSliceVar(&scanInfo.UseFrom, "use-from", nil, "Load local framework object from specified path. If not used will download latest") - scanCmd.PersistentFlags().BoolVar(&scanInfo.UseDefault, "use-default", false, "Load local framework object from default path. If not used will download latest") + scanCmd.PersistentFlags().StringSliceVar(&scanInfo.UseFrom, "use-from", nil, "Load local policy object from specified path. If not used will download latest") + scanCmd.PersistentFlags().BoolVar(&scanInfo.UseDefault, "use-default", false, "Load local policy object from default path. If not used will download latest") scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from Armo management portal") }