diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index 9a678aa9..02f667b2 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -1,8 +1,6 @@ package cautils import ( - "io" - "os" "path/filepath" "github.com/armosec/kubescape/cautils/getter" @@ -69,24 +67,6 @@ func (scanInfo *ScanInfo) setUseFrom() { } } -func (scanInfo *ScanInfo) SetInputPatterns(args []string) error { - if args[1] != "-" { - scanInfo.InputPatterns = args[1:] - } else { // store stout to file - tempFile, err := os.CreateTemp(".", "tmp-kubescape*.yaml") - if err != nil { - return err - } - defer os.Remove(tempFile.Name()) - - if _, err := io.Copy(tempFile, os.Stdin); err != nil { - return err - } - scanInfo.InputPatterns = []string{tempFile.Name()} - } - return nil -} - func (scanInfo *ScanInfo) setOutputFile() { if scanInfo.Output == "" { return @@ -107,20 +87,20 @@ func (scanInfo *ScanInfo) ScanRunningCluster() bool { return len(scanInfo.InputPatterns) == 0 } -func (scanInfo *ScanInfo) SetPolicyIdentifierForGivenFrameworks(frameworks []string) { - for _, framework := range frameworks { - if !scanInfo.contains(framework) { +func (scanInfo *ScanInfo) SetPolicyIdentifiers(policies []string, kind reporthandling.NotificationPolicyKind) { + for _, policy := range policies { + if !scanInfo.contains(policy) { newPolicy := reporthandling.PolicyIdentifier{} - newPolicy.Kind = reporthandling.KindFramework - newPolicy.Name = framework + newPolicy.Kind = kind // reporthandling.KindFramework + newPolicy.Name = policy scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy) } } } -func (scanInfo *ScanInfo) contains(framework string) bool { +func (scanInfo *ScanInfo) contains(policyName string) bool { for _, policy := range scanInfo.PolicyIdentifier { - if policy.Name == framework { + if policy.Name == policyName { return true } } diff --git a/clihandler/cmd/control.go b/clihandler/cmd/control.go index df120324..a0772767 100644 --- a/clihandler/cmd/control.go +++ b/clihandler/cmd/control.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "io" "os" "strings" @@ -21,7 +22,7 @@ var controlCmd = &cobra.Command{ controls := strings.Split(args[0], ",") if len(controls) > 1 { if controls[1] == "" { - return fmt.Errorf("usage: ,") + return fmt.Errorf("usage: ,") } } } else { @@ -34,23 +35,31 @@ var controlCmd = &cobra.Command{ scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} if len(args) == 0 { - scanInfo.SetPolicyIdentifierForGivenFrameworks(getter.NativeFrameworks) - } else { - controls := strings.Split(args[0], ",") - scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} - scanInfo.PolicyIdentifier = setScanForFirstControl(controls) + scanInfo.SetPolicyIdentifiers(getter.NativeFrameworks, reporthandling.KindFramework) + scanInfo.ScanAll = true + } else { // expected control or list of control sepparated by "," - if len(controls) > 1 { - scanInfo.PolicyIdentifier = SetScanForGivenControls(controls[1:]) - } + // Read controls from input args + scanInfo.SetPolicyIdentifiers(strings.Split(args[0], ","), reporthandling.KindControl) if len(args) > 1 { - // Set scan to run on yamls - if err := scanInfo.SetInputPatterns(args); err != nil { - return err + if len(args[1:]) == 0 || args[1] != "-" { + scanInfo.InputPatterns = args[1:] + } else { // store stdin to file - do NOT move to separate function !! + tempFile, err := os.CreateTemp(".", "tmp-kubescape*.yaml") + if err != nil { + return err + } + defer os.Remove(tempFile.Name()) + + if _, err := io.Copy(tempFile, os.Stdin); err != nil { + return err + } + scanInfo.InputPatterns = []string{tempFile.Name()} } } } + scanInfo.FrameworkScan = false scanInfo.Init() cautils.SetSilentMode(scanInfo.Silent) diff --git a/clihandler/cmd/framework.go b/clihandler/cmd/framework.go index 3d56cb36..e381ecff 100644 --- a/clihandler/cmd/framework.go +++ b/clihandler/cmd/framework.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "io" "os" "strings" @@ -18,34 +19,48 @@ var frameworkCmd = &cobra.Command{ Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin", ValidArgs: getter.NativeFrameworks, Args: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { + if len(args) > 0 { + frameworks := strings.Split(args[0], ",") + if len(frameworks) > 1 { + if frameworks[1] == "" { + return fmt.Errorf("usage: ,") + } + } + } else { return fmt.Errorf("requires at least one framework name") } return nil }, RunE: func(cmd *cobra.Command, args []string) error { flagValidationFramework() - scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} - // If no framework provided, use all - if len(args) == 0 { - scanInfo.SetPolicyIdentifierForGivenFrameworks(getter.NativeFrameworks) + var frameworks []string + + if len(args) == 0 { // scan all frameworks + frameworks = getter.NativeFrameworks scanInfo.ScanAll = true } else { // Read frameworks from input args - scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{} - frameworks := strings.Split(strings.Join(strings.Fields(args[0]), ""), ",") - scanInfo.PolicyIdentifier = SetScanForFirstFramework(frameworks) - if len(frameworks) > 1 { - scanInfo.SetPolicyIdentifierForGivenFrameworks(frameworks[1:]) - } + frameworks = strings.Split(args[0], ",") if len(args) > 1 { - // expected yaml/url input - if err := scanInfo.SetInputPatterns(args); err != nil { - return err + if len(args[1:]) == 0 || args[1] != "-" { + scanInfo.InputPatterns = args[1:] + } else { // store stdin to file - do NOT move to separate function !! + tempFile, err := os.CreateTemp(".", "tmp-kubescape*.yaml") + if err != nil { + return err + } + defer os.Remove(tempFile.Name()) + + if _, err := io.Copy(tempFile, os.Stdin); err != nil { + return err + } + scanInfo.InputPatterns = []string{tempFile.Name()} } } } + scanInfo.SetPolicyIdentifiers(frameworks, reporthandling.KindFramework) + scanInfo.Init() cautils.SetSilentMode(scanInfo.Silent) err := clihandler.ScanCliSetup(&scanInfo) @@ -62,13 +77,13 @@ func init() { scanInfo.FrameworkScan = true } -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 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 { diff --git a/clihandler/initcli.go b/clihandler/initcli.go index fb6acc9a..f74dac57 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -101,9 +101,10 @@ func setPolicyGetter(scanInfo *cautils.ScanInfo, customerGUID string) { if scanInfo.ScanAll { frameworks, err := g.ListCustomFrameworks(customerGUID) if err != nil { - glog.Error("could not get custom frameworks") + glog.Error("failed to get custom frameworks") // handle error + return } - scanInfo.SetPolicyIdentifierForGivenFrameworks(frameworks) + scanInfo.SetPolicyIdentifiers(frameworks, reporthandling.KindFramework) } } } diff --git a/examples/exceptions/README.md b/examples/exceptions/README.md index 75f23e89..4fd13019 100644 --- a/examples/exceptions/README.md +++ b/examples/exceptions/README.md @@ -27,10 +27,10 @@ e.g. When a `kube-system` resource fails and it is ok, simply add the resource t ## Usage -The `resources` list and `posturePolicies` list are design to be a combination of the resources sand policies to exclude +The `resources` list and `posturePolicies` list are design to be a combination of the resources and policies to exclude > You must declare at least one resource and one policy -e.g. If you wish to exclude all namespaces with the label "environment": "dev", the resource list should look as following: +e.g. If you wish to exclude all namespaces with the label `"environment": "dev"`, the resource list should look as following: ``` "resources": [ { @@ -43,7 +43,7 @@ e.g. If you wish to exclude all namespaces with the label "environment": "dev", ] ``` -But if you wish to exclude all namespaces **OR** any resource with the label "environment": "dev", the resource list should look as following: +But if you wish to exclude all namespaces **OR** any resource with the label `"environment": "dev"`, the resource list should look as following: ``` "resources": [ { @@ -63,17 +63,17 @@ But if you wish to exclude all namespaces **OR** any resource with the label "en Same works with the `posturePolicies` list -> -e.g. If you wish to exclude the resources decleared in the `resources` list that faild when scanning the `NSA` framework **AND** failed the `Allowed hostPath` control, the `posturePolicies` list should look as following: +e.g. If you wish to exclude the resources declared in the `resources` list that failed when scanning the `NSA` framework **AND** failed the `Allowed hostPath` control, the `posturePolicies` list should look as following: ``` "posturePolicies": [ { - "frameworkName": "NSA" + "frameworkName": "NSA", "controlName": "Allowed hostPath" } ] ``` -But if you wish to exclude the resources decleared in the `resources` list that faild when scanning the `NSA` framework **OR** failed the `Allowed hostPath` control, the `posturePolicies` list should look as following: +But if you wish to exclude the resources declared in the `resources` list that failed when scanning the `NSA` framework **OR** failed the `Allowed hostPath` control, the `posturePolicies` list should look as following: ``` "posturePolicies": [ { diff --git a/smoke_testing/smoke_utils.py b/smoke_testing/smoke_utils.py index 7b56349e..3bff46d9 100644 --- a/smoke_testing/smoke_utils.py +++ b/smoke_testing/smoke_utils.py @@ -7,7 +7,13 @@ def get_exec_from_args(args: list): def run_command(command): try: - return f"{subprocess.check_output(command, stderr=subprocess.STDOUT)}" + return f"{subprocess.check_output(command, stdin=subprocess.PIPE, stderr=subprocess.STDOUT)}" except Exception as e: return f"{e}" + +def assertion(msg): + errors = ["Error: invalid parameter", "exit status 1"] + for e in errors: + assert e not in msg, msg + diff --git a/smoke_testing/test_scan.py b/smoke_testing/test_scan.py index d886d0da..942d79aa 100644 --- a/smoke_testing/test_scan.py +++ b/smoke_testing/test_scan.py @@ -3,15 +3,71 @@ import smoke_utils import sys -def full_scan(kubescape_exec: str): - return smoke_utils.run_command(command=[kubescape_exec, "scan", "framework", "nsa", os.path.join("..", "*.yaml")]) +all_files = os.path.join("..", "examples", "online-boutique", "*.yaml") +single_file = os.path.join("..", "examples", "online-boutique", "frontend.yaml") + + +def scan_all(kubescape_exec: str): + return smoke_utils.run_command(command=[kubescape_exec, "scan", all_files]) + + +def scan_control_name(kubescape_exec: str): + return smoke_utils.run_command(command=[kubescape_exec, "scan", "control", 'Allowed hostPath', all_files]) + + +def scan_control_id(kubescape_exec: str): + return smoke_utils.run_command(command=[kubescape_exec, "scan", "control", 'C-0006', all_files]) + + +def scan_controls(kubescape_exec: str): + return smoke_utils.run_command(command=[kubescape_exec, "scan", "control", 'Allowed hostPath,Allow privilege escalation', all_files]) + + +def scan_framework(kubescape_exec: str): + return smoke_utils.run_command(command=[kubescape_exec, "scan", "framework", "nsa", all_files]) + + +def scan_frameworks(kubescape_exec: str): + return smoke_utils.run_command(command=[kubescape_exec, "scan", "framework", "nsa,mitre,armobest", all_files]) + + +def scan_from_stdin(kubescape_exec: str): + return smoke_utils.run_command(command=["cat", single_file, "|", kubescape_exec, "scan", "framework", "nsa", "-"]) def run(kubescape_exec: str): - # return - print("Testing E2E yaml files") - msg = full_scan(kubescape_exec=kubescape_exec) - assert "exit status 1" not in msg, msg + print("Testing E2E on yaml files") + + # TODO - fix support + # print("Testing scan all yaml files") + # msg = scan_all(kubescape_exec=kubescape_exec) + # smoke_utils.assertion(msg) + + print("Testing scan control name") + msg = scan_control_name(kubescape_exec=kubescape_exec) + smoke_utils.assertion(msg) + + print("Testing scan control id") + msg = scan_control_id(kubescape_exec=kubescape_exec) + smoke_utils.assertion(msg) + + print("Testing scan controls") + msg = scan_controls(kubescape_exec=kubescape_exec) + smoke_utils.assertion(msg) + + print("Testing scan framework") + msg = scan_framework(kubescape_exec=kubescape_exec) + smoke_utils.assertion(msg) + + print("Testing scan frameworks") + msg = scan_frameworks(kubescape_exec=kubescape_exec) + smoke_utils.assertion(msg) + + # TODO - fix test + # print("Testing scan from stdin") + # msg = scan_from_stdin(kubescape_exec=kubescape_exec) + # smoke_utils.assertion(msg) + print("Done E2E yaml files")