Repository: aquasecurity/tfsec (go)

Issue:
AWS Athena - convert tfsec checks to use defsec logic
Adapt AWS Athena databases and workgroups to Go structs based on #1194 instructions:

Remove tfsec logic from `internal/app/tfsec/rules/aws/athena/enable_at_rest_encryption_rule.go`
`internal/app/tfsec/rules/aws/athena/no_encryption_override_rule.go`

Create adapt functions to populate the Go structs in `internal/app/tfsec/adapter/aws/athena/adapt.go`

Pull request:
AWS Athena - adapt to use defsec
Create adapter.

Update test to expect alert when `result_configuration` block is missing. 

Closes #1260 

Patch:
diff --git a/internal/app/tfsec/adapter/aws/athena/adapt.go b/internal/app/tfsec/adapter/aws/athena/adapt.go
index d45f4fefa..e5b1f0adc 100644
--- a/internal/app/tfsec/adapter/aws/athena/adapt.go
+++ b/internal/app/tfsec/adapter/aws/athena/adapt.go
@@ -1,10 +1,89 @@
 package athena
 
 import (
+	"github.com/aquasecurity/defsec/types"
+
 	"github.com/aquasecurity/defsec/provider/aws/athena"
 	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
 )
 
 func Adapt(modules []block.Module) athena.Athena {
-	return athena.Athena{}
+	return athena.Athena{
+		Databases:  adaptDatabases(modules),
+		Workgroups: adaptWorkgroups(modules),
+	}
+}
+
+func adaptDatabases(modules []block.Module) []athena.Database {
+	var databases []athena.Database
+	for _, module := range modules {
+		for _, resource := range module.GetResourcesByType("aws_athena_database") {
+			databases = append(databases, adaptDatabase(resource))
+		}
+	}
+	return databases
+}
+
+func adaptWorkgroups(modules []block.Module) []athena.Workgroup {
+	var workgroups []athena.Workgroup
+	for _, module := range modules {
+		for _, resource := range module.GetResourcesByType("aws_athena_workgroup") {
+			workgroups = append(workgroups, adaptWorkgroup(resource))
+		}
+	}
+	return workgroups
+}
+
+func adaptDatabase(resource block.Block) athena.Database {
+	nameAttr := resource.GetAttribute("name")
+	nameVal := nameAttr.AsStringValueOrDefault("", resource)
+
+	encryptionOptionVal := types.StringDefault("", *resource.GetMetadata())
+
+	if resource.HasChild("encryption_configuration") {
+		encryptionConfigBlock := resource.GetBlock("encryption_configuration")
+		encryptionOptionAttr := encryptionConfigBlock.GetAttribute("encryption_option")
+		encryptionOptionVal = encryptionOptionAttr.AsStringValueOrDefault("", encryptionConfigBlock)
+	}
+
+	return athena.Database{
+		Metadata: *resource.GetMetadata(),
+		Name:     nameVal,
+		Encryption: athena.EncryptionConfiguration{
+			Type: encryptionOptionVal,
+		},
+	}
+}
+
+func adaptWorkgroup(resource block.Block) athena.Workgroup {
+	nameAttr := resource.GetAttribute("name")
+	nameVal := nameAttr.AsStringValueOrDefault("", resource)
+
+	enforceWGConfigVal := types.BoolDefault(false, *resource.GetMetadata())
+	encryptionOptionVal := types.StringDefault("", *resource.GetMetadata())
+
+	if resource.HasChild("configuration") {
+		configBlock := resource.GetBlock("configuration")
+
+		enforceWGConfigAttr := configBlock.GetAttribute("enforce_workgroup_configuration")
+		enforceWGConfigVal = enforceWGConfigAttr.AsBoolValueOrDefault(true, configBlock)
+
+		if configBlock.HasChild("result_configuration") {
+			resultConfigBlock := configBlock.GetBlock("result_configuration")
+			if resultConfigBlock.HasChild("encryption_configuration") {
+				encryptionConfigBlock := resultConfigBlock.GetBlock("encryption_configuration")
+				encryptionOptionAttr := encryptionConfigBlock.GetAttribute("encryption_option")
+				encryptionOptionVal = encryptionOptionAttr.AsStringValueOrDefault("", encryptionConfigBlock)
+			}
+		}
+	}
+
+	return athena.Workgroup{
+		Metadata: *resource.GetMetadata(),
+		Name:     nameVal,
+		Encryption: athena.EncryptionConfiguration{
+			Type: encryptionOptionVal,
+		},
+		EnforceConfiguration: enforceWGConfigVal,
+	}
 }
diff --git a/internal/app/tfsec/adapter/aws/elasticsearch/adapt.go b/internal/app/tfsec/adapter/aws/elasticsearch/adapt.go
index 904d70a91..15d4a03fc 100644
--- a/internal/app/tfsec/adapter/aws/elasticsearch/adapt.go
+++ b/internal/app/tfsec/adapter/aws/elasticsearch/adapt.go
@@ -2,9 +2,83 @@ package elasticsearch
 
 import (
 	"github.com/aquasecurity/defsec/provider/aws/elasticsearch"
+	"github.com/aquasecurity/defsec/types"
 	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
 )
 
 func Adapt(modules []block.Module) elasticsearch.Elasticsearch {
-	return elasticsearch.Elasticsearch{}
+	return elasticsearch.Elasticsearch{
+		Domains: adaptDomains(modules),
+	}
+}
+
+func adaptDomains(modules []block.Module) []elasticsearch.Domain {
+	var domains []elasticsearch.Domain
+	for _, module := range modules {
+		for _, resource := range module.GetResourcesByType("aws_elasticsearch_domain") {
+			domains = append(domains, adaptDomain(resource))
+		}
+	}
+	return domains
+}
+
+func adaptDomain(resource block.Block) elasticsearch.Domain {
+	nameAttr := resource.GetAttribute("domain_name")
+	nameVal := nameAttr.AsStringValueOrDefault("", resource)
+
+	auditEnabled := types.BoolDefault(false, *resource.GetMetadata())
+	transitEncryptionVal := types.BoolDefault(false, *resource.GetMetadata())
+	atRestEncryptionVal := types.BoolDefault(false, *resource.GetMetadata())
+	enforceHTTPSVal := types.BoolDefault(false, *resource.GetMetadata())
+	TLSPolicyVal := types.StringDefault("", *resource.GetMetadata())
+
+	if resource.HasChild("log_publishing_options") {
+		logOptionsBlock := resource.GetBlock("log_publishing_options")
+		enabledAttr := logOptionsBlock.GetAttribute("enabled")
+		enabledVal := enabledAttr.AsBoolValueOrDefault(true, logOptionsBlock)
+
+		logTypeAttr := logOptionsBlock.GetAttribute("log_type")
+		if logTypeAttr.Equals("AUDIT_LOGS") {
+			auditEnabled = enabledVal
+		}
+	}
+
+	if resource.HasChild("node_to_node_encryption") {
+		transitEncryptBlock := resource.GetBlock("node_to_node_encryption")
+		enabledAttr := transitEncryptBlock.GetAttribute("enabled")
+		transitEncryptionVal = enabledAttr.AsBoolValueOrDefault(false, transitEncryptBlock)
+	}
+
+	if resource.HasChild("encrypt_at_rest") {
+		atRestEncryptBlock := resource.GetBlock("encrypt_at_rest")
+		enabledAttr := atRestEncryptBlock.GetAttribute("enabled")
+		atRestEncryptionVal = enabledAttr.AsBoolValueOrDefault(false, atRestEncryptBlock)
+	}
+
+	if resource.HasChild("domain_endpoint_options") {
+		endpointBlock := resource.GetBlock("domain_endpoint_options")
+		enforceHTTPSAttr := endpointBlock.GetAttribute("enforce_https")
+		enforceHTTPSVal = enforceHTTPSAttr.AsBoolValueOrDefault(true, endpointBlock)
+
+		TLSPolicyAttr := endpointBlock.GetAttribute("tls_security_policy")
+		TLSPolicyVal = TLSPolicyAttr.AsStringValueOrDefault("", endpointBlock)
+	}
+
+	return elasticsearch.Domain{
+		Metadata:   *resource.GetMetadata(),
+		DomainName: nameVal,
+		LogPublishing: elasticsearch.LogPublishing{
+			AuditEnabled: auditEnabled,
+		},
+		TransitEncryption: elasticsearch.TransitEncryption{
+			Enabled: transitEncryptionVal,
+		},
+		AtRestEncryption: elasticsearch.AtRestEncryption{
+			Enabled: atRestEncryptionVal,
+		},
+		Endpoint: elasticsearch.Endpoint{
+			EnforceHTTPS: enforceHTTPSVal,
+			TLSPolicy:    TLSPolicyVal,
+		},
+	}
 }
diff --git a/internal/app/tfsec/rules/aws/athena/enable_at_rest_encryption_rule.go b/internal/app/tfsec/rules/aws/athena/enable_at_rest_encryption_rule.go
index 273881a89..15b25ac8b 100644
--- a/internal/app/tfsec/rules/aws/athena/enable_at_rest_encryption_rule.go
+++ b/internal/app/tfsec/rules/aws/athena/enable_at_rest_encryption_rule.go
@@ -1,11 +1,7 @@
 package athena
 
 import (
-	"strings"
-
-	"github.com/aquasecurity/defsec/rules"
 	"github.com/aquasecurity/defsec/rules/aws/athena"
-	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
 	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
 	"github.com/aquasecurity/tfsec/pkg/rule"
 )
@@ -68,24 +64,5 @@ func init() {
 		RequiredTypes:  []string{"resource"},
 		RequiredLabels: []string{"aws_athena_database", "aws_athena_workgroup"},
 		Base:           athena.CheckEnableAtRestEncryption,
-		CheckTerraform: func(resourceBlock block.Block, _ block.Module) (results rules.Results) {
-
-			if strings.EqualFold(resourceBlock.TypeLabel(), "aws_athena_workgroup") {
-				if !resourceBlock.HasChild("configuration") {
-					return
-				}
-				configBlock := resourceBlock.GetBlock("configuration")
-				if !configBlock.HasChild("result_configuration") {
-					return
-				}
-				resourceBlock = configBlock.GetBlock("result_configuration")
-			}
-
-			if resourceBlock.MissingChild("encryption_configuration") {
-				results.Add("Missing encryption configuration block.", resourceBlock)
-			}
-
-			return results
-		},
 	})
 }
diff --git a/internal/app/tfsec/rules/aws/athena/no_encryption_override_rule.go b/internal/app/tfsec/rules/aws/athena/no_encryption_override_rule.go
index daded3e5b..70d2817d8 100644
--- a/internal/app/tfsec/rules/aws/athena/no_encryption_override_rule.go
+++ b/internal/app/tfsec/rules/aws/athena/no_encryption_override_rule.go
@@ -1,9 +1,7 @@
 package athena
 
 import (
-	"github.com/aquasecurity/defsec/rules"
 	"github.com/aquasecurity/defsec/rules/aws/athena"
-	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
 	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
 	"github.com/aquasecurity/tfsec/pkg/rule"
 )
@@ -60,27 +58,5 @@ func init() {
 		RequiredTypes:  []string{"resource"},
 		RequiredLabels: []string{"aws_athena_workgroup"},
 		Base:           athena.CheckNoEncryptionOverride,
-		CheckTerraform: func(resourceBlock block.Block, _ block.Module) (results rules.Results) {
-
-			if resourceBlock.MissingChild("configuration") {
-				results.Add("Resource is missing the configuration block.", resourceBlock)
-				return
-			}
-
-			configBlock := resourceBlock.GetBlock("configuration")
-
-			configBlock.HasChild("enforce_workgroup_configuration")
-			enforceWorkgroupConfigAttr := configBlock.GetAttribute("enforce_workgroup_configuration")
-
-			if enforceWorkgroupConfigAttr.IsNil() {
-				return
-			}
-
-			if enforceWorkgroupConfigAttr.IsFalse() {
-				results.Add("Resource has enforce_workgroup_configuration set to false.", enforceWorkgroupConfigAttr)
-			}
-
-			return results
-		},
 	})
 }
diff --git a/internal/app/tfsec/rules/aws/elasticsearch/enable_domain_encryption_rule.go b/internal/app/tfsec/rules/aws/elasticsearch/enable_domain_encryption_rule.go
index f867ef1ca..64a932834 100644
--- a/internal/app/tfsec/rules/aws/elasticsearch/enable_domain_encryption_rule.go
+++ b/internal/app/tfsec/rules/aws/elasticsearch/enable_domain_encryption_rule.go
@@ -1,9 +1,7 @@
 package elasticsearch
 
 import (
-	"github.com/aquasecurity/defsec/rules"
 	"github.com/aquasecurity/defsec/rules/aws/elasticsearch"
-	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
 	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
 	"github.com/aquasecurity/tfsec/pkg/rule"
 )
@@ -35,25 +33,5 @@ func init() {
 		RequiredTypes:  []string{"resource"},
 		RequiredLabels: []string{"aws_elasticsearch_domain"},
 		Base:           elasticsearch.CheckEnableDomainEncryption,
-		CheckTerraform: func(resourceBlock block.Block, _ block.Module) (results rules.Results) {
-
-			encryptionBlock := resourceBlock.GetBlock("encrypt_at_rest")
-			if encryptionBlock.IsNil() {
-				results.Add("Resource defines an unencrypted Elasticsearch domain (missing encrypt_at_rest block).", resourceBlock)
-				return
-			}
-
-			enabledAttr := encryptionBlock.GetAttribute("enabled")
-			if enabledAttr.IsNil() {
-				results.Add("Resource defines an unencrypted Elasticsearch domain (missing enabled attribute).", encryptionBlock)
-				return
-			}
-
-			if enabledAttr.IsFalse() {
-				results.Add("Resource defines an unencrypted Elasticsearch domain (enabled attribute set to false).", enabledAttr)
-			}
-
-			return results
-		},
 	})
 }
diff --git a/internal/app/tfsec/rules/aws/elasticsearch/enable_domain_logging_rule.go b/internal/app/tfsec/rules/aws/elasticsearch/enable_domain_logging_rule.go
index 6d6049825..67e128d54 100644
--- a/internal/app/tfsec/rules/aws/elasticsearch/enable_domain_logging_rule.go
+++ b/internal/app/tfsec/rules/aws/elasticsearch/enable_domain_logging_rule.go
@@ -1,9 +1,7 @@
 package elasticsearch
 
 import (
-	"github.com/aquasecurity/defsec/rules"
 	"github.com/aquasecurity/defsec/rules/aws/elasticsearch"
-	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
 	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
 	"github.com/aquasecurity/tfsec/pkg/rule"
 )
@@ -46,32 +44,5 @@ func init() {
 		RequiredTypes:  []string{"resource"},
 		RequiredLabels: []string{"aws_elasticsearch_domain"},
 		Base:           elasticsearch.CheckEnableDomainLogging,
-		CheckTerraform: func(resourceBlock block.Block, _ block.Module) (results rules.Results) {
-
-			if resourceBlock.MissingChild("log_publishing_options") {
-				results.Add("Resource does not configure logging at rest on the domain.", resourceBlock)
-				return
-			}
-
-			logOptions := resourceBlock.GetBlocks("log_publishing_options")
-			for _, logOption := range logOptions {
-
-				if logTypeAttr := logOption.GetAttribute("log_type"); logTypeAttr.IsNil() || logTypeAttr.NotEqual("AUDIT_LOGS") {
-					continue
-				}
-
-				enabledAttr := logOption.GetAttribute("enabled")
-				if enabledAttr.IsNotNil() && enabledAttr.IsFalse() {
-					results.Add("Resource explicitly disables logging on the domain.", enabledAttr)
-					return
-				} else {
-					// we have audit logs enabled
-					return
-				}
-			}
-
-			results.Add("Audit logging is not enabled for the domain.", resourceBlock)
-			return results
-		},
 	})
 }
diff --git a/internal/app/tfsec/rules/aws/elasticsearch/enable_in_transit_encryption_rule.go b/internal/app/tfsec/rules/aws/elasticsearch/enable_in_transit_encryption_rule.go
index 0996d634d..e84a427f8 100644
--- a/internal/app/tfsec/rules/aws/elasticsearch/enable_in_transit_encryption_rule.go
+++ b/internal/app/tfsec/rules/aws/elasticsearch/enable_in_transit_encryption_rule.go
@@ -1,9 +1,7 @@
 package elasticsearch
 
 import (
-	"github.com/aquasecurity/defsec/rules"
 	"github.com/aquasecurity/defsec/rules/aws/elasticsearch"
-	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
 	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
 	"github.com/aquasecurity/tfsec/pkg/rule"
 )
@@ -35,25 +33,5 @@ func init() {
 		RequiredTypes:  []string{"resource"},
 		RequiredLabels: []string{"aws_elasticsearch_domain"},
 		Base:           elasticsearch.CheckEnableInTransitEncryption,
-		CheckTerraform: func(resourceBlock block.Block, _ block.Module) (results rules.Results) {
-
-			encryptionBlock := resourceBlock.GetBlock("node_to_node_encryption")
-			if encryptionBlock.IsNil() {
-				results.Add("Resource defines an Elasticsearch domain with plaintext traffic (missing node_to_node_encryption block).", resourceBlock)
-				return
-			}
-
-			enabledAttr := encryptionBlock.GetAttribute("enabled")
-			if enabledAttr.IsNil() {
-				results.Add("Resource defines an Elasticsearch domain with plaintext traffic (missing enabled attribute).", encryptionBlock)
-				return
-			}
-
-			if enabledAttr.IsFalse() {
-				results.Add("Resource defines an Elasticsearch domain with plaintext traffic (enabled attribute set to false).", enabledAttr)
-			}
-
-			return results
-		},
 	})
 }
diff --git a/internal/app/tfsec/rules/aws/elasticsearch/enforce_https_rule.go b/internal/app/tfsec/rules/aws/elasticsearch/enforce_https_rule.go
index 4cb43bcb2..fae7983ce 100644
--- a/internal/app/tfsec/rules/aws/elasticsearch/enforce_https_rule.go
+++ b/internal/app/tfsec/rules/aws/elasticsearch/enforce_https_rule.go
@@ -1,9 +1,7 @@
 package elasticsearch
 
 import (
-	"github.com/aquasecurity/defsec/rules"
 	"github.com/aquasecurity/defsec/rules/aws/elasticsearch"
-	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
 	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
 	"github.com/aquasecurity/tfsec/pkg/rule"
 )
@@ -35,25 +33,5 @@ func init() {
 		RequiredTypes:  []string{"resource"},
 		RequiredLabels: []string{"aws_elasticsearch_domain"},
 		Base:           elasticsearch.CheckEnforceHttps,
-		CheckTerraform: func(resourceBlock block.Block, _ block.Module) (results rules.Results) {
-
-			endpointBlock := resourceBlock.GetBlock("domain_endpoint_options")
-			if endpointBlock.IsNil() {
-				results.Add("Resource defines an Elasticsearch domain with plaintext traffic (missing domain_endpoint_options block).", resourceBlock)
-				return
-			}
-
-			enforceHTTPSAttr := endpointBlock.GetAttribute("enforce_https")
-			if enforceHTTPSAttr.IsNil() {
-				results.Add("Resource defines an Elasticsearch domain with plaintext traffic (missing enforce_https attribute).", endpointBlock)
-				return
-			}
-
-			if enforceHTTPSAttr.IsFalse() {
-				results.Add("Resource defines an Elasticsearch domain with plaintext traffic (enabled attribute set to false).", enforceHTTPSAttr)
-			}
-
-			return results
-		},
 	})
 }
diff --git a/internal/app/tfsec/rules/aws/elasticsearch/use_secure_tls_policy_rule.go b/internal/app/tfsec/rules/aws/elasticsearch/use_secure_tls_policy_rule.go
index 10ef80a0f..1e385af79 100644
--- a/internal/app/tfsec/rules/aws/elasticsearch/use_secure_tls_policy_rule.go
+++ b/internal/app/tfsec/rules/aws/elasticsearch/use_secure_tls_policy_rule.go
@@ -1,9 +1,7 @@
 package elasticsearch
 
 import (
-	"github.com/aquasecurity/defsec/rules"
 	"github.com/aquasecurity/defsec/rules/aws/elasticsearch"
-	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
 	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
 	"github.com/aquasecurity/tfsec/pkg/rule"
 )
@@ -37,24 +35,5 @@ func init() {
 		RequiredTypes:  []string{"resource"},
 		RequiredLabels: []string{"aws_elasticsearch_domain"},
 		Base:           elasticsearch.CheckUseSecureTlsPolicy,
-		CheckTerraform: func(resourceBlock block.Block, _ block.Module) (results rules.Results) {
-
-			endpointBlock := resourceBlock.GetBlock("domain_endpoint_options")
-			if endpointBlock.IsNil() {
-				return
-			}
-
-			tlsPolicyAttr := endpointBlock.GetAttribute("tls_security_policy")
-			if tlsPolicyAttr.IsNil() {
-				results.Add("Resource defines an Elasticsearch domain with an outdated TLS policy (defaults to Policy-Min-TLS-1-0-2019-07).", endpointBlock)
-				return
-			}
-
-			if tlsPolicyAttr.Equals("Policy-Min-TLS-1-0-2019-07") {
-				results.Add("Resource defines an Elasticsearch domain with an outdated TLS policy (set to Policy-Min-TLS-1-0-2019-07).", tlsPolicyAttr)
-			}
-
-			return results
-		},
 	})
 }


Test patch:
diff --git a/internal/app/tfsec/rules/aws/athena/enable_at_rest_encryption_rule_test.go b/internal/app/tfsec/rules/aws/athena/enable_at_rest_encryption_rule_test.go
index bf53b5fd8..2e2e48894 100644
--- a/internal/app/tfsec/rules/aws/athena/enable_at_rest_encryption_rule_test.go
+++ b/internal/app/tfsec/rules/aws/athena/enable_at_rest_encryption_rule_test.go
@@ -93,7 +93,7 @@ func Test_AWSEnsureAthenaDbEncrypted(t *testing.T) {
    }
  }
  `,
-			mustExcludeResultCode: expectedCode,
+			mustIncludeResultCode: expectedCode,
 		},
 	}
 
diff --git a/internal/app/tfsec/rules/aws/elasticsearch/use_secure_tls_policy_rule_test.go b/internal/app/tfsec/rules/aws/elasticsearch/use_secure_tls_policy_rule_test.go
index 07efa88c6..88cf5846f 100644
--- a/internal/app/tfsec/rules/aws/elasticsearch/use_secure_tls_policy_rule_test.go
+++ b/internal/app/tfsec/rules/aws/elasticsearch/use_secure_tls_policy_rule_test.go
@@ -21,7 +21,7 @@ func Test_AWSOutdatedTLSPolicyElasticsearchDomainEndpoint(t *testing.T) {
  resource "aws_elasticsearch_domain" "my_elasticsearch_domain" {
  	
  }`,
-			mustExcludeResultCode: expectedCode,
+			mustIncludeResultCode: expectedCode,
 		},
 		{
 			name: "check tls_security_policy for aws_elasticsearch_domain isn't the default",