Repository: tfsec/tfsec
Pull request #448 (2020-10-17 16:47:13)

Verify example code
![](https://static.wixstatic.com/media/f885f6_11784370084847ddbbc1812b3535868c~mv2.jpg)


Patch:
diff --git a/internal/app/tfsec/checks/aws037.go b/internal/app/tfsec/checks/aws037.go
index f9d91219b..4d4ec5a7a 100644
--- a/internal/app/tfsec/checks/aws037.go
+++ b/internal/app/tfsec/checks/aws037.go
@@ -21,16 +21,16 @@ The account password policy should be set to prevent using any of the last five
 `
 	AWSIAMPasswordReusePreventionBadExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
+	# ...
 	password_reuse_prevention = 1
-	...
+	# ...
 }
 `
 	AWSIAMPasswordReusePreventionGoodExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
+	# ...
 	password_reuse_prevention = 5
-	...
+	# ...
 }
 `
 )
diff --git a/internal/app/tfsec/checks/aws038.go b/internal/app/tfsec/checks/aws038.go
index 0cee51927..1d016f26a 100644
--- a/internal/app/tfsec/checks/aws038.go
+++ b/internal/app/tfsec/checks/aws038.go
@@ -2,6 +2,7 @@ package checks
 
 import (
 	"fmt"
+
 	"github.com/zclconf/go-cty/cty"
 
 	"github.com/tfsec/tfsec/internal/app/tfsec/parser"
@@ -19,16 +20,16 @@ The account password policy should be set to expire passwords after 90 days or l
 `
 	AWSIAMPasswordExpiryBadExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
-	// max_password_age not set
-	...
+	# ...
+	# max_password_age not set
+	# ...
 }
 `
 	AWSIAMPasswordExpiryGoodExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
+	# ...
 	max_password_age = 90
-	...
+	# ...
 }
 `
 )
diff --git a/internal/app/tfsec/checks/aws039.go b/internal/app/tfsec/checks/aws039.go
index 566b91862..2b18c4c3f 100644
--- a/internal/app/tfsec/checks/aws039.go
+++ b/internal/app/tfsec/checks/aws039.go
@@ -2,6 +2,7 @@ package checks
 
 import (
 	"fmt"
+
 	"github.com/zclconf/go-cty/cty"
 
 	"github.com/tfsec/tfsec/internal/app/tfsec/parser"
@@ -19,16 +20,16 @@ The account password policy should be set to enforce minimum password length of
 `
 	AWSIAMPasswordMinimumLengthBadExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
-	// minimum_password_length not set
-	...
+	# ...
+	# minimum_password_length not set
+	# ...
 }
 `
 	AWSIAMPasswordMinimumLengthGoodExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
+	# ...
 	minimum_password_length = 14
-	...
+	# ...
 }
 `
 )
diff --git a/internal/app/tfsec/checks/aws040.go b/internal/app/tfsec/checks/aws040.go
index f4ce4d13c..ba85b24f5 100644
--- a/internal/app/tfsec/checks/aws040.go
+++ b/internal/app/tfsec/checks/aws040.go
@@ -2,6 +2,7 @@ package checks
 
 import (
 	"fmt"
+
 	"github.com/zclconf/go-cty/cty"
 
 	"github.com/tfsec/tfsec/internal/app/tfsec/parser"
@@ -17,16 +18,16 @@ IAM account password policies should ensure that passwords content including a s
 `
 	AWSIAMPasswordRequiresSymbolBadExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
-	// require_symbols not set
-	...
+	# ...
+	# require_symbols not set
+	# ...
 }
 `
 	AWSIAMPasswordRequiresSymbolGoodExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
+	# ...
 	require_symbols = true
-	...
+	# ...
 }
 `
 )
diff --git a/internal/app/tfsec/checks/aws041.go b/internal/app/tfsec/checks/aws041.go
index 2aac9b28d..881f09e55 100644
--- a/internal/app/tfsec/checks/aws041.go
+++ b/internal/app/tfsec/checks/aws041.go
@@ -2,6 +2,7 @@ package checks
 
 import (
 	"fmt"
+
 	"github.com/zclconf/go-cty/cty"
 
 	"github.com/tfsec/tfsec/internal/app/tfsec/parser"
@@ -17,16 +18,16 @@ IAM account password policies should ensure that passwords content including at
 `
 	AWSIAMPasswordRequiresNumberBadExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
-	// require_numbers not set
-	...
+	# ...
+	# require_numbers not set
+	# ...
 }
 `
 	AWSIAMPasswordRequiresNumberGoodExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
+	# ...
 	require_numbers = true
-	...
+	# ...
 }
 `
 )
diff --git a/internal/app/tfsec/checks/aws042.go b/internal/app/tfsec/checks/aws042.go
index f8a536333..be115338d 100644
--- a/internal/app/tfsec/checks/aws042.go
+++ b/internal/app/tfsec/checks/aws042.go
@@ -2,6 +2,7 @@ package checks
 
 import (
 	"fmt"
+
 	"github.com/zclconf/go-cty/cty"
 
 	"github.com/tfsec/tfsec/internal/app/tfsec/parser"
@@ -17,16 +18,16 @@ IAM account password policies should ensure that passwords content including at
 `
 	AWSIAMPasswordRequiresLowercaseCharacterBadExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
-	// require_lowercase_characters not set
-	...
+	# ...
+	# require_lowercase_characters not set
+	# ...
 }
 `
 	AWSIAMPasswordRequiresLowercaseCharacterGoodExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
+	# ...
 	require_lowercase_characters = true
-	...
+	# ...
 }
 `
 )
diff --git a/internal/app/tfsec/checks/aws043.go b/internal/app/tfsec/checks/aws043.go
index 198f5cfad..34a1238ec 100644
--- a/internal/app/tfsec/checks/aws043.go
+++ b/internal/app/tfsec/checks/aws043.go
@@ -2,6 +2,7 @@ package checks
 
 import (
 	"fmt"
+
 	"github.com/zclconf/go-cty/cty"
 
 	"github.com/tfsec/tfsec/internal/app/tfsec/parser"
@@ -18,16 +19,16 @@ IAM account password policies should ensure that passwords content including at
 `
 	AWSIAMPasswordRequiresUppercaseCharacterBadExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
-	// require_uppercase_characters not set
-	...
+	# ...
+	# require_uppercase_characters not set
+	# ...
 }
 `
 	AWSIAMPasswordRequiresUppercaseCharacterGoodExample = `
 resource "aws_iam_account_password_policy" "strict" {
-	...
+	# ...
 	require_uppercase_characters = true
-	...
+	# ...
 }
 `
 )
diff --git a/internal/app/tfsec/checks/azu002.go b/internal/app/tfsec/checks/azu002.go
index 9935522e0..a3b0eb7d9 100644
--- a/internal/app/tfsec/checks/azu002.go
+++ b/internal/app/tfsec/checks/azu002.go
@@ -2,9 +2,10 @@ package checks
 
 import (
 	"fmt"
-	"github.com/zclconf/go-cty/cty"
 	"strings"
 
+	"github.com/zclconf/go-cty/cty"
+
 	"github.com/tfsec/tfsec/internal/app/tfsec/parser"
 	"github.com/tfsec/tfsec/internal/app/tfsec/scanner"
 )
@@ -19,7 +20,7 @@ Where possible, segments should be broken into smaller subnets.
 const AzureOpenOutboundNetworkSecurityGroupRuleBadExample = `
 resource "azurerm_network_security_rule" "my-rule" {
 	direction = "Outbound"
-	source_address_prefix = "0.0.0.0/0"
+	destination_address_prefix = "0.0.0.0/0"
 	access = "Allow"
 }`
 const AzureOpenOutboundNetworkSecurityGroupRuleGoodExample = `
diff --git a/internal/app/tfsec/checks/gcp001.go b/internal/app/tfsec/checks/gcp001.go
index 0de0be594..2134d4a7f 100644
--- a/internal/app/tfsec/checks/gcp001.go
+++ b/internal/app/tfsec/checks/gcp001.go
@@ -19,9 +19,9 @@ To use the default offering of Google managed keys, do not include a <code>disk_
 `
 const GoogleUnencryptedDiskBadExample = `
 resource "google_compute_disk" "my-disk" {
-	... 
+	# ... 
 	disk_encryption_key {}
-	...
+	# ...
 }`
 const GoogleUnencryptedDiskGoodExample = `
 resource "google_compute_disk" "my-disk" {
diff --git a/internal/app/tfsec/checks/gcp002.go b/internal/app/tfsec/checks/gcp002.go
index b54f8d9ed..fc4a47177 100644
--- a/internal/app/tfsec/checks/gcp002.go
+++ b/internal/app/tfsec/checks/gcp002.go
@@ -17,9 +17,9 @@ When specifying an <code>encryption</code> block, by not including the optional
 `
 const GoogleUnencryptedStorageBucketBadExample = `
 resource "google_storage_bucket" "my-bucket" {
-	...
-	no encryption block specified
-	...
+	# ...
+	# no encryption block specified
+	# ...
 }`
 const GoogleUnencryptedStorageBucketGoodExample = `
 resource "google_storage_bucket" "my-bucket" {
diff --git a/internal/app/tfsec/checks/gcp005.go b/internal/app/tfsec/checks/gcp005.go
index 3da24d0e2..a8eb21e11 100644
--- a/internal/app/tfsec/checks/gcp005.go
+++ b/internal/app/tfsec/checks/gcp005.go
@@ -3,6 +3,8 @@ package checks
 import (
 	"fmt"
 
+	"github.com/zclconf/go-cty/cty"
+
 	"github.com/tfsec/tfsec/internal/app/tfsec/parser"
 	"github.com/tfsec/tfsec/internal/app/tfsec/scanner"
 )
@@ -22,9 +24,9 @@ resource "google_container_cluster" "gke" {
 `
 const GkeAbacEnabledGoodExample = `
 resource "google_container_cluster" "gke" {
-	...
-	enable_legacy_abac not set
-	...
+	# ...
+	# enable_legacy_abac not set
+	# ...
 }
 `
 
@@ -47,7 +49,7 @@ func init() {
 		CheckFunc: func(check *scanner.Check, block *parser.Block, _ *scanner.Context) []scanner.Result {
 
 			enable_legacy_abac := block.GetAttribute("enable_legacy_abac")
-			if enable_legacy_abac.Value().AsString() == "true" {
+			if enable_legacy_abac != nil && enable_legacy_abac.Value().Type() == cty.String && enable_legacy_abac.Value().AsString() == "true" {
 				return []scanner.Result{
 					check.NewResult(
 						fmt.Sprintf("Resource '%s' defines a cluster with ABAC enabled. Disable and rely on RBAC instead. ", block.Name()),
diff --git a/internal/app/tfsec/checks/gcp006.go b/internal/app/tfsec/checks/gcp006.go
index 57cdaa133..5d4b2eb21 100644
--- a/internal/app/tfsec/checks/gcp006.go
+++ b/internal/app/tfsec/checks/gcp006.go
@@ -23,13 +23,13 @@ const GkeNodeMetadataExposedBadExample = `
 resource "google_container_cluster" "gke" {
 	workload_metadata_config {
 		node_metadata = "EXPOSE"
-		}
+	}
 }`
 const GkeNodeMetadataExposedGoodExample = `
 resource "google_container_cluster" "gke" {
 	workload_metadata_config {
-		node_metadata = "UNSPECIFIED"
-		}
+		node_metadata = "SPECIFIED"
+	}
 }`
 
 func init() {
@@ -52,7 +52,8 @@ func init() {
 
 			nodeMetadata := block.GetBlock("workload_metadata_config").GetAttribute("node_metadata")
 
-			if nodeMetadata.Type() == cty.String && nodeMetadata.Value().AsString() == "EXPOSE" || nodeMetadata.Type() == cty.String && nodeMetadata.Value().AsString() == "UNSPECIFIED" {
+			if nodeMetadata != nil && nodeMetadata.Type() == cty.String &&
+				(nodeMetadata.Value().AsString() == "EXPOSE" || nodeMetadata.Value().AsString() == "UNSPECIFIED") {
 				return []scanner.Result{
 					check.NewResult(
 						fmt.Sprintf("Resource '%s' defines a cluster with node metadata exposed. node_metadata set to EXPOSE or UNSPECIFIED disables metadata concealment. ", block.Name()),


Test patch:
diff --git a/internal/app/tfsec/test/example_code_test.go b/internal/app/tfsec/test/example_code_test.go
new file mode 100644
index 000000000..43a638c88
--- /dev/null
+++ b/internal/app/tfsec/test/example_code_test.go
@@ -0,0 +1,33 @@
+package test
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/tfsec/tfsec/internal/app/tfsec/scanner"
+)
+
+func TestExampleCode(t *testing.T) {
+	for _, check := range scanner.GetRegisteredChecks() {
+
+		t.Run(fmt.Sprintf("Check 'good' example code for %s", check.Code), func(t *testing.T) {
+			if strings.TrimSpace(check.Documentation.GoodExample) == "" {
+				t.Skip("good example code not provided")
+				return
+			}
+			results := scanSource(check.Documentation.GoodExample)
+			assertCheckCode(t, "", check.Code, results)
+		})
+
+		t.Run(fmt.Sprintf("Check 'bad' example code for %s", check.Code), func(t *testing.T) {
+			if strings.TrimSpace(check.Documentation.BadExample) == "" {
+				t.Skip("bad example code not provided")
+				return
+			}
+			results := scanSource(check.Documentation.BadExample)
+			assertCheckCode(t, check.Code, "", results)
+		})
+
+	}
+}