Repository: aquasecurity/tfsec
Pull request #913 (2021-07-21 09:48:26)

2144 azurerm virtual machine
- Add check for secrets in the azurerm_virtual_machine
- Add check for sensitive in azure VM



Patch:
diff --git a/Makefile b/Makefile
index ed22ffc13..b2b90d4ab 100644
--- a/Makefile
+++ b/Makefile
@@ -69,5 +69,8 @@ clone-image:
 	./scripts/clone-images.sh
 
 .PHONY: end-to-end
-end-to-end:
+end-to-end: test
 	go run ./cmd/tfsec -s -p --force-all-dirs ./example
+
+.PHONY: pr-ready
+pr-ready: quality end-to-end
diff --git a/go.mod b/go.mod
index 272e4cd20..f16b053ee 100644
--- a/go.mod
+++ b/go.mod
@@ -20,7 +20,7 @@ require (
 	github.com/mitchellh/go-wordwrap v1.0.0 // indirect
 	github.com/olekukonko/tablewriter v0.0.5
 	github.com/owenrumney/go-sarif v1.0.11
-	github.com/owenrumney/squealer v0.2.26
+	github.com/owenrumney/squealer v0.2.28
 	github.com/rivo/uniseg v0.2.0 // indirect
 	github.com/spf13/cobra v1.2.1
 	github.com/stretchr/testify v1.7.0
diff --git a/go.sum b/go.sum
index 4cebdebdd..4075404d1 100644
--- a/go.sum
+++ b/go.sum
@@ -308,8 +308,8 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6
 github.com/owenrumney/go-sarif v1.0.10/go.mod h1:sgJM0ZaZ28jT8t8Iq3/mUCFBW9cX09EobIBXYOhiYBc=
 github.com/owenrumney/go-sarif v1.0.11 h1:7k4TLSi6h3vAozSECjO0arcQoeUNDMgvA7LDac95sJo=
 github.com/owenrumney/go-sarif v1.0.11/go.mod h1:hTBFbxU7GuVRUvwMx+eStp9M/Oun4xHCS3vqpPvket8=
-github.com/owenrumney/squealer v0.2.26 h1:U2sSbLvOoVd5u2ACd29SJDb+gNSYYp+oP9lbxEskLbE=
-github.com/owenrumney/squealer v0.2.26/go.mod h1:wwVPzhjiUBILIdDtnzGSEcapXczIj/tONP+ZJ49IhPY=
+github.com/owenrumney/squealer v0.2.28 h1:LYsqUHal+5QlANjbZ+h44SN5kIZSfHCWKUzBAS1KwB0=
+github.com/owenrumney/squealer v0.2.28/go.mod h1:wwVPzhjiUBILIdDtnzGSEcapXczIj/tONP+ZJ49IhPY=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
diff --git a/internal/app/tfsec/rules/azure/compute/no_secrets_in_custom_data_rule.go b/internal/app/tfsec/rules/azure/compute/no_secrets_in_custom_data_rule.go
new file mode 100644
index 000000000..8299e2054
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/compute/no_secrets_in_custom_data_rule.go
@@ -0,0 +1,97 @@
+package compute
+
+import (
+	"encoding/base64"
+	"fmt"
+
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+	"github.com/owenrumney/squealer/pkg/squealer"
+
+	"github.com/aquasecurity/tfsec/pkg/provider"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/debug"
+	"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-secrets-in-custom-data",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Ensure that no sensitive credentials are exposed in VM custom_data",
+			Explanation: `When creating Azure Virtual Machines, custom_data is used to pass start up information into the EC2 instance. This custom_dat must not contain access key credentials.`,
+			Impact:      "Sensitive credentials in custom_data can be leaked",
+			Resolution:  "Don't use sensitive credentials in the VM custom_data",
+			BadExample: `
+resource "azurerm_virtual_machine" "bad_example" {
+	name = "bad_example"
+	custom_data =<<EOF
+export DATABASE_PASSWORD=\"SomeSortOfPassword\"
+EOF
+}
+`,
+			GoodExample: `
+resource "azurerm_virtual_machine" "good_example" {
+	name = "good_example"
+	custom_data =<<EOF
+export GREETING="Hello there"
+EOF
+}
+`,
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine#custom_data",
+			},
+		},
+		Provider:        provider.AzureProvider,
+		RequiredTypes:   []string{"resource"},
+		RequiredLabels:  []string{"azurerm_virtual_machine", "azurerm_linux_virtual_machine", "azurerm_windows_virtual_machine"},
+		DefaultSeverity: severity.Medium,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ *hclcontext.Context) {
+
+			if resourceBlock.MissingChild("custom_data") {
+				return
+			}
+
+			customDataAttr := resourceBlock.GetAttribute("custom_data")
+
+			if resourceBlock.TypeLabel() == "azurerm_virtual_machine" {
+				for _, str := range customDataAttr.ValueAsStrings() {
+					if checkResult := checkStringForSensitive(str, resourceBlock); checkResult != nil {
+						checkResult.
+							WithRange(customDataAttr.Range()).
+							WithAttributeAnnotation(customDataAttr)
+						set.Add(checkResult)
+					}
+				}
+			} else if customDataAttr.IsResolvable() && customDataAttr.IsString() {
+				encoded, err := base64.RawStdEncoding.DecodeString(customDataAttr.Value().AsString())
+				if err != nil {
+					debug.Log("could not decode the base64 string in the terraform, trying with the string verbatim")
+					encoded = []byte(customDataAttr.Value().AsString())
+				}
+				if checkResult := checkStringForSensitive(string(encoded), resourceBlock); checkResult != nil {
+					checkResult.
+						WithRange(customDataAttr.Range()).
+						WithAttributeAnnotation(customDataAttr)
+					set.Add(checkResult)
+				}
+
+			}
+		},
+	})
+}
+
+func checkStringForSensitive(stringToCheck string, resourceBlock block.Block) *result.Result {
+	if scanResult := squealer.NewStringScanner().Scan(stringToCheck); scanResult.TransgressionFound {
+		return result.New(resourceBlock).
+			WithDescription(fmt.Sprintf("Resource '%s' has custom_data with sensitive data.", resourceBlock.FullName()))
+	}
+	return nil
+}
diff --git a/internal/app/tfsec/rules/general/secrets/sensitive_in_attribute_value_rule.go b/internal/app/tfsec/rules/general/secrets/sensitive_in_attribute_value_rule.go
index 451d367b4..4e717efbd 100644
--- a/internal/app/tfsec/rules/general/secrets/sensitive_in_attribute_value_rule.go
+++ b/internal/app/tfsec/rules/general/secrets/sensitive_in_attribute_value_rule.go
@@ -13,21 +13,20 @@ import (
 	"github.com/aquasecurity/tfsec/pkg/severity"
 )
 
-
 func init() {
 	scanner.RegisterCheckRule(rule.Rule{
-		LegacyID:   "GEN005",
+		LegacyID:  "GEN005",
 		Service:   "secrets",
 		ShortCode: "sensitive-in-attribute-value",
 		Documentation: rule.RuleDocumentation{
-			Summary:      "The attribute has potentially sensitive data, passwords, tokens or keys in it",
-			Explanation:  `
+			Summary: "The attribute has potentially sensitive data, passwords, tokens or keys in it",
+			Explanation: `
 Sensitive data stored in attributes can result in compromised data. Sensitive data should be passed in through secret variables
 
 `,
-			Impact:       "Sensitive credentials may be compromised",
-			Resolution:   "Check the code for vulnerabilities and move to variables",
-			BadExample:   `
+			Impact:     "Sensitive credentials may be compromised",
+			Resolution: "Check the code for vulnerabilities and move to variables",
+			BadExample: `
 resource "aws_instance" "bad_example" {
 	instance_type = "t2.small"
 
@@ -37,7 +36,7 @@ EOF
 
 }
 `,
-			GoodExample:  `
+			GoodExample: `
 variable "password" {
 	type = string
 }
@@ -46,7 +45,7 @@ resource "aws_instance" "good_instance" {
 	instance_type = "t2.small"
 
 	user_data = <<EOF
-		Password = var.password
+		export EDITOR=vimacs
 EOF
 
 }
diff --git a/vendor/github.com/owenrumney/squealer/pkg/config/default.go b/vendor/github.com/owenrumney/squealer/pkg/config/default.go
index be36e4e89..31108a590 100644
--- a/vendor/github.com/owenrumney/squealer/pkg/config/default.go
+++ b/vendor/github.com/owenrumney/squealer/pkg/config/default.go
@@ -40,7 +40,7 @@ func DefaultConfig() *Config {
 				Description: "Slack OAuth Token",
 			},
 			{
-				Rule:        `(?i)password\s?[:=]\s?".+"`,
+				Rule:        `(?im)password\s?[:=]\s?"?.+"?`,
 				Description: "Password literal text",
 			},
 		},
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 477787142..df7cbdcd2 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -124,7 +124,7 @@ github.com/olekukonko/tablewriter
 # github.com/owenrumney/go-sarif v1.0.11
 ## explicit
 github.com/owenrumney/go-sarif/sarif
-# github.com/owenrumney/squealer v0.2.26
+# github.com/owenrumney/squealer v0.2.28
 ## explicit
 github.com/owenrumney/squealer/internal/app/squealer/match
 github.com/owenrumney/squealer/internal/app/squealer/mertics


Test patch:
diff --git a/internal/app/tfsec/rules/azure/compute/no_secrets_in_custom_data_rule_test.go b/internal/app/tfsec/rules/azure/compute/no_secrets_in_custom_data_rule_test.go
new file mode 100644
index 000000000..2fd139c34
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/compute/no_secrets_in_custom_data_rule_test.go
@@ -0,0 +1,83 @@
+package compute
+
+import (
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AzureNoSecretsInCustomData(t *testing.T) {
+	expectedCode := "azure-compute-no-secrets-in-custom-data"
+
+	var tests = []struct {
+		name                  string
+		source                string
+		mustIncludeResultCode string
+		mustExcludeResultCode string
+	}{
+		{
+			name: "virtual machine with a password in the custom_data fails check",
+			source: `
+			resource "azurerm_virtual_machine" "bad_example" {
+				name = "bad_example"
+				custom_data =<<EOF
+DATABASE_PASSWORD=SomeSortOfPassword
+EOF
+			}
+`,
+			mustIncludeResultCode: expectedCode,
+		},
+		{
+			name: "virtual machine with a password in a string in the custom_data fails check",
+			source: `
+			resource "azurerm_virtual_machine" "bad_example" {
+				name = "bad_example"
+				custom_data =<<EOF
+DATABASE_PASSWORD="SomeSortOfPassword"
+EOF
+			}
+`,
+			mustIncludeResultCode: expectedCode,
+		},
+		{
+			name: "linux virtual machine with a password in a base64 encoded string in the custom_data fails check",
+			source: `
+	resource "azurerm_linux_virtual_machine" "bad_example" {
+		name = "bad_example"
+		custom_data = "ZXhwb3J0IERBVEFCQVNFX1BBU1NXT1JEPSJTb21lU29ydE9mUGFzc3dvcmQi"
+	}
+`,
+			mustIncludeResultCode: expectedCode,
+		},
+		{
+			name: "virtual machine with no sensitive information in custom_data passes check",
+			source: `
+resource "azurerm_virtual_machine" "god_example" {
+				name = "good_example"
+				custom_data =<<EOF
+GREETING_TEXT="Hello"
+EOF
+			}
+`,
+			mustExcludeResultCode: expectedCode,
+		},
+		{
+			name: "linux virtual machine with no sensitive information in base64 custom_data passes check",
+			source: `
+resource "azurerm_linux_virtual_machine" "god_example" {
+				name = "good_example"
+				custom_data = "ZXhwb3J0IEVESVRPUj12aW1hY3M=" 
+			}
+`,
+			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)
+		})
+	}
+}
diff --git a/internal/app/tfsec/rules/general/secrets/sensitive_in_attribute_value_rule_test.go b/internal/app/tfsec/rules/general/secrets/sensitive_in_attribute_value_rule_test.go
index 3c09a272e..17596827d 100644
--- a/internal/app/tfsec/rules/general/secrets/sensitive_in_attribute_value_rule_test.go
+++ b/internal/app/tfsec/rules/general/secrets/sensitive_in_attribute_value_rule_test.go
@@ -82,7 +82,7 @@ resource "aws_instance" "good_instance" {
 	instance_type = "t2.small"
 
 	user_data = <<EOF
-		Password = var.password
+		export EDITOR=vimacs
 EOF
 
 }