Repository: aquasecurity/tfsec
Pull request #932 (2021-07-22 14:12:56)

add check for network flow log retention


Patch:
diff --git a/internal/app/tfsec/rules/azure/network/retention_policy_set_rule.go b/internal/app/tfsec/rules/azure/network/retention_policy_set_rule.go
new file mode 100644
index 000000000..9a780fb50
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/network/retention_policy_set_rule.go
@@ -0,0 +1,109 @@
+package network
+
+import (
+	"fmt"
+
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+
+	"github.com/aquasecurity/tfsec/pkg/provider"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/hclcontext"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+
+	"github.com/aquasecurity/tfsec/pkg/rule"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Service:   "network",
+		ShortCode: "retention-policy-set",
+		Documentation: rule.RuleDocumentation{
+			Summary: "Retention policy for flow logs should be enabled and set to greater than 90 days",
+			Explanation: `Flow logs are the source of truth for all network activity in your cloud environment. 
+To enable analysis in security event that was detected late, you need to have the logs available. 
+			
+Setting an retention policy will help ensure as much information is available for review.`,
+			Impact:     "Not enabling retention or having short expiry on flow logs could lead to compromise being undetected limiting time for analysis",
+			Resolution: "Ensure flow log retention is turned on with an expiry of >90 days",
+			BadExample: `
+resource "azurerm_network_watcher_flow_log" "bad_watcher" {
+  network_watcher_name = "bad_watcher"
+  resource_group_name  = "resource-group"
+
+  network_security_group_id = azurerm_network_security_group.test.id
+  storage_account_id        = azurerm_storage_account.test.id
+  enabled                   = true
+
+  retention_policy {
+    enabled = true
+    days    = 7
+  }
+}
+`,
+			GoodExample: `
+resource "azurerm_network_watcher_flow_log" "good_watcher" {
+  network_watcher_name = "good_watcher"
+  resource_group_name  = "resource-group"
+
+  network_security_group_id = azurerm_network_security_group.test.id
+  storage_account_id        = azurerm_storage_account.test.id
+  enabled                   = true
+
+  retention_policy {
+    enabled = true
+    days    = 90
+  }
+}
+`,
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_watcher_flow_log#retention_policy",
+				"https://docs.microsoft.com/en-us/azure/network-watcher/network-watcher-monitoring-overview",
+			},
+		},
+		Provider:        provider.AzureProvider,
+		RequiredTypes:   []string{"resource"},
+		RequiredLabels:  []string{"azurerm_network_watcher_flow_log"},
+		DefaultSeverity: severity.Low,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context) {
+
+			if resourceBlock.MissingChild("retention_policy") {
+				set.Add(
+					result.New(resourceBlock).WithDescription(fmt.Sprintf("Resource '%s' is missing the required retention policy block", resourceBlock.FullName())),
+				)
+				return
+			}
+
+			retentionPolicyBlock := resourceBlock.GetBlock("retention_policy")
+			if retentionPolicyBlock.MissingChild("enabled") || retentionPolicyBlock.MissingChild("days") {
+				set.Add(
+					result.New(resourceBlock).WithDescription(fmt.Sprintf("Resource '%s' is missing the required attributes retention policy block", resourceBlock.FullName())).
+						WithRange(retentionPolicyBlock.Range()),
+				)
+				return
+			}
+
+			enabledAttr := retentionPolicyBlock.GetAttribute("enabled")
+			daysAttr := retentionPolicyBlock.GetAttribute("days")
+
+			if enabledAttr.IsFalse() {
+				set.Add(
+					result.New(resourceBlock).WithDescription(fmt.Sprintf("Resource '%s' has retention policy turned off", resourceBlock.FullName())).
+						WithAttributeAnnotation(enabledAttr).
+						WithRange(enabledAttr.Range()),
+				)
+			}
+
+			if daysAttr.LessThan(90) {
+				set.Add(
+					result.New(resourceBlock).WithDescription(fmt.Sprintf("Resource '%s' has retention policy period of less than 90 days", resourceBlock.FullName())).
+						WithAttributeAnnotation(daysAttr).
+						WithRange(daysAttr.Range()),
+				)
+			}
+		},
+	})
+}


Test patch:
diff --git a/internal/app/tfsec/rules/azure/network/retention_policy_set_rule_test.go b/internal/app/tfsec/rules/azure/network/retention_policy_set_rule_test.go
new file mode 100644
index 000000000..93c8ec7ac
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/network/retention_policy_set_rule_test.go
@@ -0,0 +1,84 @@
+package network
+
+import (
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AzureRetentionPeriodSet(t *testing.T) {
+	expectedCode := "azure-network-retention-policy-set"
+
+	var tests = []struct {
+		name                  string
+		source                string
+		mustIncludeResultCode string
+		mustExcludeResultCode string
+	}{
+		{
+			name: "TODO: add test name",
+			source: `
+resource "azurerm_network_watcher_flow_log" "bad_watcher" {
+  network_watcher_name = "bad_watcher"
+  resource_group_name  = "resource-group"
+
+  network_security_group_id = azurerm_network_security_group.test.id
+  storage_account_id        = azurerm_storage_account.test.id
+  enabled                   = true
+
+  retention_policy {
+    enabled = true
+    days    = 7
+  }
+}
+`,
+			mustIncludeResultCode: expectedCode,
+		},
+		{
+			name: "TODO: add test name",
+			source: `
+resource "azurerm_network_watcher_flow_log" "bad_watcher" {
+  network_watcher_name = "bad_watcher"
+  resource_group_name  = "resource-group"
+
+  network_security_group_id = azurerm_network_security_group.test.id
+  storage_account_id        = azurerm_storage_account.test.id
+  enabled                   = true
+
+  retention_policy {
+    enabled = false
+    days    = 7
+  }
+}
+`,
+			mustIncludeResultCode: expectedCode,
+		},
+		{
+			name: "TODO: add test name",
+			source: `
+resource "azurerm_network_watcher_flow_log" "good_watcher" {
+  network_watcher_name = "good_watcher"
+  resource_group_name  = "resource-group"
+
+  network_security_group_id = azurerm_network_security_group.test.id
+  storage_account_id        = azurerm_storage_account.test.id
+  enabled                   = true
+
+  retention_policy {
+    enabled = true
+    days    = 90
+  }
+}
+`,
+			mustExcludeResultCode: expectedCode,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+
+			results := testutil.ScanHCL(test.source, t)
+			testutil.AssertCheckCode(t, test.mustIncludeResultCode, test.mustExcludeResultCode, results)
+		})
+	}
+}