Repository: tfsec/tfsec
Pull request #500 (2020-11-08 21:26:29)

Rds performance insights encryption
Adding performance insights encryption checks from the previous PR that had failing tests and was in the old format


Patch:
diff --git a/internal/app/tfsec/checks/aws053.go b/internal/app/tfsec/checks/aws053.go
new file mode 100644
index 000000000..e73dcf98e
--- /dev/null
+++ b/internal/app/tfsec/checks/aws053.go
@@ -0,0 +1,66 @@
+package checks
+
+import (
+	"fmt"
+
+	"github.com/tfsec/tfsec/internal/app/tfsec/parser"
+	"github.com/tfsec/tfsec/internal/app/tfsec/scanner"
+)
+
+const AWSRDSPerformanceInsughtsEncryptionNotEnabled scanner.RuleCode = "AWS053"
+const AWSRDSPerformanceInsughtsEncryptionNotEnabledDescription scanner.RuleSummary = "Encryption for RDS Perfomance Insights should be enabled."
+const AWSRDSPerformanceInsughtsEncryptionNotEnabledExplanation = `
+When enabling Performance Insights on an RDS cluster or RDS DB Instance, and encryption key should be provided.
+
+The encryption key specified in ` + "`" + `performance_insights_kms_key_id` + "`" + ` references a KMS ARN
+`
+const AWSRDSPerformanceInsughtsEncryptionNotEnabledBadExample = `
+resource "aws_rds_cluster_instance" "foo" {
+  name                 = "bar"
+  performance_insights_enabled = true
+  performance_insights_kms_key_id = ""
+}
+`
+const AWSRDSPerformanceInsughtsEncryptionNotEnabledGoodExample = `
+resource "aws_rds_cluster_instance" "foo" {
+  name                 = "bar"
+  performance_insights_enabled = true
+  performance_insights_kms_key_id = "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
+}
+`
+
+func init() {
+	scanner.RegisterCheck(scanner.Check{
+		Code: AWSRDSPerformanceInsughtsEncryptionNotEnabled,
+		Documentation: scanner.CheckDocumentation{
+			Summary:     AWSRDSPerformanceInsughtsEncryptionNotEnabledDescription,
+			Explanation: AWSRDSPerformanceInsughtsEncryptionNotEnabledExplanation,
+			BadExample:  AWSRDSPerformanceInsughtsEncryptionNotEnabledBadExample,
+			GoodExample: AWSRDSPerformanceInsughtsEncryptionNotEnabledGoodExample,
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster_instance#performance_insights_kms_key_id",
+				"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#performance_insights_kms_key_id",
+				"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Encryption.htm",
+			},
+		},
+		Provider:       scanner.AWSProvider,
+		RequiredTypes:  []string{"resource"},
+		RequiredLabels: []string{"aws_rds_cluster_instance", "aws_db_instance"},
+		CheckFunc: func(check *scanner.Check, block *parser.Block, _ *scanner.Context) []scanner.Result {
+
+			if block.HasChild("performance_insights_enabled") && block.GetAttribute("performance_insights_enabled").IsTrue() {
+				if block.MissingChild("performance_insights_kms_key_id") || block.GetAttribute("performance_insights_kms_key_id").IsEmpty() {
+					return []scanner.Result{
+						check.NewResult(
+							fmt.Sprintf("Resource '%s' defines Performance Insights without encryption key specified.", block.FullName()),
+							block.Range(),
+							scanner.SeverityError,
+						),
+					}
+				}
+			}
+
+			return nil
+		},
+	})
+}
diff --git a/internal/app/tfsec/parser/block.go b/internal/app/tfsec/parser/block.go
index 739ca9404..8775a7af1 100644
--- a/internal/app/tfsec/parser/block.go
+++ b/internal/app/tfsec/parser/block.go
@@ -182,6 +182,10 @@ func (block *Block) HasChild(childElement string) bool {
 	return block.GetAttribute(childElement) != nil || block.GetBlock(childElement) != nil
 }
 
+func (block *Block) MissingChild(childElement string) bool {
+	return !block.HasChild(childElement)
+}
+
 func (block *Block) InModule() bool {
 	return block.moduleBlock != nil
 }


Test patch:
diff --git a/internal/app/tfsec/parser/block_test.go b/internal/app/tfsec/parser/block_test.go
index 86b62ca18..f419859d6 100644
--- a/internal/app/tfsec/parser/block_test.go
+++ b/internal/app/tfsec/parser/block_test.go
@@ -94,6 +94,44 @@ resource "aws_s3_bucket" "my-bucket" {
 	}
 }
 
+func Test_MissingChildNotFoundOnBlock(t *testing.T) {
+	var tests = []struct {
+		name              string
+		source            string
+		expectedAttribute string
+	}{
+		{
+			name: "expected attribute is not present",
+			source: `
+resource "aws_s3_bucket" "my-bucket" {
+ 	bucket_name = "bucketName"
+	
+}`,
+			expectedAttribute: "acl",
+		},
+		{
+			name: "expected acl attribute is not present",
+			source: `
+resource "aws_s3_bucket" "my-bucket" {
+ 	bucket_name = "bucketName"
+	acl = "public-read"
+	
+}`,
+			expectedAttribute: "logging",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			blocks := createBlocksFromSource(test.source)
+			for _, block := range blocks {
+				assert.Equal(t, block.MissingChild(test.expectedAttribute), true)
+				assert.Equal(t, !block.HasChild(test.expectedAttribute), true)
+			}
+		})
+	}
+}
+
 func createBlocksFromSource(source string) []*Block {
 	path := createTestFile("test.tf", source)
 	blocks, err := New(filepath.Dir(path), "").ParseDirectory()
diff --git a/internal/app/tfsec/test/aws053_test.go b/internal/app/tfsec/test/aws053_test.go
new file mode 100644
index 000000000..e8fa5e142
--- /dev/null
+++ b/internal/app/tfsec/test/aws053_test.go
@@ -0,0 +1,110 @@
+package test
+
+import (
+	"testing"
+
+	"github.com/tfsec/tfsec/internal/app/tfsec/checks"
+	"github.com/tfsec/tfsec/internal/app/tfsec/scanner"
+)
+
+func Test_AWSRDSPerformanceInsughtsEncryptionNotEnabled(t *testing.T) {
+
+	var tests = []struct {
+		name                  string
+		source                string
+		mustIncludeResultCode scanner.RuleCode
+		mustExcludeResultCode scanner.RuleCode
+	}{
+		{
+			name: "Performance insights enabled but no kms key provided",
+			source: `
+resource "aws_rds_cluster_instance" "foo" {
+  name                 = "bar"
+  performance_insights_enabled = true
+  performance_insights_kms_key_id = ""
+}
+`,
+			mustIncludeResultCode: checks.AWSRDSPerformanceInsughtsEncryptionNotEnabled,
+		},
+		{
+			name: "Performance insights disable",
+			source: `
+resource "aws_rds_cluster_instance" "foo" {
+  name                 = "bar"
+  performance_insights_enabled = false
+
+}
+`,
+			mustExcludeResultCode: checks.AWSRDSPerformanceInsughtsEncryptionNotEnabled,
+		},
+		{
+			name: "Performance insights not mentioned",
+			source: `
+resource "aws_rds_cluster_instance" "foo" {
+  
+}
+`,
+			mustExcludeResultCode: checks.AWSRDSPerformanceInsughtsEncryptionNotEnabled,
+		},
+		{
+			name: "Performance insights enabled and kms key provided",
+			source: `
+resource "aws_rds_cluster_instance" "foo" {
+  name                 = "bar"
+  performance_insights_enabled = true
+  performance_insights_kms_key_id = "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
+}
+`,
+			mustExcludeResultCode: checks.AWSRDSPerformanceInsughtsEncryptionNotEnabled,
+		},
+		{
+			name: "Performance insights on aws_db_instance enabled but no kms key provided",
+			source: `
+resource "aws_db_instance" "foo" {
+  name                 = "bar"
+  performance_insights_enabled = true
+  performance_insights_kms_key_id = ""
+}
+`,
+			mustIncludeResultCode: checks.AWSRDSPerformanceInsughtsEncryptionNotEnabled,
+		}, {
+			name: "Performance insights on aws_db_instance disable",
+			source: `
+resource "aws_db_instance" "foo" {
+  name                 = "bar"
+  performance_insights_enabled = false
+
+}
+`,
+			mustExcludeResultCode: checks.AWSRDSPerformanceInsughtsEncryptionNotEnabled,
+		},
+		{
+			name: "Performance insights on aws_db_instance not mentioned",
+			source: `
+resource "aws_db_instance" "foo" {
+  
+}
+`,
+			mustExcludeResultCode: checks.AWSRDSPerformanceInsughtsEncryptionNotEnabled,
+		},
+		{
+			name: "Performance insights enabled on aws_db_instance and kms key provided",
+			source: `
+resource "aws_db_instance" "foo" {
+  name                 = "bar"
+  performance_insights_enabled = true
+  performance_insights_kms_key_id = "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
+}
+`,
+			mustExcludeResultCode: checks.AWSRDSPerformanceInsughtsEncryptionNotEnabled,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			results := scanSource(test.source)
+			assertCheckCode(t, test.mustIncludeResultCode, test.mustExcludeResultCode, results)
+		})
+	}
+
+}