Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow use of multiple policy types (scan -t x,y or scan -t x -t y) #368

Merged
merged 4 commits into from
Nov 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ for more information on how to use the server endpoints.
To scan your code for security issues you can run the following

```sh
$ terrascan scan -t aws
$ terrascan scan
```
Terrascan will exit 3 if any issues are found.

Expand Down
34 changes: 24 additions & 10 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,24 @@ Use "terrascan [command] --help" for more information about a command.
The initialization process downloads the latest policies from the [repository](https://github.com/accurics/terrascan) into `~/.terrascan`. The policies are located at `~/.terrascan/pkg/policies/opa/rego` and are fetched when scanning the IaC. This command is implicitly executed if the `scan` command doesn't found policies while executing.

### Scanning
The CLI will default to the `scan` command if no other subcommands are used. For example, the below two commands will scan the current directory containing Terraform HCL2 files for AWS resources:
The CLI will default to scanning all supported cloud providers on Terraform HCL files if the `scan` command is used with no arguments. For example, the below two commands will scan the current directory containing Terraform HCL2 files for supported cloud providers (AWS, GCP, and Azure) resources:

``` Bash
$ terrascan scan
```

Individual cloud providers can be specified using the -t flag as follows:

``` Bash
$ terrascan -t aws
$ terrascan scan -t aws
```

By default Terrascan defaults to scanning Terraform HCL files, you can change the IaC provider using the -i flag. Here's an example of scanning kubernetes yaml files:

``` Bash
$ terrascan scan -t k8s -i k8s
```

The `scan` command support flags to configure: the directory being scanned, scanning of a specific file, IaC provier type, path to policies, and policy type. The full list of flags can be found by typing `terrascan scan -h`

``` Bash
Expand All @@ -102,14 +113,17 @@ Usage:
terrascan scan [flags]

Flags:
--config-only will output resource config (should only be used for debugging purposes)
-h, --help help for scan
-d, --iac-dir string path to a directory containing one or more IaC files (default ".")
-f, --iac-file string path to a single IaC file
-i, --iac-type string iac type (terraform, k8s)
--iac-version string iac version terraform:(v12) k8s:(v1)
-p, --policy-path string policy path directory
-t, --policy-type string <required> policy type (aws, azure, gcp, k8s, github)
--config-only will output resource config (should only be used for debugging purposes)
-h, --help help for scan
-d, --iac-dir string path to a directory containing one or more IaC files (default ".")
-f, --iac-file string path to a single IaC file
-i, --iac-type string iac type (k8s, terraform)
--iac-version string iac version (k8s: v1, terraform: v12)
-p, --policy-path stringArray policy path directory
-t, --policy-type strings policy type (all, aws, azure, gcp, github, k8s) (default [all])
-r, --remote-type string type of remote backend (git, s3, gcs, http)
-u, --remote-url string url pointing to remote IaC repository
--use-colors string color output (auto, t, f) (default "auto")

Global Flags:
-c, --config-path string config file path
Expand Down
5 changes: 3 additions & 2 deletions pkg/cli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ import (
)

// Run executes terrascan in CLI mode
func Run(iacType, iacVersion, cloudType, iacFilePath, iacDirPath, configFile,
policyPath, format, remoteType, remoteURL string, configOnly, useColors bool) {
func Run(iacType, iacVersion string, cloudType []string,
iacFilePath, iacDirPath, configFile string, policyPath []string,
format, remoteType, remoteURL string, configOnly, useColors bool) {

// temp dir to download the remote repo
tempDir := filepath.Join(os.TempDir(), utils.GenRandomString(6))
Expand Down
12 changes: 6 additions & 6 deletions pkg/cli/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestRun(t *testing.T) {
name string
iacType string
iacVersion string
cloudType string
cloudType []string
iacFilePath string
iacDirPath string
configFile string
Expand All @@ -36,31 +36,31 @@ func TestRun(t *testing.T) {
}{
{
name: "normal terraform run",
cloudType: "terraform",
cloudType: []string{"terraform"},
iacDirPath: "testdata/run-test",
},
{
name: "normal k8s run",
cloudType: "k8s",
cloudType: []string{"k8s"},
iacDirPath: "testdata/run-test",
},
{
name: "config-only flag terraform",
cloudType: "terraform",
cloudType: []string{"terraform"},
iacFilePath: "testdata/run-test/config-only.tf",
configOnly: true,
},
{
name: "config-only flag k8s",
cloudType: "k8s",
cloudType: []string{"k8s"},
iacFilePath: "testdata/run-test/config-only.yaml",
configOnly: true,
},
}

for _, tt := range table {
t.Run(tt.name, func(t *testing.T) {
Run(tt.iacType, tt.iacVersion, tt.cloudType, tt.iacFilePath, tt.iacDirPath, tt.configFile, "", "", "", "", tt.configOnly, false)
Run(tt.iacType, tt.iacVersion, tt.cloudType, tt.iacFilePath, tt.iacDirPath, tt.configFile, []string{}, "", "", "", tt.configOnly, false)
})
}
}
9 changes: 4 additions & 5 deletions pkg/cli/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ import (

var (
// PolicyPath Policy path directory
PolicyPath string
PolicyPath []string

// PolicyType Cloud type (aws, azure, gcp, github)
PolicyType string
PolicyType []string

// IacType IaC type (terraform)
IacType string
Expand Down Expand Up @@ -104,17 +104,16 @@ func scan(cmd *cobra.Command, args []string) {
}

func init() {
scanCmd.Flags().StringVarP(&PolicyType, "policy-type", "t", "", fmt.Sprintf("<required> policy type (%v)", strings.Join(policy.SupportedPolicyTypes(), ", ")))
scanCmd.Flags().StringSliceVarP(&PolicyType, "policy-type", "t", []string{"all"}, fmt.Sprintf("policy type (%s)", strings.Join(policy.SupportedPolicyTypes(true), ", ")))
scanCmd.Flags().StringVarP(&IacType, "iac-type", "i", "", fmt.Sprintf("iac type (%v)", strings.Join(iacProvider.SupportedIacProviders(), ", ")))
scanCmd.Flags().StringVarP(&IacVersion, "iac-version", "", "", fmt.Sprintf("iac version (%v)", strings.Join(iacProvider.SupportedIacVersions(), ", ")))
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().StringArrayVarP(&PolicyPath, "policy-path", "p", []string{}, "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)")
scanCmd.MarkFlagRequired("policy-type")
RegisterCommand(rootCmd, scanCmd)
}
7 changes: 4 additions & 3 deletions pkg/http-server/file-scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"io/ioutil"
"net/http"
"os"
"strings"

"github.com/accurics/terrascan/pkg/runtime"
"github.com/gorilla/mux"
Expand All @@ -36,7 +37,7 @@ func (g *APIHandler) scanFile(w http.ResponseWriter, r *http.Request) {
var (
iacType = params["iac"]
iacVersion = params["iacVersion"]
cloudType = params["cloud"]
cloudType = strings.Split(params["cloud"], ",")
)

// parse multipart form, 10 << 20 specifies maximum upload of 10 MB files
Expand Down Expand Up @@ -85,10 +86,10 @@ func (g *APIHandler) scanFile(w http.ResponseWriter, r *http.Request) {
var executor *runtime.Executor
if g.test {
executor, err = runtime.NewExecutor(iacType, iacVersion, cloudType,
tempFile.Name(), "", "", "./testdata/testpolicies")
tempFile.Name(), "", "", []string{"./testdata/testpolicies"})
} else {
executor, err = runtime.NewExecutor(iacType, iacVersion, cloudType,
tempFile.Name(), "", "", "")
tempFile.Name(), "", "", []string{})
}
if err != nil {
zap.S().Error(err)
Expand Down
9 changes: 5 additions & 4 deletions pkg/http-server/remote-repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"net/http"
"os"
"path/filepath"
"strings"

"github.com/accurics/terrascan/pkg/downloader"
"github.com/accurics/terrascan/pkg/runtime"
Expand All @@ -48,7 +49,7 @@ func (g *APIHandler) scanRemoteRepo(w http.ResponseWriter, r *http.Request) {
// url params
iacType = params["iac"]
iacVersion = params["iacVersion"]
cloudType = params["cloud"]
cloudType = strings.Split(params["cloud"], ",")
)

// read request body
Expand All @@ -64,10 +65,10 @@ func (g *APIHandler) scanRemoteRepo(w http.ResponseWriter, r *http.Request) {
s.d = downloader.NewDownloader()
var results interface{}
if g.test {
results, err = s.ScanRemoteRepo(iacType, iacVersion, cloudType, "./testdata/testpolicies")
results, err = s.ScanRemoteRepo(iacType, iacVersion, cloudType, []string{"./testdata/testpolicies"})

} else {
results, err = s.ScanRemoteRepo(iacType, iacVersion, cloudType, "")
results, err = s.ScanRemoteRepo(iacType, iacVersion, cloudType, []string{})
}
if err != nil {
apiErrorResponse(w, err.Error(), http.StatusBadRequest)
Expand All @@ -89,7 +90,7 @@ func (g *APIHandler) scanRemoteRepo(w http.ResponseWriter, r *http.Request) {

// ScanRemoteRepo is the actual method where a remote repo is downloaded and
// scanned for violations
func (s *scanRemoteRepoReq) ScanRemoteRepo(iacType, iacVersion, cloudType, policyPath string) (interface{}, error) {
func (s *scanRemoteRepoReq) ScanRemoteRepo(iacType, iacVersion string, cloudType []string, policyPath []string) (interface{}, error) {

// return params
var (
Expand Down
4 changes: 2 additions & 2 deletions pkg/http-server/remote-repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func TestScanRemoteRepo(t *testing.T) {
name string
iacType string
iacVersion string
cloudType string
cloudType []string
s *scanRemoteRepoReq
wantOutput interface{}
wantErr error
Expand Down Expand Up @@ -69,7 +69,7 @@ func TestScanRemoteRepo(t *testing.T) {

for _, tt := range table {
t.Run(tt.name, func(t *testing.T) {
gotOutput, gotErr := tt.s.ScanRemoteRepo(tt.iacType, tt.iacVersion, tt.cloudType, "")
gotOutput, gotErr := tt.s.ScanRemoteRepo(tt.iacType, tt.iacVersion, tt.cloudType, []string{})
if !reflect.DeepEqual(gotErr, tt.wantErr) {
t.Errorf("error got: '%v', want: '%v'", gotErr, tt.wantErr)
}
Expand Down
27 changes: 27 additions & 0 deletions pkg/policy/all.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Copyright (C) 2020 Accurics, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package policy

const (
defaultAllIacType supportedIacType = "terraform"
defaultAllIacVersion supportedIacVersion = "v12"
)

func init() {
// Register all as a cloud provider with terrascan
RegisterIndirectCloudProvider("all", defaultAllIacType, defaultAllIacVersion, func() []string { return SupportedPolicyTypes(false) })
}
51 changes: 51 additions & 0 deletions pkg/policy/all_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
Copyright (C) 2020 Accurics, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package policy

import (
"reflect"
"sort"
"testing"
)

func TestPolicyTypeAllExpandedCorrectly(t *testing.T) {
t.Run("policy type all gets right policy names", func(t *testing.T) {

want := SupportedPolicyTypes(false)
got := supportedCloudProvider["all"].policyNames()

sort.Strings(want)
sort.Strings(got)

if !reflect.DeepEqual(got, want) {
t.Errorf("got: '%v', want: '%v'", got, want)
}
})

t.Run("policy type all gets right policy paths", func(t *testing.T) {

want := GetDefaultPolicyPaths(SupportedPolicyTypes(false))
got := GetDefaultPolicyPaths([]string{"all"})

sort.Strings(want)
sort.Strings(got)

if !reflect.DeepEqual(got, want) {
t.Errorf("got: '%v', want: '%v'", got, want)
}
})
}
Loading