Repository: tfsec/tfsec
Pull request #685 (2021-04-09 21:12:27)

add new check - for ecr image tab immutability
fixed https://github.com/tfsec/tfsec/issues/196

based on: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository

Simple check.
- verify if attribute not missing (as default value is `MUTABLE`)
- verify if value of attribute set to `IMMUTABLE`



Patch:
diff --git a/internal/app/tfsec/checks/aws078.go b/internal/app/tfsec/checks/aws078.go
new file mode 100644
index 000000000..66b13358f
--- /dev/null
+++ b/internal/app/tfsec/checks/aws078.go
@@ -0,0 +1,79 @@
+package checks
+
+import (
+	"fmt"
+	"github.com/tfsec/tfsec/internal/app/tfsec/parser"
+	"github.com/tfsec/tfsec/internal/app/tfsec/scanner"
+)
+
+const AWSEnsureEcrImagesHaveImmutableTags scanner.RuleCode = "AWS078"
+const AWSEnsureEcrImagesHaveImmutableTagsDescription scanner.RuleSummary = "ECR images tags shouldn't be mutable."
+const AWSEnsureEcrImagesHaveImmutableTagsExplanation = `
+ECR images should be set to IMMUTABLE to prevent code injection through image mutation.
+
+This can be done by setting <code>image_tab_mutability</code> to <code>IMMUTABLE</code>
+`
+const AWSEnsureEcrImagesHaveImmutableTagsBadExample = `
+resource "aws_ecr_repository" "foo" {
+  name                 = "bar"
+  image_tag_mutability = "MUTABLE"
+
+  image_scanning_configuration {
+    scan_on_push = true
+  }
+}
+`
+const AWSEnsureEcrImagesHaveImmutableTagsGoodExample = `
+resource "aws_ecr_repository" "foo" {
+  name                 = "bar"
+  image_tag_mutability = "IMMUTABLE"
+
+  image_scanning_configuration {
+    scan_on_push = true
+  }
+}
+`
+
+func init() {
+	scanner.RegisterCheck(scanner.Check{
+		Code: AWSEnsureEcrImagesHaveImmutableTags,
+		Documentation: scanner.CheckDocumentation{
+			Summary:     AWSEnsureEcrImagesHaveImmutableTagsDescription,
+			Explanation: AWSEnsureEcrImagesHaveImmutableTagsExplanation,
+			BadExample:  AWSEnsureEcrImagesHaveImmutableTagsBadExample,
+			GoodExample: AWSEnsureEcrImagesHaveImmutableTagsGoodExample,
+			Links: []string{
+				"https://sysdig.com/blog/toctou-tag-mutability/",
+				"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository",
+			},
+		},
+		Provider:       scanner.AWSProvider,
+		RequiredTypes:  []string{"resource"},
+		RequiredLabels: []string{"aws_ecr_repository"},
+		CheckFunc: func(check *scanner.Check, block *parser.Block, _ *scanner.Context) []scanner.Result {
+
+			imageTagMutability := block.GetAttribute("image_tag_mutability")
+			if imageTagMutability == nil {
+				return []scanner.Result{
+					check.NewResult(
+						fmt.Sprintf("Resource '%s' is missing `image_tag_mutability` attribute - it is required to make ecr image tag immutable.", block.FullName()),
+						block.Range(),
+						scanner.SeverityError,
+					),
+				}
+			}
+
+			if !imageTagMutability.Equals("IMMUTABLE") {
+				return []scanner.Result{
+					check.NewResult(
+						fmt.Sprintf("Resource '%s' has `image_tag_mutability` attribute  not set to `IMMUTABLE`", block.FullName()),
+						imageTagMutability.Range(),
+						scanner.SeverityError,
+					),
+				}
+			}
+
+			return nil
+		},
+	})
+}


Test patch:
diff --git a/internal/app/tfsec/test/aws078_test.go b/internal/app/tfsec/test/aws078_test.go
new file mode 100644
index 000000000..62f09f68d
--- /dev/null
+++ b/internal/app/tfsec/test/aws078_test.go
@@ -0,0 +1,68 @@
+package test
+
+import (
+	"testing"
+
+	"github.com/tfsec/tfsec/internal/app/tfsec/checks"
+	"github.com/tfsec/tfsec/internal/app/tfsec/scanner"
+)
+
+func Test_AWSEnsureEcrImagesHaveImmutableTags(t *testing.T) {
+
+	var tests = []struct {
+		name                  string
+		source                string
+		mustIncludeResultCode scanner.RuleCode
+		mustExcludeResultCode scanner.RuleCode
+	}{
+		{
+			name: "should fire when image_tab_mutability attribute missing",
+			source: `
+resource "aws_ecr_repository" "foo" {
+  name                 = "bar"
+
+  image_scanning_configuration {
+    scan_on_push = true
+  }
+}
+`,
+			mustIncludeResultCode: checks.AWSEnsureEcrImagesHaveImmutableTags,
+		},
+		{
+			name: "should fire when image_tab_mutability not set to IMMUTABLE",
+			source: `
+resource "aws_ecr_repository" "foo" {
+  name                 = "bar"
+  image_tag_mutability = "MUTABLE"
+
+  image_scanning_configuration {
+    scan_on_push = true
+  }
+}
+`,
+			mustIncludeResultCode: checks.AWSEnsureEcrImagesHaveImmutableTags,
+		},
+		{
+			name: "should not fire when image_tab_mutability set to IMMUTABLE",
+			source: `
+resource "aws_ecr_repository" "foo" {
+  name                 = "bar"
+  image_tag_mutability = "IMMUTABLE"
+
+  image_scanning_configuration {
+    scan_on_push = true
+  }
+}
+`,
+			mustExcludeResultCode: checks.AWSEnsureEcrImagesHaveImmutableTags,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			results := scanSource(test.source)
+			assertCheckCode(t, test.mustIncludeResultCode, test.mustExcludeResultCode, results)
+		})
+	}
+
+}