From 05e108b47bbd0cca48d4cd90481471bb0333d2ef Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Wed, 1 Sep 2021 15:51:24 +0300 Subject: [PATCH] add download json option --- cautils/downloadinfo.go | 20 ++++++++++ cautils/getter/getpolicies.go | 61 +++++++++++++++++++++++++++-- cautils/scaninfo.go | 27 ++++++++++++- cmd/download.go | 55 ++++++++++++++++++++++++++ cmd/framework.go | 8 +++- docs/release.md | 26 +++++++++++- policyhandler/handlepullpolicies.go | 2 +- 7 files changed, 190 insertions(+), 9 deletions(-) create mode 100644 cautils/downloadinfo.go create mode 100644 cmd/download.go diff --git a/cautils/downloadinfo.go b/cautils/downloadinfo.go new file mode 100644 index 00000000..a232183a --- /dev/null +++ b/cautils/downloadinfo.go @@ -0,0 +1,20 @@ +package cautils + +import ( + "os" + "path/filepath" + + "github.com/armosec/kubescape/cautils/getter" +) + +type DownloadInfo struct { + Path string + FrameworkName string +} + +func GetDefaultPath(frameworkName string) string { + if homeDir, err := os.UserHomeDir(); err == nil { + return filepath.Join(homeDir, getter.DefaultLocalStore, frameworkName+".json") + } + return filepath.Join(getter.DefaultLocalStore, frameworkName+".json") +} diff --git a/cautils/getter/getpolicies.go b/cautils/getter/getpolicies.go index 10287710..30e26857 100644 --- a/cautils/getter/getpolicies.go +++ b/cautils/getter/getpolicies.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "net/http" + "os" "strings" "github.com/armosec/kubescape/cautils/opapolicy" @@ -12,6 +13,8 @@ import ( const DefaultLocalStore = ".kubescape" +var path string + type IPolicyGetter interface { GetFramework(name string) (*opapolicy.Framework, error) } @@ -33,6 +36,15 @@ func NewDownloadReleasedPolicy() *DownloadReleasedPolicy { } } +func SaveFrameworkInFile(framework *opapolicy.Framework, path string) error { + encodedData, _ := json.Marshal(framework) + err := os.WriteFile(path, []byte(fmt.Sprintf("%v", string(encodedData))), 0644) + if err != nil { + return err + } + return nil +} + func (drp *DownloadReleasedPolicy) GetFramework(name string) (*opapolicy.Framework, error) { drp.setURL(name) respStr, err := HttpGetter(drp.httpClient, drp.hostURL) @@ -42,13 +54,56 @@ func (drp *DownloadReleasedPolicy) GetFramework(name string) (*opapolicy.Framewo framework := &opapolicy.Framework{} err = JSONDecoder(respStr).Decode(framework) + // SaveFrameworkInFile(framework) + // store in file + // + + /* + + 1. Public save framework function (framework, path) error + 2. Call the function from Download And GetFramework + 3. export to function: os.Join($HOME, getter.DefaultLocalStore, .json) + + */ return framework, err } -func (drp *DownloadReleasedPolicy) setURL(frameworkName string) { - // requestURI := "v1/armoFrameworks" +func (drp *DownloadReleasedPolicy) setURL(frameworkName string) error { + + resp, err := http.Get("https://api.github.com/repos/armosec/regolibrary/releases/latest") + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode < 200 || 301 < resp.StatusCode { + return fmt.Errorf("failed to download file, status code: %s", resp.Status) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + var data map[string]interface{} + err = json.Unmarshal(body, &data) + if err != nil { + return err + } + + if assets, ok := data["assets"].([]interface{}); ok { + for i := range assets { + if asset, ok := assets[i].(map[string]interface{}); ok { + if name, ok := asset["name"].(string); ok { + if name == frameworkName { + if url, ok := asset["browser_download_url"].(string); ok { + drp.hostURL = url + } + } + } + } + } + } + return nil - // drp.hostURL = URLEncoder(fmt.Sprintf("%s/%s", drp.hostURL, requestURI)) } // ======================================================================================================================= diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index 32545c5f..b7c5cfee 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -10,6 +10,8 @@ import ( type ScanInfo struct { PolicyGetter getter.IPolicyGetter PolicyIdentifier opapolicy.PolicyIdentifier + UseFrom string + UseDefault bool Format string Output string ExcludedNamespaces string @@ -19,13 +21,34 @@ type ScanInfo struct { func (scanInfo *ScanInfo) Init() { // scanInfo.setSilentMode() + scanInfo.setUseFrom() scanInfo.setOutputFile() scanInfo.setGetter() } -func (scanInfo *ScanInfo) setGetter() { - scanInfo.PolicyGetter = getter.NewArmoAPI() +func (scanInfo *ScanInfo) setUseFrom() { + if scanInfo.UseFrom != "" { + return + } + if scanInfo.UseDefault { + scanInfo.UseFrom = GetDefaultPath(scanInfo.PolicyIdentifier.Name) + } + } +func (scanInfo *ScanInfo) setGetter() { + + // // get from armo backend + // scanInfo.PolicyGetter = getter.NewArmoAPI() + + if scanInfo.UseFrom != "" { + // load from file + scanInfo.PolicyGetter = getter.NewLoadPolicy(scanInfo.UseFrom) + } else { + scanInfo.PolicyGetter = getter.NewDownloadReleasedPolicy() + + } +} + func (scanInfo *ScanInfo) setSilentMode() { if scanInfo.Format == "json" || scanInfo.Format == "junit" { scanInfo.Silent = true diff --git a/cmd/download.go b/cmd/download.go new file mode 100644 index 00000000..4bb9cd51 --- /dev/null +++ b/cmd/download.go @@ -0,0 +1,55 @@ +package cmd + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/armosec/kubescape/cautils" + "github.com/armosec/kubescape/cautils/getter" + "github.com/spf13/cobra" +) + +var downloadInfo cautils.DownloadInfo + +var downloadCmd = &cobra.Command{ + Use: "download framework ", + Short: "Download framework controls", + Long: ``, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 2 { + return fmt.Errorf("requires two arguments : framework ") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + downloadInfo.FrameworkName = args[1] + g := getter.NewDownloadReleasedPolicy() + if !(cmd.Flags().Lookup("output").Changed) { + downloadInfo.Path = cautils.GetDefaultPath(downloadInfo.FrameworkName) + } else { + currPath, err := os.Getwd() + if err != nil { + downloadInfo.Path = filepath.Join(downloadInfo.Path) + } else { + downloadInfo.Path = filepath.Join(currPath, downloadInfo.Path) + } + + } + frameworks, err := g.GetFramework(downloadInfo.FrameworkName) + if err != nil { + return err + } + err = getter.SaveFrameworkInFile(frameworks, downloadInfo.Path) + if err != nil { + return err + } + return nil + }, +} + +func init() { + rootCmd.AddCommand(downloadCmd) + downloadInfo = cautils.DownloadInfo{} + downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file.") +} diff --git a/cmd/framework.go b/cmd/framework.go index 5e334d13..160c8975 100644 --- a/cmd/framework.go +++ b/cmd/framework.go @@ -33,7 +33,7 @@ var frameworkCmd = &cobra.Command{ Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin", ValidArgs: supportedFrameworks, Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { + if len(args) < 1 && !(cmd.Flags().Lookup("use-from").Changed) { return fmt.Errorf("requires at least one argument") } if !isValidFramework(args[0]) { @@ -44,8 +44,10 @@ var frameworkCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { scanInfo.PolicyIdentifier = opapolicy.PolicyIdentifier{} scanInfo.PolicyIdentifier.Kind = opapolicy.KindFramework - scanInfo.PolicyIdentifier.Name = args[0] + if !(cmd.Flags().Lookup("use-from").Changed) { + scanInfo.PolicyIdentifier.Name = args[0] + } if len(args[1:]) == 0 || args[1] != "-" { scanInfo.InputPatterns = args[1:] } else { // store stout to file @@ -75,6 +77,8 @@ func isValidFramework(framework string) bool { func init() { scanCmd.AddCommand(frameworkCmd) scanInfo = cautils.ScanInfo{} + frameworkCmd.Flags().StringVarP(&scanInfo.UseFrom, "use-from", "", "", "Path to load framework from") + frameworkCmd.Flags().BoolVarP(&scanInfo.UseDefault, "use-default", "", false, "Load framework from default path") frameworkCmd.Flags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from check") frameworkCmd.Flags().StringVarP(&scanInfo.Format, "format", "f", "pretty-printer", `Output format. supported formats: "pretty-printer"/"json"/"junit"`) frameworkCmd.Flags().StringVarP(&scanInfo.Output, "output", "o", "", "Output file. print output to file and not stdout") diff --git a/docs/release.md b/docs/release.md index 4690dda4..ad80d4f6 100644 --- a/docs/release.md +++ b/docs/release.md @@ -50,6 +50,18 @@ kubescape scan framework nsa https://raw.githubusercontent.com/GoogleCloudPlatfo helm template [CHART] [flags] --generate-name --dry-run | kubescape scan framework nsa - ``` +### Scan on-prem (offline) + +* Scan using a framework from the local file system +``` +kubescape scan framework --use-from +``` + +* Scan using the framework from the default location in file system +``` +kubescape scan framework --use-default +``` + ## Output formats By default, the output is user friendly. @@ -64,4 +76,16 @@ kubescape scan framework nsa --format json --output results.json * Output in `junit xml` format ``` kubescape scan framework nsa --format junit --output results.xml -``` \ No newline at end of file +``` + +## Download + +* Download and save in file +``` +kubescape download framework nsa --output nsa.json +``` + +* Download and save in default file (`~/.kubescape/.json`) +``` +kubescape download framework nsa +``` diff --git a/policyhandler/handlepullpolicies.go b/policyhandler/handlepullpolicies.go index f034ba9b..ac8ce08d 100644 --- a/policyhandler/handlepullpolicies.go +++ b/policyhandler/handlepullpolicies.go @@ -25,7 +25,7 @@ func (policyHandler *PolicyHandler) GetPoliciesFromBackend(notification *opapoli } default: - err := fmt.Errorf("Missing rule kind, expected: %s", opapolicy.KindFramework) + err := fmt.Errorf("missing rule kind, expected: %s", opapolicy.KindFramework) errs = fmt.Errorf("%s", err.Error()) }