Repository: aquasecurity/tfsec
Pull request #998 (2021-08-05 11:14:14)

Add rules for google dns


Patch:
diff --git a/internal/app/tfsec/rules/google/dns/enable_dnssec_rule.go b/internal/app/tfsec/rules/google/dns/enable_dnssec_rule.go
new file mode 100644
index 000000000..57c1f5ad0
--- /dev/null
+++ b/internal/app/tfsec/rules/google/dns/enable_dnssec_rule.go
@@ -0,0 +1,83 @@
+package dns
+
+// ATTENTION!
+// This rule was autogenerated!
+// Before making changes, consider updating the generator.
+
+import (
+	"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:   "dns",
+		ShortCode: "enable-dnssec",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Cloud DNS should use DNSSEC",
+			Explanation: `DNSSEC authenticates DNS responses, preventing MITM attacks and impersonation.`,
+			Impact:      "Unverified DNS responses could lead to man-in-the-middle attacks",
+			Resolution:  "Enable DNSSEC",
+			BadExample: []string{  `
+resource "google_dns_managed_zone" "bad_example" {
+  name        = "example-zone"
+  dns_name    = "example-${random_id.rnd.hex}.com."
+  description = "Example DNS zone"
+  labels = {
+    foo = "bar"
+  }
+  dnssec_config {
+    state = "off"
+  }
+}
+
+resource "random_id" "rnd" {
+  byte_length = 4
+}
+`},
+			GoodExample: []string{ `
+resource "google_dns_managed_zone" "good_example" {
+  name        = "example-zone"
+  dns_name    = "example-${random_id.rnd.hex}.com."
+  description = "Example DNS zone"
+  labels = {
+    foo = "bar"
+  }
+  dnssec_config {
+    state = "on"
+  }
+}
+
+resource "random_id" "rnd" {
+  byte_length = 4
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone#state",
+			},
+		},
+		RequiredTypes:  []string{ 
+			"resource",
+		},
+		RequiredLabels: []string{ 
+			"google_dns_managed_zone",
+		},
+		DefaultSeverity: severity.Medium, 
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context){
+			if stateAttr := resourceBlock.GetBlock("dnssec_config").GetAttribute("state"); stateAttr.IsNil() { // alert on use of default value
+				set.AddResult().
+					WithDescription("Resource '%s' uses default value for dnssec_config.state", resourceBlock.FullName())
+			} else if stateAttr.NotEqual("on") {
+				set.AddResult().
+					WithDescription("Resource '%s' does not have dnssec_config.state set to on", resourceBlock.FullName()).
+					WithAttribute(stateAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/google/dns/no_rsa_sha1_rule.go b/internal/app/tfsec/rules/google/dns/no_rsa_sha1_rule.go
new file mode 100644
index 000000000..cb398d864
--- /dev/null
+++ b/internal/app/tfsec/rules/google/dns/no_rsa_sha1_rule.go
@@ -0,0 +1,97 @@
+package dns
+
+// ATTENTION!
+// This rule was autogenerated!
+// Before making changes, consider updating the generator.
+
+import (
+	"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:   "dns",
+		ShortCode: "no-rsa-sha1",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Zone signing should not use RSA SHA1",
+			Explanation: `RSA SHA1 is a weaker algorithm than SHA2-based algorithms such as RSA SHA256/512`,
+			Impact:      "Less secure encryption algorithm than others available",
+			Resolution:  "Use RSA SHA512",
+			BadExample: []string{`
+resource "google_dns_managed_zone" "foo" {
+	name     = "foobar"
+	dns_name = "foo.bar."
+	
+	dnssec_config {
+		state         = "on"
+		non_existence = "nsec3"
+	}
+}
+	
+data "google_dns_keys" "foo_dns_keys" {
+	managed_zone = google_dns_managed_zone.foo.id
+	zone_signing_keys {
+		algorithm = "rsasha1"
+	}
+}
+	
+output "foo_dns_ds_record" {
+	description = "DS record of the foo subdomain."
+	value       = data.google_dns_keys.foo_dns_keys.key_signing_keys[0].ds_record
+}
+`},
+			GoodExample: []string{`
+resource "google_dns_managed_zone" "foo" {
+	name     = "foobar"
+	dns_name = "foo.bar."
+	
+	dnssec_config {
+		state         = "on"
+		non_existence = "nsec3"
+	}
+}
+	
+data "google_dns_keys" "foo_dns_keys" {
+	managed_zone = google_dns_managed_zone.foo.id
+	zone_signing_keys {
+		algorithm = "rsasha512"
+	}
+}
+	
+output "foo_dns_ds_record" {
+	description = "DS record of the foo subdomain."
+	value       = data.google_dns_keys.foo_dns_keys.key_signing_keys[0].ds_record
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone#algorithm",
+			},
+		},
+		RequiredTypes: []string{
+			"data",
+		},
+		RequiredLabels: []string{
+			"google_dns_keys",
+		},
+		DefaultSeverity: severity.Medium,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context) {
+			if algorithmAttr := resourceBlock.GetBlock("zone_signing_keys").GetAttribute("algorithm"); algorithmAttr.Equals("rsasha1") {
+				set.AddResult().
+					WithDescription("Data '%s' has zone_signing_keys.algorithm set to rsasha1", resourceBlock.FullName()).
+					WithAttribute(algorithmAttr)
+			}
+			if algorithmAttr := resourceBlock.GetBlock("key_signing_keys").GetAttribute("algorithm"); algorithmAttr.Equals("rsasha1") {
+				set.AddResult().
+					WithDescription("Data '%s' has key_signing_keys.algorithm set to rsasha1", resourceBlock.FullName()).
+					WithAttribute(algorithmAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/init.go b/internal/app/tfsec/rules/init.go
index 20d2aadc6..087a585ef 100644
--- a/internal/app/tfsec/rules/init.go
+++ b/internal/app/tfsec/rules/init.go
@@ -61,6 +61,7 @@ import (
 	_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/github/repositories"
 	_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/google/bigquery"
 	_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/google/compute"
+	_ "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/project"


Test patch:
diff --git a/internal/app/tfsec/rules/google/dns/enable_dnssec_rule_test.go b/internal/app/tfsec/rules/google/dns/enable_dnssec_rule_test.go
new file mode 100644
index 000000000..f2f7bae6e
--- /dev/null
+++ b/internal/app/tfsec/rules/google/dns/enable_dnssec_rule_test.go
@@ -0,0 +1,53 @@
+package dns
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_GoogleEnableDnssec_FailureExamples(t *testing.T) {
+	expectedCode := "google-dns-enable-dnssec"
+
+	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_GoogleEnableDnssec_SuccessExamples(t *testing.T) {
+	expectedCode := "google-dns-enable-dnssec"
+
+	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)
+	}
+}
diff --git a/internal/app/tfsec/rules/google/dns/no_rsa_sha1_rule_test.go b/internal/app/tfsec/rules/google/dns/no_rsa_sha1_rule_test.go
new file mode 100644
index 000000000..ab4ca767c
--- /dev/null
+++ b/internal/app/tfsec/rules/google/dns/no_rsa_sha1_rule_test.go
@@ -0,0 +1,53 @@
+package dns
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_GoogleNoRsaSha1_FailureExamples(t *testing.T) {
+	expectedCode := "google-dns-no-rsa-sha1"
+
+	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_GoogleNoRsaSha1_SuccessExamples(t *testing.T) {
+	expectedCode := "google-dns-no-rsa-sha1"
+
+	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)
+	}
+}