From 0481625c99a80496e8ab24ee81385a48e1ef55db Mon Sep 17 00:00:00 2001 From: Yusuf Kanchwala Date: Mon, 28 Sep 2020 18:59:25 +0530 Subject: [PATCH] add ability to download remote repository in CLI --- pkg/cli/run.go | 74 +++++++++++++++++++++++++++++++++++++++++++++++-- pkg/cli/scan.go | 21 ++++++++++++-- 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/pkg/cli/run.go b/pkg/cli/run.go index 15b4b0191..204a930b4 100644 --- a/pkg/cli/run.go +++ b/pkg/cli/run.go @@ -18,19 +18,45 @@ package cli import ( "flag" + "fmt" "os" + "path/filepath" + "github.com/accurics/terrascan/pkg/downloader" "github.com/accurics/terrascan/pkg/runtime" + "github.com/accurics/terrascan/pkg/utils" "github.com/accurics/terrascan/pkg/writer" + "go.uber.org/zap" ) // Run executes terrascan in CLI mode func Run(iacType, iacVersion, cloudType, iacFilePath, iacDirPath, configFile, - policyPath, format string, configOnly bool, useColors bool) { + policyPath, format, remoteType, remoteURL string, configOnly, useColors bool) { + + // validate remote repository options + toDownload, err := validateRemoteOpts(remoteType, remoteURL) + if err != nil { + os.Exit(5) + } + + // download remote repository + var tempDir string + if toDownload { + // temp dir to download the remote repo + tempDir = filepath.Join(os.TempDir(), utils.GenRandomString(6)) + defer os.RemoveAll(tempDir) + + // download remote repository + iacDirPath, err = downloadRemoteRepo(remoteType, remoteURL, tempDir) + if err != nil { + os.RemoveAll(tempDir) + os.Exit(5) + } + } // create a new runtime executor for processing IaC - executor, err := runtime.NewExecutor(iacType, iacVersion, cloudType, iacFilePath, - iacDirPath, configFile, policyPath) + executor, err := runtime.NewExecutor(iacType, iacVersion, cloudType, + iacFilePath, iacDirPath, configFile, policyPath) if err != nil { return } @@ -50,6 +76,48 @@ func Run(iacType, iacVersion, cloudType, iacFilePath, iacDirPath, configFile, } if results.Violations.ViolationStore.Count.TotalCount != 0 && flag.Lookup("test.v") == nil { + os.RemoveAll(tempDir) os.Exit(3) } } + +// validateRemoteOpts validate remote repository options +func validateRemoteOpts(remoteType, remoteURL string) (bool, error) { + + // 1. remoteType and remoteURL both are empty + if remoteType == "" && remoteURL == "" { + return false, nil + } + + // 2. remoteType and remoteURL both are not empty + if remoteType != "" && remoteURL != "" { + zap.S().Debugf("remoteType: %q, remoteURL: %q", remoteType, remoteURL) + return true, nil + } + + // 3. remoteType is empty and remoteURL is not + if remoteType != "" || remoteURL != "" { + zap.S().Errorf("remote type and remote url both options should be specified") + return false, fmt.Errorf("incorrect remote options") + } + + return false, nil +} + +// downloadRemoteRepo downloads the remote repo in the temp directory and +// returns the path of the dir where the remote repository has been downloaded +func downloadRemoteRepo(remoteType, remoteURL, destDir string) (string, error) { + + // new downloader + d := downloader.NewDownloader() + url := fmt.Sprintf("%s::%s", remoteType, remoteURL) + path, err := d.Download(url, destDir) + if err != nil { + zap.S().Errorf("failed to download remote repo url: %q, type: %q. error: '%v'", + remoteURL, remoteType, err) + return "", err + } + + // successful + return path, nil +} diff --git a/pkg/cli/scan.go b/pkg/cli/scan.go index 0bfd77632..1720d36d9 100644 --- a/pkg/cli/scan.go +++ b/pkg/cli/scan.go @@ -31,18 +31,32 @@ import ( var ( // PolicyPath Policy path directory PolicyPath string + // PolicyType Cloud type (aws, azure, gcp, github) PolicyType string + // IacType IaC type (terraform) IacType string + // IacVersion IaC version (for terraform:v12) IacVersion string + // IacFilePath Path to a single IaC file IacFilePath string + // IacDirPath Path to a directory containing one or more IaC files IacDirPath string - //ConfigOnly will output resource config (should only be used for debugging purposes) + + // RemoteType indicates the type of remote backend. Supported backends are + // git s3, gcs, http. + RemoteType string + + // RemoteURL points to the remote Iac repository on git, s3, gcs, http + RemoteURL string + + // ConfigOnly will output resource config (should only be used for debugging purposes) ConfigOnly bool + // UseColors indicates whether to use color output UseColors bool useColors string // used for flag processing @@ -85,7 +99,8 @@ Detect compliance and security violations across Infrastructure as Code to mitig func scan(cmd *cobra.Command, args []string) { zap.S().Debug("running terrascan in cli mode") - Run(IacType, IacVersion, PolicyType, IacFilePath, IacDirPath, ConfigFile, PolicyPath, OutputType, ConfigOnly, UseColors) + Run(IacType, IacVersion, PolicyType, IacFilePath, IacDirPath, ConfigFile, + PolicyPath, OutputType, RemoteType, RemoteURL, ConfigOnly, UseColors) } func init() { @@ -95,6 +110,8 @@ func init() { scanCmd.Flags().StringVarP(&IacFilePath, "iac-file", "f", "", "path to a single IaC file") scanCmd.Flags().StringVarP(&IacDirPath, "iac-dir", "d", ".", "path to a directory containing one or more IaC files") scanCmd.Flags().StringVarP(&PolicyPath, "policy-path", "p", "", "policy path directory") + scanCmd.Flags().StringVarP(&RemoteType, "remote-type", "r", "", "type of remote backend (git, s3, gcs, http)") + scanCmd.Flags().StringVarP(&RemoteURL, "remote-url", "u", "", "url pointing to remote IaC repository") scanCmd.Flags().BoolVarP(&ConfigOnly, "config-only", "", false, "will output resource config (should only be used for debugging purposes)") // flag passes a string, but we normalize to bool in PreRun scanCmd.Flags().StringVar(&useColors, "use-colors", "auto", "color output (auto, t, f)")