Repository: aquasecurity/tfsec
Pull request #864 (2021-07-13 14:52:33)

Add -p flag to allow panics in checks


Patch:
diff --git a/cmd/tfsec/main.go b/cmd/tfsec/main.go
index b0d036abd..926dae084 100644
--- a/cmd/tfsec/main.go
+++ b/cmd/tfsec/main.go
@@ -52,6 +52,7 @@ var ignoreInfo = false
 var allDirs = false
 var runStatistics bool
 var ignoreHCLErrors bool
+var stopOnCheckError bool
 
 func init() {
 	rootCmd.Flags().BoolVar(&ignoreHCLErrors, "ignore-hcl-errors", ignoreHCLErrors, "Stop and report an error if an HCL parse error is encountered")
@@ -77,6 +78,7 @@ func init() {
 	rootCmd.Flags().BoolVar(&runStatistics, "run-statistics", runStatistics, "View statistics table of current findings.")
 	rootCmd.Flags().BoolVar(&ignoreWarnings, "ignore-warnings", ignoreWarnings, "Don't show warnings in the output.")
 	rootCmd.Flags().BoolVar(&ignoreInfo, "ignore-info", ignoreWarnings, "Don't show info results in the output.")
+	rootCmd.Flags().BoolVarP(&stopOnCheckError, "allow-checks-to-panic", "p", stopOnCheckError, "Allow panics to propagate up from rule checking")
 }
 
 func main() {
@@ -341,6 +343,8 @@ func getScannerOptions() []scanner.Option {
 		options = append(options, scanner.OptionIncludeIgnored())
 	}
 
+	options = append(options, scanner.OptionIgnoreCheckErrors(!stopOnCheckError))
+
 	var allExcludedRuleIDs []string
 	for _, exclude := range strings.Split(excludedRuleIDs, ",") {
 		allExcludedRuleIDs = append(allExcludedRuleIDs, strings.TrimSpace(exclude))
diff --git a/internal/app/tfsec/scanner/option.go b/internal/app/tfsec/scanner/option.go
index 06dad9608..9b71c4d1f 100644
--- a/internal/app/tfsec/scanner/option.go
+++ b/internal/app/tfsec/scanner/option.go
@@ -19,3 +19,9 @@ func OptionExcludeRules(ruleIDs []string) func(s *Scanner) {
 		s.excludedRuleIDs = ruleIDs
 	}
 }
+
+func OptionIgnoreCheckErrors(ignore bool) func(s *Scanner) {
+	return func(s *Scanner) {
+		s.ignoreCheckErrors = ignore
+	}
+}
diff --git a/internal/app/tfsec/scanner/scanner.go b/internal/app/tfsec/scanner/scanner.go
index d557d4974..7b9559358 100644
--- a/internal/app/tfsec/scanner/scanner.go
+++ b/internal/app/tfsec/scanner/scanner.go
@@ -21,14 +21,17 @@ import (
 
 // Scanner scans HCL blocks by running all registered rules against them
 type Scanner struct {
-	includePassed   bool
-	includeIgnored  bool
-	excludedRuleIDs []string
+	includePassed     bool
+	includeIgnored    bool
+	excludedRuleIDs   []string
+	ignoreCheckErrors bool
 }
 
 // New creates a new Scanner
 func New(options ...Option) *Scanner {
-	s := &Scanner{}
+	s := &Scanner{
+		ignoreCheckErrors: true,
+	}
 	for _, option := range options {
 		option(s)
 	}
@@ -62,7 +65,7 @@ func (scanner *Scanner) Scan(blocks []block.Block) []result.Result {
 			func(r *rule.Rule) {
 				if rule.IsRuleRequiredForBlock(r, checkBlock) {
 					debug.Log("Running rule for %s on %s.%s (%s)...", r.ID, checkBlock.Type(), checkBlock.FullName(), checkBlock.Range().Filename)
-					ruleResults := rule.CheckRule(r, checkBlock, context)
+					ruleResults := rule.CheckRule(r, checkBlock, context, scanner.ignoreCheckErrors)
 					if scanner.includePassed && ruleResults.All() == nil {
 						res := result.New(checkBlock).
 							WithRuleID(r.ID).
diff --git a/pkg/rule/check.go b/pkg/rule/check.go
index 959d2c281..2818839e5 100644
--- a/pkg/rule/check.go
+++ b/pkg/rule/check.go
@@ -17,13 +17,15 @@ import (
 )
 
 // CheckRule the provided HCL block against the rule
-func CheckRule(r *Rule, block block.Block, ctx *hclcontext.Context) result.Set {
-	defer func() {
-		if err := recover(); err != nil {
-			_, _ = fmt.Fprintf(os.Stderr, "WARNING: skipped %s due to error(s): %s\n", r.ID, err)
-			debug.Log("Stack trace for failed %s r:\n%s\n\n", r.ID, string(runtimeDebug.Stack()))
-		}
-	}()
+func CheckRule(r *Rule, block block.Block, ctx *hclcontext.Context, ignoreErrors bool) result.Set {
+	if ignoreErrors {
+		defer func() {
+			if err := recover(); err != nil {
+				_, _ = fmt.Fprintf(os.Stderr, "WARNING: skipped %s due to error(s): %s\n", r.ID, err)
+				debug.Log("Stack trace for failed %s r:\n%s\n\n", r.ID, string(runtimeDebug.Stack()))
+			}
+		}()
+	}
 
 	var links []string
 


Test patch:
diff --git a/internal/app/tfsec/custom/processing_test.go b/internal/app/tfsec/custom/processing_test.go
index 6f4ea03b7..cc5c86de6 100644
--- a/internal/app/tfsec/custom/processing_test.go
+++ b/internal/app/tfsec/custom/processing_test.go
@@ -328,7 +328,7 @@ func scanTerraform(t *testing.T, mainTf string) []result.Result {
 	blocks, err := parser.New(dirName, parser.OptionStopOnHCLError()).ParseDirectory()
 	assert.NoError(t, err)
 
-	return scanner.New().Scan(blocks)
+	return scanner.New(scanner.OptionIgnoreCheckErrors(false)).Scan(blocks)
 }
 
 // This function is copied from setup_test.go as it is not possible to import function from test files.
diff --git a/internal/app/tfsec/test/module_scan_test.go b/internal/app/tfsec/test/module_scan_test.go
index a40a3789d..9e7c5a087 100644
--- a/internal/app/tfsec/test/module_scan_test.go
+++ b/internal/app/tfsec/test/module_scan_test.go
@@ -39,7 +39,7 @@ resource "problem" "uhoh" {
 			if err != nil {
 				t.Fatal(err)
 			}
-			results := scanner.New(scanner.OptionExcludeRules(excludedChecksList)).Scan(blocks)
+			results := scanner.New(scanner.OptionExcludeRules(excludedChecksList), scanner.OptionIgnoreCheckErrors(false)).Scan(blocks)
 			assertCheckCode(t, test.mustIncludeResultCode, test.mustExcludeResultCode, results)
 		})
 	}
diff --git a/internal/app/tfsec/test/setup_test.go b/internal/app/tfsec/test/setup_test.go
index 6efda1c36..867544150 100644
--- a/internal/app/tfsec/test/setup_test.go
+++ b/internal/app/tfsec/test/setup_test.go
@@ -64,12 +64,12 @@ resource "problem" "x" {
 
 func scanHCL(source string, t *testing.T) []result.Result {
 	blocks := createBlocksFromSource(source, ".tf", t)
-	return scanner.New(scanner.OptionExcludeRules(excludedChecksList)).Scan(blocks)
+	return scanner.New(scanner.OptionExcludeRules(excludedChecksList), scanner.OptionIgnoreCheckErrors(false)).Scan(blocks)
 }
 
 func scanJSON(source string, t *testing.T) []result.Result {
 	blocks := createBlocksFromSource(source, ".tf.json", t)
-	return scanner.New(scanner.OptionExcludeRules(excludedChecksList)).Scan(blocks)
+	return scanner.New(scanner.OptionExcludeRules(excludedChecksList), scanner.OptionIgnoreCheckErrors(false)).Scan(blocks)
 }
 
 func createBlocksFromSource(source string, ext string, t *testing.T) []block.Block {