Repository: liamg/tfsec
Pull request #80 (2020-01-25 23:18:56)

Adds check for outdated SSL/TLS usage in cloudfront
Checks for outdated SSL/TLS usage. 

**Background**
CloudFront supports a few different protocols and ciphers for communication between viewers and CloudFront. The supported security policies from CloudFront are are:

- SSLv3
- TLSv1
- TLSv1_2016
- TLSv1.1_2016
- TLSv1.2_2018

From a best practice perspective [AWS says](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/secure-connections-supported-viewer-protocols-ciphers.html) 
> We recommend that you specify TLSv1.2_2018 unless your viewers are using browsers or devices that don’t support TLSv1.2. 

This adds a check for that. 


Patch:
diff --git a/README.md b/README.md
index 8d2f78346..01ab0a76d 100644
--- a/README.md
+++ b/README.md
@@ -130,6 +130,7 @@ there are also checks which are provider agnostic.
 | AWS018  | aws      | Missing description for security group/security group rule.
 | AWS019  | aws      | A KMS key is not configured to auto-rotate
 | AWS020  | aws      | CloudFront distribution allows unencrypted (HTTP) communications.
+| AWS021  | aws      | CloudFront distribution uses outdated SSL/TSL protocols.
 | AZU001  | azurerm  | An inbound network security rule allows traffic from `/0`.
 | AZU002  | azurerm  | An outbound network security rule allows traffic to `/0`.
 | AZU003  | azurerm  | Unencrypted managed disk.
diff --git a/internal/app/tfsec/checks/aws_cloudfront_outdated_protocol.go b/internal/app/tfsec/checks/aws_cloudfront_outdated_protocol.go
new file mode 100644
index 000000000..b725c559b
--- /dev/null
+++ b/internal/app/tfsec/checks/aws_cloudfront_outdated_protocol.go
@@ -0,0 +1,51 @@
+package checks
+
+import (
+	"fmt"
+	"github.com/liamg/tfsec/internal/app/tfsec/parser"
+	"github.com/liamg/tfsec/internal/app/tfsec/scanner"
+	"github.com/zclconf/go-cty/cty"
+)
+
+// AWSCloudFrontOutdatedProtocol see https://github.com/liamg/tfsec#included-checks for check info
+const AWSCloudFrontOutdatedProtocol scanner.RuleID = "AWS021"
+
+func init() {
+	scanner.RegisterCheck(scanner.Check{
+		Code:           AWSCloudFrontOutdatedProtocol,
+		RequiredTypes:  []string{"resource"},
+		RequiredLabels: []string{"aws_cloudfront_distribution"},
+		CheckFunc: func(check *scanner.Check, block *parser.Block, context *scanner.Context) []scanner.Result {
+
+			viewerCertificateBlock := block.GetBlock("viewer_certificate")
+			if viewerCertificateBlock == nil {
+				return []scanner.Result{
+					check.NewResult(
+						fmt.Sprintf("Resource '%s' defines outdated SSL/TLS policies (missing viewer_certificate block)", block.Name()),
+						viewerCertificateBlock.Range(),
+						scanner.SeverityError,
+					),
+				}
+			}
+
+			if minVersion := viewerCertificateBlock.GetAttribute("minimum_protocol_version"); minVersion == nil {
+				return []scanner.Result{
+					check.NewResult(
+						fmt.Sprintf("Resource '%s' defines outdated SSL/TLS policies (missing minimum_protocol_version attribute)", block.Name()),
+						viewerCertificateBlock.Range(),
+						scanner.SeverityError,
+					),
+				}
+			} else if minVersion.Type() == cty.String && minVersion.Value().AsString() != "TLSv1.2_2018" {
+				return []scanner.Result{
+					check.NewResult(
+						fmt.Sprintf("Resource '%s' defines outdated SSL/TLS policies (not using TLSv1.2_2018)", block.Name()),
+						minVersion.Range(),
+						scanner.SeverityError,
+					),
+				}
+			}
+			return nil
+		},
+	})
+}


Test patch:
diff --git a/internal/app/tfsec/aws_cloudfront_outdated_protocol_test.go b/internal/app/tfsec/aws_cloudfront_outdated_protocol_test.go
new file mode 100644
index 000000000..ebd58c7a8
--- /dev/null
+++ b/internal/app/tfsec/aws_cloudfront_outdated_protocol_test.go
@@ -0,0 +1,53 @@
+package tfsec
+
+import (
+	"github.com/liamg/tfsec/internal/app/tfsec/checks"
+	"github.com/liamg/tfsec/internal/app/tfsec/scanner"
+	"testing"
+)
+
+func Test_AWSCloudFrontOutdatedProtocol(t *testing.T) {
+	var tests = []struct {
+		name                  string
+		source                string
+		mustIncludeResultCode scanner.RuleID
+		mustExcludeResultCode scanner.RuleID
+	}{
+		{
+			name: "check no viewer_certificate block in aws_cloudfront_distribution",
+			source: `
+resource "aws_cloudfront_distribution" "s3_distribution" {
+
+}`,
+			mustIncludeResultCode: checks.AWSCloudFrontOutdatedProtocol,
+		},
+		{
+			name: "check no default minimum_protocol_version attribute in viewer_certificate block",
+			source: `
+resource "aws_cloudfront_distribution" "s3_distribution" {
+  viewer_certificate {
+    cloudfront_default_certificate = true
+  }
+}`,
+			mustIncludeResultCode: checks.AWSCloudFrontOutdatedProtocol,
+		},
+		{
+			name: "check TLSv1.2_2018 not used",
+			source: `
+resource "aws_cloudfront_distribution" "s3_distribution" {
+  viewer_certificate {
+    cloudfront_default_certificate = true
+	minimum_protocol_version = "TLSv1.1_2016"
+  }
+}`,
+			mustIncludeResultCode: checks.AWSCloudFrontOutdatedProtocol,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			results := scanSource(test.source)
+			assertCheckCode(t, test.mustIncludeResultCode, test.mustExcludeResultCode, results)
+		})
+	}
+}