Repository: aquasecurity/tfsec
Pull request #999 (2021-08-05 12:37:35)

Add KMS key rotation period check


Patch:
diff --git a/internal/app/tfsec/rules/google/kms/rotate_kms_keys_rule.go b/internal/app/tfsec/rules/google/kms/rotate_kms_keys_rule.go
new file mode 100644
index 000000000..33f438496
--- /dev/null
+++ b/internal/app/tfsec/rules/google/kms/rotate_kms_keys_rule.go
@@ -0,0 +1,97 @@
+package kms
+
+// ATTENTION!
+// This rule was autogenerated!
+// Before making changes, consider updating the generator.
+
+import (
+	"strconv"
+
+	"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.GoogleProvider,
+		Service:   "kms",
+		ShortCode: "rotate-kms-keys",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "KMS keys should be rotated at least every 90 days",
+			Explanation: `Keys should be rotated on a regular basis to limit exposure if a given key should become compromised.`,
+			Impact:      "Exposure is greater if the same keys are used over a long period",
+			Resolution:  "Set key rotation period to 90 days",
+			BadExample: []string{`
+resource "google_kms_key_ring" "keyring" {
+  name     = "keyring-example"
+  location = "global"
+}
+
+resource "google_kms_crypto_key" "example-key" {
+  name            = "crypto-key-example"
+  key_ring        = google_kms_key_ring.keyring.id
+  rotation_period = "15552000s"
+
+  lifecycle {
+    prevent_destroy = true
+  }
+}
+`},
+			GoodExample: []string{`
+resource "google_kms_key_ring" "keyring" {
+  name     = "keyring-example"
+  location = "global"
+}
+
+resource "google_kms_crypto_key" "example-key" {
+  name            = "crypto-key-example"
+  key_ring        = google_kms_key_ring.keyring.id
+  rotation_period = "7776000s"
+
+  lifecycle {
+    prevent_destroy = true
+  }
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/kms_crypto_key#rotation_period",
+			},
+		},
+		RequiredTypes: []string{
+			"resource",
+		},
+		RequiredLabels: []string{
+			"google_kms_crypto_key",
+		},
+		DefaultSeverity: severity.High,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context) {
+			rotationAttr := resourceBlock.GetAttribute("rotation_period")
+			if rotationAttr.IsNil() || (rotationAttr.IsResolvable() && rotationAttr.IsEmpty()) {
+				set.AddResult().
+					WithDescription("Resource '%s' does not have key rotation enabled.", resourceBlock)
+				return
+			}
+			if !rotationAttr.IsResolvable() || !rotationAttr.IsString() {
+				return
+			}
+
+			rotationStr := rotationAttr.Value().AsString()
+			if rotationStr[len(rotationStr)-1:] != "s" {
+				return
+			}
+			seconds, err := strconv.Atoi(rotationStr[:len(rotationStr)-1])
+			if err != nil {
+				return
+			}
+			if seconds > 7776000 {
+				set.AddResult().
+					WithDescription("Resource '%s' has a key rotation of greater than 90 days.", resourceBlock)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/init.go b/internal/app/tfsec/rules/init.go
index 087a585ef..260d7e13d 100644
--- a/internal/app/tfsec/rules/init.go
+++ b/internal/app/tfsec/rules/init.go
@@ -64,6 +64,7 @@ import (
 	_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/google/dns"
 	_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/google/gke"
 	_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/google/iam"
+	_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/google/kms"
 	_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/google/project"
 	_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/google/sql"
 	_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/google/storage"


Test patch:
diff --git a/internal/app/tfsec/rules/google/kms/rotate_kms_keys_rule_test.go b/internal/app/tfsec/rules/google/kms/rotate_kms_keys_rule_test.go
new file mode 100644
index 000000000..f867c6891
--- /dev/null
+++ b/internal/app/tfsec/rules/google/kms/rotate_kms_keys_rule_test.go
@@ -0,0 +1,53 @@
+package kms
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_GoogleRotateKmsKeys_FailureExamples(t *testing.T) {
+	expectedCode := "google-kms-rotate-kms-keys"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	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_GoogleRotateKmsKeys_SuccessExamples(t *testing.T) {
+	expectedCode := "google-kms-rotate-kms-keys"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	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)
+	}
+}