Skip to content

Commit

Permalink
Merge pull request #441 from patilpankaj212/scan-and-skip-rules
Browse files Browse the repository at this point in the history
implement scan and skip rules
  • Loading branch information
Willie authored Jan 5, 2021
2 parents 02e77b8 + 78b6822 commit b8a6849
Show file tree
Hide file tree
Showing 37 changed files with 1,023 additions and 248 deletions.
15 changes: 15 additions & 0 deletions config/terrascan.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,18 @@
[notifications]
[notifications.webhook]
url = "https://httpbin.org/post"

# scan and skip rules configuration
[rules]
# scan rules (list of rules to scan)
# adding rules here will override rules in the policy path
scan-rules = [
"AWS.S3Bucket.DS.High.1043",
"AWS.S3Bucket.IAM.High.0370"
]

# skip rules (list of rules to skip)
skip-rules = [
"AWS.S3Bucket.DS.High.1043",
"AWS.S3Bucket.IAM.High.0370",
]
11 changes: 8 additions & 3 deletions pkg/cli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ type ScanOptions struct {
UseColors bool
useColors string // used for flag processing

// ScanRules is the array of rules to scan
scanRules []string

// SkipRules is the array of rules to skip while scanning
skipRules []string

// Verbose indicates whether to display all fields in default human readlbe output
Verbose bool
}
Expand All @@ -101,8 +107,7 @@ func (s *ScanOptions) Scan() error {
//Init initalises and validates ScanOptions
func (s *ScanOptions) Init() error {
s.initColor()
err := s.validate()
if err != nil {
if err := s.validate(); err != nil {
zap.S().Error("failed to start scan", zap.Error(err))
return err
}
Expand Down Expand Up @@ -162,7 +167,7 @@ func (s *ScanOptions) Run() error {

// create a new runtime executor for processing IaC
executor, err := runtime.NewExecutor(s.iacType, s.iacVersion, s.policyType,
s.iacFilePath, s.iacDirPath, s.configFile, s.policyPath)
s.iacFilePath, s.iacDirPath, s.configFile, s.policyPath, s.scanRules, s.skipRules)
if err != nil {
return err
}
Expand Down
38 changes: 38 additions & 0 deletions pkg/cli/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func TestRun(t *testing.T) {
testDirPath := "testdata/run-test"
kustomizeTestDirPath := testDirPath + "/kustomize-test"
testTerraformFilePath := testDirPath + "/config-only.tf"
ruleSlice := []string{"AWS.ECR.DataSecurity.High.0579", "AWS.SecurityGroup.NetworkPortsSecurity.Low.0561"}

table := []struct {
name string
Expand Down Expand Up @@ -133,6 +134,43 @@ func TestRun(t *testing.T) {
},
wantErr: true,
},
{
name: "incorrect config file",
scanOptions: &ScanOptions{
policyType: []string{"all"},
iacDirPath: testTerraformFilePath,
outputType: "json",
configFile: "invalidFile",
},
wantErr: true,
},
{
name: "run with skip rules",
scanOptions: &ScanOptions{
policyType: []string{"all"},
iacDirPath: testDirPath,
outputType: "json",
skipRules: ruleSlice,
},
},
{
name: "run with scan rules",
scanOptions: &ScanOptions{
policyType: []string{"all"},
iacDirPath: testDirPath,
outputType: "yaml",
scanRules: ruleSlice,
},
},
{
name: "config file with rules",
scanOptions: &ScanOptions{
policyType: []string{"all"},
iacDirPath: testDirPath,
outputType: "yaml",
configFile: "testdata/configFile.toml",
},
},
}

for _, tt := range table {
Expand Down
2 changes: 2 additions & 0 deletions pkg/cli/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,7 @@ func init() {
// flag passes a string, but we normalize to bool in PreRun
scanCmd.Flags().StringVar(&scanOptions.useColors, "use-colors", "auto", "color output (auto, t, f)")
scanCmd.Flags().BoolVarP(&scanOptions.Verbose, "verbose", "v", false, "will show violations with details (applicable for default output)")
scanCmd.Flags().StringSliceVarP(&scanOptions.scanRules, "scan-rules", "", []string{}, "one or more rules to scan (example: --scan-rules=\"ruleID1,ruleID2\")")
scanCmd.Flags().StringSliceVarP(&scanOptions.skipRules, "skip-rules", "", []string{}, "one or more rules to skip while scanning (example: --skip-rules=\"ruleID1,ruleID2\")")
RegisterCommand(rootCmd, scanCmd)
}
7 changes: 7 additions & 0 deletions pkg/cli/testdata/configFile.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[rules]
scan-rules = [
"AWS.ECR.DataSecurity.High.0579"
]
skip-rules = [
"AWS.SecurityGroup.NetworkPortsSecurity.Low.0561"
]
84 changes: 84 additions & 0 deletions pkg/config/config-reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
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 config

import (
"fmt"
"io/ioutil"
"os"

"github.com/pelletier/go-toml"
"go.uber.org/zap"
)

var (
// ErrTomlLoadConfig indicates error: Failed to load toml config
errTomlLoadConfig = fmt.Errorf("failed to load toml config")
// ErrNotPresent indicates error: Config file not present
ErrNotPresent = fmt.Errorf("config file not present")
)

// TerrascanConfigReader holds the terrascan config file name
type TerrascanConfigReader struct {
config TerrascanConfig
}

// NewTerrascanConfigReader initialises and returns a config reader
func NewTerrascanConfigReader(fileName string) (*TerrascanConfigReader, error) {
config := TerrascanConfig{}
configReader := new(TerrascanConfigReader)
configReader.config = config

// empty file name check should be done by the caller, this is a safe check
if fileName == "" {
zap.S().Debug("no config file specified")
return configReader, nil
}

// return error if file doesn't exist
_, err := os.Stat(fileName)
if err != nil {
zap.S().Error("config file: %s, doesn't exist", fileName)
return configReader, ErrNotPresent
}

data, err := ioutil.ReadFile(fileName)
if err != nil {
zap.S().Error("error loading config file", zap.Error(err))
return configReader, errTomlLoadConfig
}

if err = toml.Unmarshal(data, &configReader.config); err != nil {
return configReader, err
}
return configReader, nil
}

// GetPolicyConfig will return the policy config from the terrascan config file
func (r TerrascanConfigReader) GetPolicyConfig() Policy {
return r.config.Policy
}

// GetNotifications will return the notifiers specified in the terrascan config file
func (r TerrascanConfigReader) GetNotifications() map[string]Notifier {
return r.config.Notifications
}

// GetRules will return the rules specified in the terrascan config file
func (r TerrascanConfigReader) GetRules() Rules {
return r.config.Rules
}
128 changes: 128 additions & 0 deletions pkg/config/config-reader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
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 config

import (
"reflect"
"testing"
)

func TestNewTerrascanConfigReader(t *testing.T) {
testNotifier := Notifier{
NotifierType: "webhook",
NotifierConfig: map[string]interface{}{
"url": "testurl1",
},
}
testPolicy := Policy{
BasePath: "custom-path",
RepoPath: "rego-subdir",
RepoURL: "https://repository/url",
Branch: "branch-name",
}
testRules := Rules{
ScanRules: []string{"rule.1", "rule.2", "rule.3", "rule.4", "rule.5"},
SkipRules: []string{"rule.1"},
}

type args struct {
fileName string
}
tests := []struct {
name string
args args
want *TerrascanConfigReader
wantErr bool
assertGetters bool
Policy
notifications map[string]Notifier
Rules
}{
{
name: "empty config file",
args: args{
fileName: "",
},
want: &TerrascanConfigReader{},
},
{
name: "non existent config file",
args: args{
fileName: "test",
},
wantErr: true,
want: &TerrascanConfigReader{},
},
{
name: "invalid toml config file",
args: args{
fileName: "testdata/invalid.toml",
},
wantErr: true,
want: &TerrascanConfigReader{},
},
{
name: "valid toml config file with partial fields",
args: args{
fileName: "testdata/terrascan-config.toml",
},
want: &TerrascanConfigReader{
config: TerrascanConfig{
Policy: testPolicy,
},
},
},
{
name: "valid toml config file with all fields",
args: args{
fileName: "testdata/terrascan-config-all-fields.toml",
},
want: &TerrascanConfigReader{
config: TerrascanConfig{
Policy: testPolicy,
Notifications: map[string]Notifier{
"webhook1": testNotifier,
},
Rules: testRules,
},
},
assertGetters: true,
notifications: map[string]Notifier{
"webhook1": testNotifier,
},
Policy: testPolicy,
Rules: testRules,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := NewTerrascanConfigReader(tt.args.fileName)
if (err != nil) != tt.wantErr {
t.Errorf("NewTerrascanConfigReader() got error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewTerrascanConfigReader() = got %v, want %v", got, tt.want)
}
if tt.assertGetters {
if !reflect.DeepEqual(got.GetPolicyConfig(), tt.Policy) || !reflect.DeepEqual(got.GetNotifications(), tt.notifications) || !reflect.DeepEqual(got.GetRules(), tt.Rules) {
t.Errorf("NewTerrascanConfigReader() = got config: %v, notifications: %v, rules: %v want config: %v, notifications: %v, rules: %v", got.GetPolicyConfig(), got.GetNotifications(), got.GetRules(), tt.Policy, tt.notifications, tt.Rules)
}
}
})
}
}
60 changes: 0 additions & 60 deletions pkg/config/configfile.go

This file was deleted.

Loading

0 comments on commit b8a6849

Please sign in to comment.