Repository: aquasecurity/tfsec
Pull request #970 (2021-07-29 16:34:48)

First round of new checks


Patch:
diff --git a/cmd/tfsec-skeleton/requirements/requirements.go b/cmd/tfsec-skeleton/requirements/requirements.go
index 5dd47d2cd..bf24fde56 100644
--- a/cmd/tfsec-skeleton/requirements/requirements.go
+++ b/cmd/tfsec-skeleton/requirements/requirements.go
@@ -119,7 +119,7 @@ func buildStringSliceComparison(values []string, comparison Comparison) string {
 	case ComparisonAnyOf:
 		return fmt.Sprintf(`IsAny(%s)`, sprintGo(values))
 	case ComparisonNotAnyOf:
-		return fmt.Sprintf(`IsNone(%s)`, sprintGo(values))
+		return fmt.Sprintf(`IsNotAny(%s)`, sprintGo(values))
 	}
 	panic(fmt.Sprintf("Comparison '%s' not supported for string slice", comparison))
 }
diff --git a/internal/app/tfsec/block/attribute.go b/internal/app/tfsec/block/attribute.go
index 9148e1a35..628cdcaef 100644
--- a/internal/app/tfsec/block/attribute.go
+++ b/internal/app/tfsec/block/attribute.go
@@ -17,6 +17,7 @@ type Attribute interface {
 	NotEqual(checkValue interface{}, equalityOptions ...EqualityOption) bool
 	RegexMatches(pattern interface{}) bool
 	IsAny(options ...interface{}) bool
+	IsNotAny(options ...interface{}) bool
 	IsNone(options ...interface{}) bool
 	IsTrue() bool
 	IsFalse() bool
diff --git a/internal/app/tfsec/block/hclattribute.go b/internal/app/tfsec/block/hclattribute.go
index 17f1e0511..d5a43e251 100644
--- a/internal/app/tfsec/block/hclattribute.go
+++ b/internal/app/tfsec/block/hclattribute.go
@@ -321,6 +321,10 @@ func (attr *HCLAttribute) RegexMatches(pattern interface{}) bool {
 	return false
 }
 
+func (attr *HCLAttribute) IsNotAny(options ...interface{}) bool {
+	return !attr.IsAny(options...)
+}
+
 func (attr *HCLAttribute) IsAny(options ...interface{}) bool {
 	if attr == nil {
 		return false
diff --git a/internal/app/tfsec/rules/aws/apigateway/enable_cache_encryption_rule.go b/internal/app/tfsec/rules/aws/apigateway/enable_cache_encryption_rule.go
new file mode 100644
index 000000000..fd55da23f
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/apigateway/enable_cache_encryption_rule.go
@@ -0,0 +1,75 @@
+package apigateway
+
+// ATTENTION!
+// This rule was autogenerated!
+// Before making changes, consider updating the generator.
+
+import (
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/hclcontext"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/pkg/provider"
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/rule"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Provider:       provider.AWSProvider,
+		Service:   "apigateway",
+		ShortCode: "enable-cache-encryption",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "API Gateway must have cache enabled",
+			Explanation: `Method cache encryption ensures that any sensitive data in the cache is not vulnerable to compromise in the event of interception`,
+			Impact:      "Data stored in the cache that is unencrypted may be vulnerable to compromise",
+			Resolution:  "Enable cache encryption",
+			BadExample: []string{  `
+resource "aws_api_gateway_method_settings" "bad_example" {
+  rest_api_id = aws_api_gateway_rest_api.example.id
+  stage_name  = aws_api_gateway_stage.example.stage_name
+  method_path = "path1/GET"
+
+  settings {
+    metrics_enabled = true
+    logging_level   = "INFO"
+    cache_data_encrypted = false
+  }
+}
+`},
+			GoodExample: []string{ `
+resource "aws_api_gateway_method_settings" "good_example" {
+  rest_api_id = aws_api_gateway_rest_api.example.id
+  stage_name  = aws_api_gateway_stage.example.stage_name
+  method_path = "path1/GET"
+
+  settings {
+    metrics_enabled = true
+    logging_level   = "INFO"
+    cache_data_encrypted = true
+  }
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_method_settings#cache_data_encrypted",
+			},
+		},
+		RequiredTypes:  []string{ 
+			"resource",
+		},
+		RequiredLabels: []string{ 
+			"aws_api_gateway_method_settings",
+		},
+		DefaultSeverity: severity.Medium, 
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context){
+			if cacheDataEncryptedAttr := resourceBlock.GetBlock("settings").GetAttribute("cache_data_encrypted"); cacheDataEncryptedAttr.IsNil() { // alert on use of default value
+				set.AddResult().
+					WithDescription("Resource '%s' uses default value for settings.cache_data_encrypted", resourceBlock.FullName())
+			} else if cacheDataEncryptedAttr.IsFalse() {
+				set.AddResult().
+					WithDescription("Resource '%s' does not have settings.cache_data_encrypted set to true", resourceBlock.FullName()).
+					WithAttribute(cacheDataEncryptedAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/aws/apigateway/enable_tracing_rule.go b/internal/app/tfsec/rules/aws/apigateway/enable_tracing_rule.go
new file mode 100644
index 000000000..9b599f217
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/apigateway/enable_tracing_rule.go
@@ -0,0 +1,65 @@
+package apigateway
+
+// ATTENTION!
+// This rule was autogenerated!
+// Before making changes, consider updating the generator.
+
+import (
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/hclcontext"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/pkg/provider"
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/rule"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Provider:       provider.AWSProvider,
+		Service:   "api-gateway",
+		ShortCode: "enable-tracing",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "API Gateway must have X-Ray tracing enabled",
+			Explanation: `X-Ray tracing enables end-to-end debugging and analysis of all API Gateway HTTP requests.`,
+			Impact:      "WIthout full tracing enabled it is difficult to trace the flow of logs",
+			Resolution:  "Enable tracing",
+			BadExample: []string{  `
+resource "aws_api_gateway_stage" "bad_example" {
+  stage_name    = "prod"
+  rest_api_id   = aws_api_gateway_rest_api.test.id
+  deployment_id = aws_api_gateway_deployment.test.id
+  xray_tracing_enabled = false
+}
+`},
+			GoodExample: []string{ `
+resource "aws_api_gateway_stage" "good_example" {
+  stage_name    = "prod"
+  rest_api_id   = aws_api_gateway_rest_api.test.id
+  deployment_id = aws_api_gateway_deployment.test.id
+  xray_tracing_enabled = true
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_stage#xray_tracing_enabled",
+			},
+		},
+		RequiredTypes:  []string{ 
+			"resource",
+		},
+		RequiredLabels: []string{ 
+			"aws_api_gateway_stage",
+		},
+		DefaultSeverity: severity.Low, 
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context){
+			if xrayTracingEnabledAttr := resourceBlock.GetAttribute("xray_tracing_enabled"); xrayTracingEnabledAttr.IsNil() { // alert on use of default value
+				set.AddResult().
+					WithDescription("Resource '%s' uses default value for xray_tracing_enabled", resourceBlock.FullName())
+			} else if xrayTracingEnabledAttr.IsFalse() {
+				set.AddResult().
+					WithDescription("Resource '%s' does not have xray_tracing_enabled set to true", resourceBlock.FullName()).
+					WithAttribute(xrayTracingEnabledAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/aws/documentdb/enable_log_export_rule.go b/internal/app/tfsec/rules/aws/documentdb/enable_log_export_rule.go
new file mode 100644
index 000000000..f108ca86a
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/documentdb/enable_log_export_rule.go
@@ -0,0 +1,73 @@
+package documentdb
+
+// ATTENTION!
+// This rule was autogenerated!
+// Before making changes, consider updating the generator.
+
+import (
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/hclcontext"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/pkg/provider"
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/rule"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Provider:  provider.AWSProvider,
+		Service:   "documentdb",
+		ShortCode: "enable-log-export",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "DocumentDB logs export should be enabled",
+			Explanation: `Document DB does not have auditing by default. To ensure that you are able to accurately audit the usage of your DocumentDB cluster you should enable export logs.`,
+			Impact:      "Limited visibility of audit trail for changes to the DocumentDB",
+			Resolution:  "Enable export logs",
+			BadExample: []string{`
+resource "aws_docdb_cluster" "bad_example" {
+  cluster_identifier      = "my-docdb-cluster"
+  engine                  = "docdb"
+  master_username         = "foo"
+  master_password         = "mustbeeightchars"
+  backup_retention_period = 5
+  preferred_backup_window = "07:00-09:00"
+  skip_final_snapshot     = true
+  enabled_cloudwatch_logs_exports = "something"
+}
+`},
+			GoodExample: []string{`
+resource "aws_docdb_cluster" "good_example" {
+  cluster_identifier      = "my-docdb-cluster"
+  engine                  = "docdb"
+  master_username         = "foo"
+  master_password         = "mustbeeightchars"
+  backup_retention_period = 5
+  preferred_backup_window = "07:00-09:00"
+  skip_final_snapshot     = true
+  enabled_cloudwatch_logs_exports = "audit"
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/docdb_cluster#enabled_cloudwatch_logs_exports",
+			},
+		},
+		RequiredTypes: []string{
+			"resource",
+		},
+		RequiredLabels: []string{
+			"aws_docdb_cluster",
+		},
+		DefaultSeverity: severity.Medium,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context) {
+			if enabledCloudwatchLogsExportsAttr := resourceBlock.GetAttribute("enabled_cloudwatch_logs_exports"); enabledCloudwatchLogsExportsAttr.IsNil() { // alert on use of default value
+				set.AddResult().
+					WithDescription("Resource '%s' uses default value for enabled_cloudwatch_logs_exports", resourceBlock.FullName())
+			} else if enabledCloudwatchLogsExportsAttr.IsNotAny("audit", "profiler") {
+				set.AddResult().
+					WithDescription("Resource '%s' does not have enabled_cloudwatch_logs_exports set to one of [audit profiler]", resourceBlock.FullName()).
+					WithAttribute(enabledCloudwatchLogsExportsAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/aws/documentdb/enable_storage_encryption_rule.go b/internal/app/tfsec/rules/aws/documentdb/enable_storage_encryption_rule.go
new file mode 100644
index 000000000..4e400515c
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/documentdb/enable_storage_encryption_rule.go
@@ -0,0 +1,74 @@
+package documentdb
+
+// ATTENTION!
+// This rule was autogenerated!
+// Before making changes, consider updating the generator.
+
+import (
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/hclcontext"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/pkg/provider"
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/rule"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Provider:       provider.AWSProvider,
+		Service:   "documentdb",
+		ShortCode: "enable-storage-encryption",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "DocumentDB storage must be encrypted",
+			Explanation: `Encryption of the underlying storage used by DocumentDB ensures that if their is compromise of the disks, the data is still protected.`,
+			Impact:      "Unencrypted sensitive data is vulnerable to compromise.",
+			Resolution:  "Enable storage encryption",
+			BadExample: []string{  `
+resource "aws_docdb_cluster" "bad_example" {
+  cluster_identifier      = "my-docdb-cluster"
+  engine                  = "docdb"
+  master_username         = "foo"
+  master_password         = "mustbeeightchars"
+  backup_retention_period = 5
+  preferred_backup_window = "07:00-09:00"
+  skip_final_snapshot     = true
+  storage_encrypted = false
+}
+`},
+			GoodExample: []string{ `
+resource "aws_docdb_cluster" "good_example" {
+  cluster_identifier      = "my-docdb-cluster"
+  engine                  = "docdb"
+  master_username         = "foo"
+  master_password         = "mustbeeightchars"
+  backup_retention_period = 5
+  preferred_backup_window = "07:00-09:00"
+  skip_final_snapshot     = true
+  storage_encrypted = true
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/docdb_cluster#storage_encrypted",
+			},
+		},
+		RequiredTypes:  []string{ 
+			"resource",
+		},
+		RequiredLabels: []string{ 
+			"aws_docdb_cluster",
+			"aws_docdb_cluster_instance",
+		},
+		DefaultSeverity: severity.High, 
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context){
+			if storageEncryptedAttr := resourceBlock.GetAttribute("storage_encrypted"); storageEncryptedAttr.IsNil() { // alert on use of default value
+				set.AddResult().
+					WithDescription("Resource '%s' uses default value for storage_encrypted", resourceBlock.FullName())
+			} else if storageEncryptedAttr.IsFalse() {
+				set.AddResult().
+					WithDescription("Resource '%s' does not have storage_encrypted set to true", resourceBlock.FullName()).
+					WithAttribute(storageEncryptedAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/aws/ebs/enable_volume_encryption_rule.go b/internal/app/tfsec/rules/aws/ebs/enable_volume_encryption_rule.go
new file mode 100644
index 000000000..614adf38e
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/ebs/enable_volume_encryption_rule.go
@@ -0,0 +1,71 @@
+package ebs
+
+// ATTENTION!
+// This rule was autogenerated!
+// Before making changes, consider updating the generator.
+
+import (
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/hclcontext"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/pkg/provider"
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/rule"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Provider:       provider.AWSProvider,
+		Service:   "ebs",
+		ShortCode: "enable-volume-encryption",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "EBS volumes must be encrypted",
+			Explanation: `By enabling encryption on EBS volumes you protect the volume, the disk I/O and any derived snapshots from compromise if intercepted.`,
+			Impact:      "Unencrypted sensitive data is vulnerable to compromise.",
+			Resolution:  "Enable encryption of EBS volumes",
+			BadExample: []string{  `
+resource "aws_ebs_volume" "bad_example" {
+  availability_zone = "us-west-2a"
+  size              = 40
+
+  tags = {
+    Name = "HelloWorld"
+  }
+  encrypted = false
+}
+`},
+			GoodExample: []string{ `
+resource "aws_ebs_volume" "good_example" {
+  availability_zone = "us-west-2a"
+  size              = 40
+
+  tags = {
+    Name = "HelloWorld"
+  }
+  encrypted = true
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ebs_volume#encrypted",
+			},
+		},
+		RequiredTypes:  []string{ 
+			"resource",
+		},
+		RequiredLabels: []string{ 
+			"aws_ebs_volume",
+		},
+		DefaultSeverity: severity.High, 
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context){
+			if encryptedAttr := resourceBlock.GetAttribute("encrypted"); encryptedAttr.IsNil() { // alert on use of default value
+				set.AddResult().
+					WithDescription("Resource '%s' uses default value for encrypted", resourceBlock.FullName())
+			} else if encryptedAttr.IsFalse() {
+				set.AddResult().
+					WithDescription("Resource '%s' does not have encrypted set to true", resourceBlock.FullName()).
+					WithAttribute(encryptedAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/aws/elasticache/add_description_for_security_group_rule.go b/internal/app/tfsec/rules/aws/elasticache/add_description_for_security_group_rule.go
new file mode 100644
index 000000000..9e80670c0
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/elasticache/add_description_for_security_group_rule.go
@@ -0,0 +1,73 @@
+package elasticache
+
+// ATTENTION!
+// This rule was autogenerated!
+// Before making changes, consider updating the generator.
+
+import (
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/hclcontext"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/pkg/provider"
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/rule"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Provider:       provider.AWSProvider,
+		Service:   "elasticache",
+		ShortCode: "add-description-for-security-group",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Missing description for security group/security group rule.",
+			Explanation: `Security groups and security group rules should include a description for auditing purposes.
+
+Simplifies auditing, debugging, and managing security groups.`,
+			Impact:      "Descriptions provide context for the firewall rule reasons",
+			Resolution:  "Add descriptions for all security groups and rules",
+			BadExample: []string{  `
+resource "aws_security_group" "bar" {
+  name = "security-group"
+}
+
+resource "aws_elasticache_security_group" "bad_example" {
+  name                 = "elasticache-security-group"
+  security_group_names = [aws_security_group.bar.name]
+  description = ""
+}
+`},
+			GoodExample: []string{ `
+resource "aws_security_group" "bar" {
+  name = "security-group"
+}
+
+resource "aws_elasticache_security_group" "good_example" {
+  name                 = "elasticache-security-group"
+  security_group_names = [aws_security_group.bar.name]
+  description = "something"
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticache_security_group#description",
+			},
+		},
+		RequiredTypes:  []string{ 
+			"resource",
+		},
+		RequiredLabels: []string{ 
+			"aws_elasticache_security_group",
+		},
+		DefaultSeverity: severity.Low, 
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context){
+			if descriptionAttr := resourceBlock.GetAttribute("description"); descriptionAttr.IsNil() { // alert on use of default value
+				set.AddResult().
+					WithDescription("Resource '%s' uses default value for description", resourceBlock.FullName())
+			} else if descriptionAttr.IsEmpty() {
+				set.AddResult().
+					WithDescription("Resource '%s' has description set to ", resourceBlock.FullName()).
+					WithAttribute(descriptionAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/aws/lambda/enable_tracing_rule.go b/internal/app/tfsec/rules/aws/lambda/enable_tracing_rule.go
new file mode 100644
index 000000000..92eccf41c
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/lambda/enable_tracing_rule.go
@@ -0,0 +1,137 @@
+package lambda
+
+// ATTENTION!
+// This rule was autogenerated!
+// Before making changes, consider updating the generator.
+
+import (
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/hclcontext"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/pkg/provider"
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/rule"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Provider:       provider.AWSProvider,
+		Service:   "lambda",
+		ShortCode: "enable-tracing",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Lambda functions should have X-Ray tracing enabled",
+			Explanation: `X-Ray tracing enables end-to-end debugging and analysis of all function activity. This will allow for identifying bottlenecks, slow downs and timeouts.`,
+			Impact:      "WIthout full tracing enabled it is difficult to trace the flow of logs",
+			Resolution:  "Enable tracing",
+			BadExample: []string{  `
+resource "aws_iam_role" "iam_for_lambda" {
+  name = "iam_for_lambda"
+
+  assume_role_policy = <<EOF
+{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Action": "sts:AssumeRole",
+      "Principal": {
+        "Service": "lambda.amazonaws.com"
+      },
+      "Effect": "Allow",
+      "Sid": ""
+    }
+  ]
+}
+EOF
+}
+
+resource "aws_lambda_function" "bad_example" {
+  filename      = "lambda_function_payload.zip"
+  function_name = "lambda_function_name"
+  role          = aws_iam_role.iam_for_lambda.arn
+  handler       = "exports.test"
+
+  # The filebase64sha256() function is available in Terraform 0.11.12 and later
+  # For Terraform 0.11.11 and earlier, use the base64sha256() function and the file() function:
+  # source_code_hash = "${base64sha256(file("lambda_function_payload.zip"))}"
+  source_code_hash = filebase64sha256("lambda_function_payload.zip")
+
+  runtime = "nodejs12.x"
+
+  environment {
+    variables = {
+      foo = "bar"
+    }
+  }
+  tracing_config {
+    mode = ""
+  }
+}
+`},
+			GoodExample: []string{ `
+resource "aws_iam_role" "iam_for_lambda" {
+  name = "iam_for_lambda"
+
+  assume_role_policy = <<EOF
+{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Action": "sts:AssumeRole",
+      "Principal": {
+        "Service": "lambda.amazonaws.com"
+      },
+      "Effect": "Allow",
+      "Sid": ""
+    }
+  ]
+}
+EOF
+}
+
+resource "aws_lambda_function" "good_example" {
+  filename      = "lambda_function_payload.zip"
+  function_name = "lambda_function_name"
+  role          = aws_iam_role.iam_for_lambda.arn
+  handler       = "exports.test"
+
+  # The filebase64sha256() function is available in Terraform 0.11.12 and later
+  # For Terraform 0.11.11 and earlier, use the base64sha256() function and the file() function:
+  # source_code_hash = "${base64sha256(file("lambda_function_payload.zip"))}"
+  source_code_hash = filebase64sha256("lambda_function_payload.zip")
+
+  runtime = "nodejs12.x"
+
+  environment {
+    variables = {
+      foo = "bar"
+    }
+  }
+  tracing_config {
+    mode = "something"
+  }
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function#mode",
+			},
+		},
+		RequiredTypes:  []string{ 
+			"resource",
+		},
+		RequiredLabels: []string{ 
+			"aws_lambda_function",
+		},
+		DefaultSeverity: severity.Low, 
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context){
+			if modeAttr := resourceBlock.GetBlock("tracing_config").GetAttribute("mode"); modeAttr.IsNil() { // alert on use of default value
+				set.AddResult().
+					WithDescription("Resource '%s' uses default value for tracing_config.mode", resourceBlock.FullName())
+			} else if modeAttr.IsEmpty() {
+				set.AddResult().
+					WithDescription("Resource '%s' has tracing_config.mode set to ", resourceBlock.FullName()).
+					WithAttribute(modeAttr)
+			}
+		},
+	})
+}


Test patch:
diff --git a/internal/app/tfsec/rules/aws/apigateway/enable_cache_encryption_rule_test.go b/internal/app/tfsec/rules/aws/apigateway/enable_cache_encryption_rule_test.go
new file mode 100644
index 000000000..e0f09cd57
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/apigateway/enable_cache_encryption_rule_test.go
@@ -0,0 +1,53 @@
+package apigateway
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AWSEnableCacheEncryption_FailureExamples(t *testing.T) {
+	expectedCode := "aws-apigateway-enable-cache-encryption"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AWSEnableCacheEncryption_SuccessExamples(t *testing.T) {
+	expectedCode := "aws-apigateway-enable-cache-encryption"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}
diff --git a/internal/app/tfsec/rules/aws/apigateway/enable_tracing_rule_test.go b/internal/app/tfsec/rules/aws/apigateway/enable_tracing_rule_test.go
new file mode 100644
index 000000000..078d56ced
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/apigateway/enable_tracing_rule_test.go
@@ -0,0 +1,53 @@
+package apigateway
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AWSEnableTracing_FailureExamples(t *testing.T) {
+	expectedCode := "aws-api-gateway-enable-tracing"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AWSEnableTracing_SuccessExamples(t *testing.T) {
+	expectedCode := "aws-api-gateway-enable-tracing"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}
diff --git a/internal/app/tfsec/rules/aws/documentdb/enable_log_export_rule_test.go b/internal/app/tfsec/rules/aws/documentdb/enable_log_export_rule_test.go
new file mode 100644
index 000000000..27a2e0d50
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/documentdb/enable_log_export_rule_test.go
@@ -0,0 +1,53 @@
+package documentdb
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AWSEnableLogExport_FailureExamples(t *testing.T) {
+	expectedCode := "aws-documentdb-enable-log-export"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AWSEnableLogExport_SuccessExamples(t *testing.T) {
+	expectedCode := "aws-documentdb-enable-log-export"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}
diff --git a/internal/app/tfsec/rules/aws/documentdb/enable_storage_encryption_rule_test.go b/internal/app/tfsec/rules/aws/documentdb/enable_storage_encryption_rule_test.go
new file mode 100644
index 000000000..c603897ad
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/documentdb/enable_storage_encryption_rule_test.go
@@ -0,0 +1,53 @@
+package documentdb
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AWSEnableStorageEncryption_FailureExamples(t *testing.T) {
+	expectedCode := "aws-documentdb-enable-storage-encryption"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AWSEnableStorageEncryption_SuccessExamples(t *testing.T) {
+	expectedCode := "aws-documentdb-enable-storage-encryption"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}
diff --git a/internal/app/tfsec/rules/aws/ebs/enable_volume_encryption_rule_test.go b/internal/app/tfsec/rules/aws/ebs/enable_volume_encryption_rule_test.go
new file mode 100644
index 000000000..5f1bd0cef
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/ebs/enable_volume_encryption_rule_test.go
@@ -0,0 +1,53 @@
+package ebs
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AWSEnableVolumeEncryption_FailureExamples(t *testing.T) {
+	expectedCode := "aws-ebs-enable-volume-encryption"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AWSEnableVolumeEncryption_SuccessExamples(t *testing.T) {
+	expectedCode := "aws-ebs-enable-volume-encryption"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}
diff --git a/internal/app/tfsec/rules/aws/elasticache/add_description_for_security_group_rule_test.go b/internal/app/tfsec/rules/aws/elasticache/add_description_for_security_group_rule_test.go
new file mode 100644
index 000000000..17537b911
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/elasticache/add_description_for_security_group_rule_test.go
@@ -0,0 +1,53 @@
+package elasticache
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AWSAddDescriptionForSecurityGroup_FailureExamples(t *testing.T) {
+	expectedCode := "aws-elasticache-add-description-for-security-group"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AWSAddDescriptionForSecurityGroup_SuccessExamples(t *testing.T) {
+	expectedCode := "aws-elasticache-add-description-for-security-group"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}
diff --git a/internal/app/tfsec/rules/aws/lambda/enable_tracing_rule_test.go b/internal/app/tfsec/rules/aws/lambda/enable_tracing_rule_test.go
new file mode 100644
index 000000000..00f7e834a
--- /dev/null
+++ b/internal/app/tfsec/rules/aws/lambda/enable_tracing_rule_test.go
@@ -0,0 +1,53 @@
+package lambda
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AWSEnableTracing_FailureExamples(t *testing.T) {
+	expectedCode := "aws-lambda-enable-tracing"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AWSEnableTracing_SuccessExamples(t *testing.T) {
+	expectedCode := "aws-lambda-enable-tracing"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.FailNow()
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}