Repository: tfsec/tfsec
Pull request #691 (2021-04-16 20:20:07)

Added Azure Key Vault Purge Protection Check and Test AZU021.
Added Azure Key Vault Purge Protection Test.

https://docs.microsoft.com/en-us/azure/key-vault/general/soft-delete-overview#purge-protection


Patch:
diff --git a/internal/app/tfsec/checks/azu020.go b/internal/app/tfsec/checks/azu020.go
index 778a66d89..8b234a110 100644
--- a/internal/app/tfsec/checks/azu020.go
+++ b/internal/app/tfsec/checks/azu020.go
@@ -102,4 +102,4 @@ func init() {
 			return nil
 		},
 	})
-}
+}
\ No newline at end of file
diff --git a/internal/app/tfsec/checks/azu021.go b/internal/app/tfsec/checks/azu021.go
new file mode 100644
index 000000000..d681efe77
--- /dev/null
+++ b/internal/app/tfsec/checks/azu021.go
@@ -0,0 +1,74 @@
+package checks
+
+import (
+	"fmt"
+
+	"github.com/tfsec/tfsec/internal/app/tfsec/parser"
+	"github.com/tfsec/tfsec/internal/app/tfsec/scanner"
+)
+
+const AZUKeyVaultPurgeProtection scanner.RuleCode = "AZU021"
+const AZUKeyVaultPurgeProtectionDescription scanner.RuleSummary = "Key vault should have purge protection enabled"
+const AZUKeyVaultPurgeProtectionExplanation = `
+Purge protection is an optional Key Vault behavior and is not enabled by default.
+
+Purge protection can only be enabled once soft-delete is enabled. It can be turned on via CLI or PowerShell.
+`
+const AZUKeyVaultPurgeProtectionBadExample = `
+resource "azurerm_key_vault" "bad_example" {
+    name                        = "examplekeyvault"
+    location                    = azurerm_resource_group.bad_example.location
+    enabled_for_disk_encryption = true
+    purge_protection_enabled    = false
+}
+`
+const AZUKeyVaultPurgeProtectionGoodExample = `
+resource "azurerm_key_vault" "good_example" {
+    name                        = "examplekeyvault"
+    location                    = azurerm_resource_group.good_example.location
+    enabled_for_disk_encryption = true
+    soft_delete_retention_days  = 7
+    purge_protection_enabled    = true
+}
+`
+
+func init() {
+	scanner.RegisterCheck(scanner.Check{
+		Code: AZUKeyVaultPurgeProtection,
+		Documentation: scanner.CheckDocumentation{
+			Summary:     AZUKeyVaultPurgeProtectionDescription,
+			Explanation: AZUKeyVaultPurgeProtectionExplanation,
+			BadExample:  AZUKeyVaultPurgeProtectionBadExample,
+			GoodExample: AZUKeyVaultPurgeProtectionGoodExample,
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault#purge_protection_enabled",
+				"https://docs.microsoft.com/en-us/azure/key-vault/general/soft-delete-overview#purge-protection",
+			},
+		},
+		Provider:       scanner.AzureProvider,
+		RequiredTypes:  []string{"resource"},
+		RequiredLabels: []string{"azurerm_key_vault"},
+		CheckFunc: func(check *scanner.Check, block *parser.Block, _ *scanner.Context) []scanner.Result {
+
+			if block.MissingChild("purge_protection_enabled") || block.GetAttribute("purge_protection_enabled").IsFalse() {
+				return []scanner.Result{
+					check.NewResult(
+						fmt.Sprintf("Resource '%s' should have purge protection enabled.", block.FullName()),
+						block.Range(),
+						scanner.SeverityWarning,
+					),
+				}
+			}
+			if block.GetAttribute("purge_protection_enabled").IsTrue() && (block.MissingChild("soft_delete_retention_days") || block.GetAttribute("soft_delete_retention_days").LessThan(1)) {
+				return []scanner.Result{
+					check.NewResult(
+						fmt.Sprintf("Resource '%s' should have soft_delete_retention_days set in order to enabled purge protection.", block.FullName()),
+						block.Range(),
+						scanner.SeverityWarning,
+					),
+				}
+			}
+			return nil
+		},
+	})
+}


Test patch:
diff --git a/internal/app/tfsec/test/azu021_test.go b/internal/app/tfsec/test/azu021_test.go
new file mode 100644
index 000000000..5949ea686
--- /dev/null
+++ b/internal/app/tfsec/test/azu021_test.go
@@ -0,0 +1,90 @@
+package test
+
+import (
+	"testing"
+
+	"github.com/tfsec/tfsec/internal/app/tfsec/checks"
+	"github.com/tfsec/tfsec/internal/app/tfsec/scanner"
+)
+
+func Test_AZUKeyVaultPurgeProtection(t *testing.T) {
+
+	var tests = []struct {
+		name                  string
+		source                string
+		mustIncludeResultCode scanner.RuleCode
+		mustExcludeResultCode scanner.RuleCode
+	}{
+		{
+			name: "check if purge_protection_enabled not set, check fails",
+			source: `
+resource "azurerm_key_vault" "bad_example" {
+    name                        = "examplekeyvault"
+    location                    = azurerm_resource_group.bad_example.location
+    enabled_for_disk_encryption = true
+    soft_delete_retention_days  = 7
+    purge_protection_enabled    = false
+}
+`,
+			mustIncludeResultCode: checks.AZUKeyVaultPurgeProtection,
+		},
+		{
+			name: "check if purge_protection_enabled is set, check passes",
+			source: `
+resource "azurerm_key_vault" "good_example" {
+    name                        = "examplekeyvault"
+    location                    = azurerm_resource_group.good_example.location
+    enabled_for_disk_encryption = true
+    soft_delete_retention_days  = 7
+    purge_protection_enabled    = true
+}
+`,
+			mustExcludeResultCode: checks.AZUKeyVaultPurgeProtection,
+		},
+		{
+			name: "check if purge_protection_enabled and soft_delete_retention_days is not set, check fails",
+			source: `
+resource "azurerm_key_vault" "bad_example" {
+    name                        = "examplekeyvault"
+    location                    = azurerm_resource_group.bad_example.location
+    enabled_for_disk_encryption = true
+    purge_protection_enabled    = false
+}
+`,
+			mustIncludeResultCode: checks.AZUKeyVaultPurgeProtection,
+		},
+		{
+			name: "check if purge_protection_enabled is set but soft_delete_retention_days is not set, check fails",
+			source: `
+resource "azurerm_key_vault" "bad_example" {
+    name                        = "examplekeyvault"
+    location                    = azurerm_resource_group.bad_example.location
+    enabled_for_disk_encryption = true
+    purge_protection_enabled    = true
+}
+`,
+			mustIncludeResultCode: checks.AZUKeyVaultPurgeProtection,
+		},
+		{
+			name: "check if purge_protection_enabled is set but soft_delete_retention_days is not set, check fails",
+			source: `
+resource "azurerm_key_vault" "bad_example" {
+    name                        = "examplekeyvault"
+    location                    = azurerm_resource_group.bad_example.location
+    enabled_for_disk_encryption = true
+	soft_delete_retention_days  = 0
+    purge_protection_enabled    = true
+}
+`,
+			mustIncludeResultCode: checks.AZUKeyVaultPurgeProtection,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			results := scanSource(test.source)
+			assertCheckCode(t, test.mustIncludeResultCode, test.mustExcludeResultCode, results)
+		})
+	}
+
+}