Repository: aquasecurity/tfsec
Pull request #936 (2021-07-22 15:58:59)

Add openstack for compute admin pass


Patch:
diff --git a/Makefile b/Makefile
index e04bf8a2a..596c591d2 100644
--- a/Makefile
+++ b/Makefile
@@ -52,7 +52,7 @@ vet:
 .PHONY: typos
 typos:
 	which codespell || pip install codespell
-	codespell -S vendor,funcs,.terraform --ignore-words .codespellignore -f
+	codespell -S vendor,funcs,.terraform,.git --ignore-words .codespellignore -f
 
 .PHONY: quality
 quality: cyclo vet
@@ -75,4 +75,4 @@ pr-lint:
 	go run ./cmd/tfsec-pr-lint
 
 .PHONY: pr-ready
-pr-ready: quality end-to-end pr-lint
+pr-ready: quality end-to-end pr-lint typos
diff --git a/internal/app/tfsec/rules/init.go b/internal/app/tfsec/rules/init.go
index 0edd5bfc1..06748b187 100644
--- a/internal/app/tfsec/rules/init.go
+++ b/internal/app/tfsec/rules/init.go
@@ -55,5 +55,6 @@ import (
 	_ "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/storage"
+	_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/openstack/compute"
 	_ "github.com/aquasecurity/tfsec/internal/app/tfsec/rules/oracle/compute"
 )
diff --git a/internal/app/tfsec/rules/openstack/compute/no_plaintext_password_rule.go b/internal/app/tfsec/rules/openstack/compute/no_plaintext_password_rule.go
new file mode 100644
index 000000000..1b4203754
--- /dev/null
+++ b/internal/app/tfsec/rules/openstack/compute/no_plaintext_password_rule.go
@@ -0,0 +1,80 @@
+package compute
+
+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:   "compute",
+		ShortCode: "no-plaintext-password",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "No plaintext password for compute instance",
+			Explanation: `Assigning a password to the compute instance using plaintext could lead to compromise; it would be preferable to use key-pairs as a login mechanism`,
+			Impact:      "Including a plaintext password could lead to compromised instance",
+			Resolution:  "Do not use plaintext passwords in terraform files",
+			BadExample: `
+resource "openstack_compute_instance_v2" "bad_example" {
+  name            = "basic"
+  image_id        = "ad091b52-742f-469e-8f3c-fd81cadf0743"
+  flavor_id       = "3"
+  admin_pass      = "N0tSoS3cretP4ssw0rd"
+  security_groups = ["default"]
+  user_data       = "#cloud-config\nhostname: instance_1.example.com\nfqdn: instance_1.example.com"
+
+  network {
+    name = "my_network"
+  }
+}`,
+			GoodExample: `
+resource "openstack_compute_instance_v2" "good_example" {
+  name            = "basic"
+  image_id        = "ad091b52-742f-469e-8f3c-fd81cadf0743"
+  flavor_id       = "3"
+  key_pair        = "my_key_pair_name"
+  security_groups = ["default"]
+  user_data       = "#cloud-config\nhostname: instance_1.example.com\nfqdn: instance_1.example.com"
+
+  network {
+    name = "my_network"
+  }
+}`,
+			Links: []string{
+				"https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/resources/compute_instance_v2#admin_pass",
+			},
+		},
+		Provider:        provider.OpenStackProvider,
+		RequiredTypes:   []string{"resource"},
+		RequiredLabels:  []string{"openstack_compute_instance_v2"},
+		DefaultSeverity: severity.Medium,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context) {
+
+			if resourceBlock.MissingChild("admin_pass") {
+				return
+			}
+
+			adminPassAttr := resourceBlock.GetAttribute("admin_pass")
+			if adminPassAttr.IsString() && !adminPassAttr.IsEmpty() {
+				set.Add(
+					result.New(resourceBlock).
+						WithDescription(fmt.Sprintf("Resource '%s' specifies a plain text password", resourceBlock.FullName())).
+						WithRange(adminPassAttr.Range()).
+						WithAttributeAnnotation(adminPassAttr),
+				)
+			}
+		},
+	})
+}


Test patch:
diff --git a/internal/app/tfsec/rules/openstack/compute/no_plaintext_password_rule_test.go b/internal/app/tfsec/rules/openstack/compute/no_plaintext_password_rule_test.go
new file mode 100644
index 000000000..7c783eb05
--- /dev/null
+++ b/internal/app/tfsec/rules/openstack/compute/no_plaintext_password_rule_test.go
@@ -0,0 +1,86 @@
+package compute
+
+import (
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_OpenStackNoPlaintextPassword(t *testing.T) {
+	expectedCode := "openstack-compute-no-plaintext-password"
+
+	var tests = []struct {
+		name                  string
+		source                string
+		mustIncludeResultCode string
+		mustExcludeResultCode string
+	}{
+		{
+			name: "compute instance with a plain text password fails check",
+			source: `
+resource "openstack_compute_instance_v2" "bad_example" {
+  name            = "basic"
+  image_id        = "ad091b52-742f-469e-8f3c-fd81cadf0743"
+  flavor_id       = "3"
+  admin_pass      = "N0tSoS3cretP4ssw0rd"
+  security_groups = ["default"]
+  user_data       = "#cloud-config\nhostname: instance_1.example.com\nfqdn: instance_1.example.com"
+
+  network {
+    name = "my_network"
+  }
+}
+`,
+			mustIncludeResultCode: expectedCode,
+		},
+		{
+			name: "compute instance with no admin_pass passes check",
+			source: `
+resource "openstack_compute_instance_v2" "good_example" {
+  name            = "basic"
+  image_id        = "ad091b52-742f-469e-8f3c-fd81cadf0743"
+  flavor_id       = "3"
+  key_pair        = "my_key_pair_name"
+  security_groups = ["default"]
+  user_data       = "#cloud-config\nhostname: instance_1.example.com\nfqdn: instance_1.example.com"
+
+  network {
+    name = "my_network"
+  }
+}
+`,
+			mustExcludeResultCode: expectedCode,
+		},
+
+		{
+			name: "compute instance with no admin_pass passes check",
+			source: `
+variable "password" {
+	type = string
+}
+
+resource "openstack_compute_instance_v2" "good_example" {
+  name            = "basic"
+  image_id        = "ad091b52-742f-469e-8f3c-fd81cadf0743"
+  flavor_id       = "3"
+  admin_pass      = var.password
+  security_groups = ["default"]
+  user_data       = "#cloud-config\nhostname: instance_1.example.com\nfqdn: instance_1.example.com"
+
+  network {
+    name = "my_network"
+  }
+}
+`,
+			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)
+		})
+	}
+}