Repository: tfsec/tfsec
Pull request #532 (2020-12-03 21:56:23)

Also load exclude list from config and merge with flags
Also fixes a small bug where config files with the suffix `yml` where not being loaded correctly.



Patch:
diff --git a/cmd/tfsec/main.go b/cmd/tfsec/main.go
index 683dcb11e..def0da919 100644
--- a/cmd/tfsec/main.go
+++ b/cmd/tfsec/main.go
@@ -150,7 +150,7 @@ var rootCmd = &cobra.Command{
 		}
 
 		debug.Log("Starting scanner...")
-		results := scanner.New().Scan(blocks, excludedChecksList)
+		results := scanner.New().Scan(blocks, mergeWithoutDuplicates(excludedChecksList, tfsecConfig.ExcludedChecks))
 		results = updateResultSeverity(results)
 		if err := formatter(outputFile, results, dir); err != nil {
 			fmt.Println(err)
@@ -165,6 +165,20 @@ var rootCmd = &cobra.Command{
 	},
 }
 
+func mergeWithoutDuplicates(left, right []string) []string {
+	all := append(left, right...)
+	var set = map[string]bool{}
+	for _, x := range all {
+		set[x] = true
+	}
+	var result []string
+	for x, _ := range set {
+		result = append(result, x)
+	}
+
+	return result
+}
+
 func allInfo(results []scanner.Result) bool {
 	for _, result := range results {
 		if result.Severity != scanner.SeverityInfo {
diff --git a/docs-website/_docs/config.md b/docs-website/_docs/config.md
index f38e4ae07..f4abbc4ce 100644
--- a/docs-website/_docs/config.md
+++ b/docs-website/_docs/config.md
@@ -21,6 +21,7 @@ The structure of the config file can be either `json` or `yaml` and is passed us
 ``` 
 
 or 
+
 ```yaml
 ---
 severity_overrides:
@@ -28,6 +29,27 @@ severity_overrides:
   AWS025: INFO
 ```
 
+## Excluding checks
+
+There are moments where the list of checks you'd want to exclude becomes larger and larger.
+Rather than passing all the excluded checks via the command line, you can use the configuration
+entry `exclude` to list them all out. 
+
+```json
+{
+  "exclude": ["CUS002", "AWS025"]
+}
+``` 
+
+or 
+
+```yaml
+---
+exclude:
+  - CUS002
+  - AWS025
+```
+
 ## Running with the config
 
 To run tfsec against a Terraform folder called `tf` with the config file `tfsec.yml` you would run 
diff --git a/internal/app/tfsec/config/config.go b/internal/app/tfsec/config/config.go
index ef7b8e7b4..f775dacbf 100644
--- a/internal/app/tfsec/config/config.go
+++ b/internal/app/tfsec/config/config.go
@@ -12,6 +12,7 @@ import (
 
 type Config struct {
 	SeverityOverrides map[string]string `json:"severity_overrides,omitempty" yaml:"severity_overrides,omitempty"`
+	ExcludedChecks    []string          `json:"exclude,omitempty" yaml:"exclude,omitempty"`
 }
 
 func LoadConfig(configFilePath string) (*Config, error) {
@@ -33,8 +34,7 @@ func LoadConfig(configFilePath string) (*Config, error) {
 		if err != nil {
 			return nil, err
 		}
-	case ".yml":
-	case ".yaml":
+	case ".yaml", ".yml":
 		err = yaml.Unmarshal(configFileContent, config)
 		if err != nil {
 			return nil, err


Test patch:
diff --git a/internal/app/tfsec/config/config_test.go b/internal/app/tfsec/config/config_test.go
new file mode 100644
index 000000000..063a61ec5
--- /dev/null
+++ b/internal/app/tfsec/config/config_test.go
@@ -0,0 +1,70 @@
+package config_test
+
+import (
+	"fmt"
+	"github.com/stretchr/testify/assert"
+	"github.com/tfsec/tfsec/internal/app/tfsec/config"
+	"io/ioutil"
+	"os"
+	"testing"
+)
+
+func TestExcludesElementsFromYAML(t *testing.T) {
+	content := `
+severity_overrides:
+  AWS018: INFO
+
+exclude:
+  - DP001
+`
+	c := load(t,"config.yaml", content)
+
+	assert.Contains(t, c.SeverityOverrides, "AWS018")
+	assert.Contains(t, c.ExcludedChecks, "DP001")
+}
+
+func TestExcludesElementsFromYML(t *testing.T) {
+	content := `
+severity_overrides:
+  AWS018: INFO
+
+exclude:
+  - DP001
+`
+	c := load(t,"config.yml", content)
+
+	assert.Contains(t, c.SeverityOverrides, "AWS018")
+	assert.Contains(t, c.ExcludedChecks, "DP001")
+}
+
+func TestExcludesElementsFromJSON(t *testing.T) {
+	content := `{
+  "severity_overrides": {
+    "AWS018": "INFO"
+  },
+  "exclude": [
+    "DP001"
+  ]
+}
+`
+	c := load(t,"config.json", content)
+
+	assert.Contains(t, c.SeverityOverrides, "AWS018")
+	assert.Contains(t, c.ExcludedChecks, "DP001")
+}
+
+func load(t *testing.T, filename, content string) *config.Config {
+	dir, err := ioutil.TempDir("", "")
+	assert.NoError(t, err)
+
+	configFileName := fmt.Sprintf("%s/%s", dir, filename)
+
+	err = ioutil.WriteFile(configFileName, []byte(content), os.ModePerm)
+	assert.NoError(t, err)
+
+	c, err := config.LoadConfig(configFileName)
+	assert.NoError(t, err)
+	return c
+}
+
+