Repository: aquasecurity/tfsec
Pull request #997 (2021-08-05 10:07:48)

Add more google compute rules


Patch:
diff --git a/internal/app/tfsec/rules/google/compute/enable_shielded_vm_rule.go b/internal/app/tfsec/rules/google/compute/enable_shielded_vm_rule.go
new file mode 100644
index 000000000..cd53e92b0
--- /dev/null
+++ b/internal/app/tfsec/rules/google/compute/enable_shielded_vm_rule.go
@@ -0,0 +1,151 @@
+package compute
+
+// 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:   "compute",
+		ShortCode: "enable-shielded-vm",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Instances should have Shielded VM enabled",
+			Explanation: `A Shielded VM is a VM with enhanced defences/detection for rootkits/bootkits.`,
+			Impact:      "Unable to detect rootkits",
+			Resolution:  "Enable Shielded VM",
+			BadExample: []string{`
+resource "google_compute_instance" "bad_example" {
+  name         = "test"
+  machine_type = "e2-medium"
+  zone         = "us-central1-a"
+
+  tags = ["foo", "bar"]
+
+  boot_disk {
+    initialize_params {
+      image = "debian-cloud/debian-9"
+    }
+  }
+
+  // Local SSD disk
+  scratch_disk {
+    interface = "SCSI"
+  }
+
+  shielded_instance_config {
+    enable_vtpm = false
+    enable_integrity_monitoring = false
+  }
+}
+`,
+				`
+resource "google_compute_instance" "bad_example" {
+  name         = "test"
+  machine_type = "e2-medium"
+  zone         = "us-central1-a"
+
+  tags = ["foo", "bar"]
+
+  boot_disk {
+    initialize_params {
+      image = "debian-cloud/debian-9"
+    }
+  }
+
+  // Local SSD disk
+  scratch_disk {
+    interface = "SCSI"
+  }
+
+  shielded_instance_config {
+    enable_vtpm = true
+    enable_integrity_monitoring = false
+  }
+}
+`,
+				`
+resource "google_compute_instance" "bad_example" {
+  name         = "test"
+  machine_type = "e2-medium"
+  zone         = "us-central1-a"
+
+  tags = ["foo", "bar"]
+
+  boot_disk {
+    initialize_params {
+      image = "debian-cloud/debian-9"
+    }
+  }
+
+  // Local SSD disk
+  scratch_disk {
+    interface = "SCSI"
+  }
+
+  shielded_instance_config {
+    enable_vtpm = false
+    enable_integrity_monitoring = true
+  }
+}
+`},
+			GoodExample: []string{`
+resource "google_compute_instance" "bad_example" {
+  name         = "test"
+  machine_type = "e2-medium"
+  zone         = "us-central1-a"
+
+  tags = ["foo", "bar"]
+
+  boot_disk {
+    initialize_params {
+      image = "debian-cloud/debian-9"
+    }
+  }
+
+  // Local SSD disk
+  scratch_disk {
+    interface = "SCSI"
+  }
+
+  shielded_instance_config {
+    enable_vtpm = true
+    enable_integrity_monitoring = true
+  }
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#enable_vtpm",
+			},
+		},
+		RequiredTypes: []string{
+			"resource",
+		},
+		RequiredLabels: []string{
+			"google_compute_instance",
+		},
+		DefaultSeverity: severity.Medium,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context) {
+			if enableVtpmAttr := resourceBlock.GetBlock("shielded_instance_config").GetAttribute("enable_vtpm"); enableVtpmAttr.IsFalse() {
+				set.AddResult().
+					WithDescription("Resource '%s' has shielded_instance_config.enable_vtpm set to false", resourceBlock.FullName()).
+					WithAttribute(enableVtpmAttr)
+			}
+			if enableIMAttr := resourceBlock.GetBlock("shielded_instance_config").GetAttribute("enable_integrity_monitoring"); enableIMAttr.IsFalse() {
+				set.AddResult().
+					WithDescription("Resource '%s' has shielded_instance_config.enable_integrity_monitoring set to false", resourceBlock.FullName()).
+					WithAttribute(enableIMAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/google/compute/no_default_service_account_rule.go b/internal/app/tfsec/rules/google/compute/no_default_service_account_rule.go
new file mode 100644
index 000000000..81fa77726
--- /dev/null
+++ b/internal/app/tfsec/rules/google/compute/no_default_service_account_rule.go
@@ -0,0 +1,118 @@
+package compute
+
+// 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:   "compute",
+		ShortCode: "no-default-service-account",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Instances should not use the default service account",
+			Explanation: `The default service account has full project access. Instances should instead be assigned the minimal access they need.`,
+			Impact:      "Instance has full access to the project",
+			Resolution:  "Remove use of default service account",
+			BadExample: []string{`
+resource "google_compute_instance" "default" {
+  name         = "test"
+  machine_type = "e2-medium"
+  zone         = "us-central1-a"
+
+  tags = ["foo", "bar"]
+
+  boot_disk {
+    initialize_params {
+      image = "debian-cloud/debian-9"
+    }
+  }
+
+  // Local SSD disk
+  scratch_disk {
+    interface = "SCSI"
+  }
+
+  service_account {
+    # Google recommends custom service accounts that have cloud-platform scope and permissions granted via IAM Roles.
+    email  = "1234567890-compute@developer.gserviceaccount.com"
+    scopes = ["cloud-platform"]
+  }
+}
+`},
+			GoodExample: []string{`
+resource "google_service_account" "default" {
+  account_id   = "service_account_id"
+  display_name = "Service Account"
+}
+
+resource "google_compute_instance" "default" {
+  name         = "test"
+  machine_type = "e2-medium"
+  zone         = "us-central1-a"
+
+  tags = ["foo", "bar"]
+
+  boot_disk {
+    initialize_params {
+      image = "debian-cloud/debian-9"
+    }
+  }
+
+  // Local SSD disk
+  scratch_disk {
+    interface = "SCSI"
+  }
+
+  network_interface {
+    network = "default"
+
+    access_config {
+      // Ephemeral IP
+    }
+  }
+
+  metadata = {
+    foo = "bar"
+  }
+
+  metadata_startup_script = "echo hi > /test.txt"
+
+  service_account {
+    # Google recommends custom service accounts that have cloud-platform scope and permissions granted via IAM Roles.
+    email  = google_service_account.default.email
+    scopes = ["cloud-platform"]
+  }
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#",
+			},
+		},
+		RequiredTypes: []string{
+			"resource",
+		},
+		RequiredLabels: []string{
+			"google_compute_instance",
+		},
+		DefaultSeverity: severity.Critical,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context) {
+			if emailAttr := resourceBlock.GetBlock("service_account").GetAttribute("email"); emailAttr.IsNil() || emailAttr.EndsWith("-compute@developer.gserviceaccount.com") {
+				set.AddResult().
+					WithDescription("Resource '%s' uses the default service account.", resourceBlock).
+					WithBlock(resourceBlock.GetBlock("service_account")).
+					WithAttribute(emailAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/google/compute/no_ip_forwarding_rule.go b/internal/app/tfsec/rules/google/compute/no_ip_forwarding_rule.go
new file mode 100644
index 000000000..997fafaad
--- /dev/null
+++ b/internal/app/tfsec/rules/google/compute/no_ip_forwarding_rule.go
@@ -0,0 +1,86 @@
+package compute
+
+// 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:   "compute",
+		ShortCode: "no-ip-forwarding",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Instances should not have IP forwarding enabled",
+			Explanation: `Disabling IP forwarding ensuresthe instance can only receive packets addressed to the instance and can only send packets with a source address of the instance.`,
+			Impact:      "Instance can send/receive packets without the explicit instance address",
+			Resolution:  "Disable IP forwarding",
+			BadExample: []string{`
+resource "google_compute_instance" "bad_example" {
+  name         = "test"
+  machine_type = "e2-medium"
+  zone         = "us-central1-a"
+
+  boot_disk {
+    initialize_params {
+      image = "debian-cloud/debian-9"
+    }
+  }
+
+  // Local SSD disk
+  scratch_disk {
+    interface = "SCSI"
+  }
+
+  can_ip_forward = true
+}
+`},
+			GoodExample: []string{`
+resource "google_compute_instance" "bad_example" {
+  name         = "test"
+  machine_type = "e2-medium"
+  zone         = "us-central1-a"
+
+  boot_disk {
+    initialize_params {
+      image = "debian-cloud/debian-9"
+    }
+  }
+
+  // Local SSD disk
+  scratch_disk {
+    interface = "SCSI"
+  }
+  
+  can_ip_forward = false
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#can_ip_forward",
+			},
+		},
+		RequiredTypes: []string{
+			"resource",
+		},
+		RequiredLabels: []string{
+			"google_compute_instance",
+		},
+		DefaultSeverity: severity.High,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context) {
+			if canIpForwardAttr := resourceBlock.GetAttribute("can_ip_forward"); canIpForwardAttr.IsTrue() {
+				set.AddResult().
+					WithDescription("Resource '%s' has can_ip_forward set to true", resourceBlock.FullName()).
+					WithAttribute(canIpForwardAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/google/compute/no_oslogin_override_rule.go b/internal/app/tfsec/rules/google/compute/no_oslogin_override_rule.go
new file mode 100644
index 000000000..acc741178
--- /dev/null
+++ b/internal/app/tfsec/rules/google/compute/no_oslogin_override_rule.go
@@ -0,0 +1,93 @@
+package compute
+
+// 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"
+	"github.com/zclconf/go-cty/cty"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Provider:  provider.GoogleProvider,
+		Service:   "compute",
+		ShortCode: "no-oslogin-override",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Instances should not override the project setting for OS Login",
+			Explanation: `OS Login automatically revokes the relevant SSH keys when an IAM user has their access revoked.`,
+			Impact:      "Access via SSH key cannot be revoked automatically when an IAM user is removed.",
+			Resolution:  "Enable OS Login at project level and remove instance-level overrides",
+			BadExample: []string{`
+resource "google_compute_instance" "default" {
+  name         = "test"
+  machine_type = "e2-medium"
+  zone         = "us-central1-a"
+
+  boot_disk {
+    initialize_params {
+      image = "debian-cloud/debian-9"
+    }
+  }
+
+  // Local SSD disk
+  scratch_disk {
+    interface = "SCSI"
+  }
+
+  metadata = {
+    enable-oslogin = false
+  }
+}
+`},
+			GoodExample: []string{`
+resource "google_compute_instance" "default" {
+  name         = "test"
+  machine_type = "e2-medium"
+  zone         = "us-central1-a"
+
+  boot_disk {
+    initialize_params {
+      image = "debian-cloud/debian-9"
+    }
+  }
+
+  // Local SSD disk
+  scratch_disk {
+    interface = "SCSI"
+  }
+
+  metadata = {
+  }
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#",
+			},
+		},
+		RequiredTypes: []string{
+			"resource",
+		},
+		RequiredLabels: []string{
+			"google_compute_instance",
+		},
+		DefaultSeverity: severity.Medium,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context) {
+			metadataAttr := resourceBlock.GetAttribute("metadata")
+			val := metadataAttr.MapValue("enable-oslogin")
+			if val.Type() == cty.Bool && val.False() {
+				set.AddResult().
+					WithDescription("Resource'%s' has OS Login disabled at instance-level", resourceBlock).
+					WithAttribute(metadataAttr)
+				return
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/google/compute/no_public_ip_rule.go b/internal/app/tfsec/rules/google/compute/no_public_ip_rule.go
new file mode 100644
index 000000000..367e131cf
--- /dev/null
+++ b/internal/app/tfsec/rules/google/compute/no_public_ip_rule.go
@@ -0,0 +1,98 @@
+package compute
+
+// 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:   "compute",
+		ShortCode: "no-public-ip",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Instances should not have public IP addresses",
+			Explanation: `Instances should not be publicly exposed to the internet`,
+			Impact:      "Direct exposure of an instance to the public internet",
+			Resolution:  "Remove public IP",
+			BadExample: []string{`
+resource "google_compute_instance" "bad_example" {
+  name         = "test"
+  machine_type = "e2-medium"
+  zone         = "us-central1-a"
+
+  tags = ["foo", "bar"]
+
+  boot_disk {
+    initialize_params {
+      image = "debian-cloud/debian-9"
+    }
+  }
+
+  // Local SSD disk
+  scratch_disk {
+    interface = "SCSI"
+  }
+
+  network_interface {
+    network = "default"
+
+    access_config {
+      // Ephemeral IP
+    }
+  }
+}
+`},
+			GoodExample: []string{`
+resource "google_compute_instance" "good_example" {
+  name         = "test"
+  machine_type = "e2-medium"
+  zone         = "us-central1-a"
+
+  tags = ["foo", "bar"]
+
+  boot_disk {
+    initialize_params {
+      image = "debian-cloud/debian-9"
+    }
+  }
+
+  // Local SSD disk
+  scratch_disk {
+    interface = "SCSI"
+  }
+
+  network_interface {
+    network = "default"
+  }
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#access_config",
+			},
+		},
+		RequiredTypes: []string{
+			"resource",
+		},
+		RequiredLabels: []string{
+			"google_compute_instance",
+		},
+		DefaultSeverity: severity.High,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context) {
+			if accessConfigBlock := resourceBlock.GetBlock("network_interface").GetBlock("access_config"); !accessConfigBlock.IsNil() {
+				set.AddResult().
+					WithDescription("Resource '%s' sets access_config", resourceBlock.FullName()).
+					WithBlock(accessConfigBlock)
+			}
+		},
+	})
+}
diff --git a/pkg/result/result.go b/pkg/result/result.go
index b27ab5708..7ed4b63a9 100644
--- a/pkg/result/result.go
+++ b/pkg/result/result.go
@@ -118,6 +118,9 @@ func (r *Result) WithLinks(links []string) *Result {
 }
 
 func (r *Result) WithBlock(block block.Block) *Result {
+	if block.IsNil() {
+		return r
+	}
 	r.blocks = append(r.blocks, block)
 	return r
 }


Test patch:
diff --git a/internal/app/tfsec/rules/google/compute/enable_shielded_vm_rule_test.go b/internal/app/tfsec/rules/google/compute/enable_shielded_vm_rule_test.go
new file mode 100644
index 000000000..ddc83eee1
--- /dev/null
+++ b/internal/app/tfsec/rules/google/compute/enable_shielded_vm_rule_test.go
@@ -0,0 +1,53 @@
+package compute
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_GoogleEnableShieldedVm_FailureExamples(t *testing.T) {
+	expectedCode := "google-compute-enable-shielded-vm"
+
+	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_GoogleEnableShieldedVm_SuccessExamples(t *testing.T) {
+	expectedCode := "google-compute-enable-shielded-vm"
+
+	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/compute/no_default_service_account_rule_test.go b/internal/app/tfsec/rules/google/compute/no_default_service_account_rule_test.go
new file mode 100644
index 000000000..3bd738065
--- /dev/null
+++ b/internal/app/tfsec/rules/google/compute/no_default_service_account_rule_test.go
@@ -0,0 +1,53 @@
+package compute
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_GoogleNoDefaultServiceAccount_FailureExamples(t *testing.T) {
+	expectedCode := "google-compute-no-default-service-account"
+
+	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_GoogleNoDefaultServiceAccount_SuccessExamples(t *testing.T) {
+	expectedCode := "google-compute-no-default-service-account"
+
+	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/compute/no_ip_forwarding_rule_test.go b/internal/app/tfsec/rules/google/compute/no_ip_forwarding_rule_test.go
new file mode 100644
index 000000000..3a5c40ccf
--- /dev/null
+++ b/internal/app/tfsec/rules/google/compute/no_ip_forwarding_rule_test.go
@@ -0,0 +1,53 @@
+package compute
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_GoogleNoIpForwarding_FailureExamples(t *testing.T) {
+	expectedCode := "google-compute-no-ip-forwarding"
+
+	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_GoogleNoIpForwarding_SuccessExamples(t *testing.T) {
+	expectedCode := "google-compute-no-ip-forwarding"
+
+	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/compute/no_oslogin_override_rule_test.go b/internal/app/tfsec/rules/google/compute/no_oslogin_override_rule_test.go
new file mode 100644
index 000000000..a48db75b5
--- /dev/null
+++ b/internal/app/tfsec/rules/google/compute/no_oslogin_override_rule_test.go
@@ -0,0 +1,53 @@
+package compute
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_GoogleNoOsloginOverride_FailureExamples(t *testing.T) {
+	expectedCode := "google-compute-no-oslogin-override"
+
+	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_GoogleNoOsloginOverride_SuccessExamples(t *testing.T) {
+	expectedCode := "google-compute-no-oslogin-override"
+
+	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/compute/no_public_ip_rule_test.go b/internal/app/tfsec/rules/google/compute/no_public_ip_rule_test.go
new file mode 100644
index 000000000..9f975cc7f
--- /dev/null
+++ b/internal/app/tfsec/rules/google/compute/no_public_ip_rule_test.go
@@ -0,0 +1,53 @@
+package compute
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_GoogleNoPublicIp_FailureExamples(t *testing.T) {
+	expectedCode := "google-compute-no-public-ip"
+
+	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_GoogleNoPublicIp_SuccessExamples(t *testing.T) {
+	expectedCode := "google-compute-no-public-ip"
+
+	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)
+	}
+}