Repository: aquasecurity/tfsec (go)

Issue:
Regression with conditional resources
Something changed recently which caused projects using the [terraform-aws-vpc](https://github.com/terraform-aws-modules/terraform-aws-vpc/) module to be flagged because it conditionally manages the default VPC. This rule will trigger even with the default value (0).

```
 [AWS082][ERROR] Resource 'module.vpc:aws_default_vpc.this' should not exist
  /Users/…/.terraform/modules/vpc/main.tf:1301-1315


    1298 | # Defaults
    1299 | ################################################################################
    1300 | 
    1301 | resource "aws_default_vpc" "this" {
    1302 |   count = var.manage_default_vpc ? 1 : 0
    1303 | 
    1304 |   enable_dns_support   = var.default_vpc_enable_dns_support
    1305 |   enable_dns_hostnames = var.default_vpc_enable_dns_hostnames
    1306 |   enable_classiclink   = var.default_vpc_enable_classiclink
    1307 | 
    1308 |   tags = merge(
    1309 |     {
    1310 |       "Name" = format("%s", var.default_vpc_name)
    1311 |     },
    1312 |     var.tags,
    1313 |     var.default_vpc_tags,
    1314 |   )
    1315 | }
    1316 | 
```

Version: tfsec 0.45.8


Pull request:
Fix 'count==0' resources giving false positives
Resolves #868 

Patch:
diff --git a/internal/app/tfsec/block/hclattribute.go b/internal/app/tfsec/block/hclattribute.go
index 43ec9d41f..5ca509a03 100644
--- a/internal/app/tfsec/block/hclattribute.go
+++ b/internal/app/tfsec/block/hclattribute.go
@@ -307,6 +307,10 @@ func (attr *HCLAttribute) IsTrue() bool {
 		val := attr.Value().AsString()
 		val = strings.Trim(val, "\"")
 		return strings.ToLower(val) == "true"
+	case cty.Number:
+		val := attr.Value().AsBigFloat()
+		f, _ := val.Float64()
+		return f > 0
 	}
 	return false
 }
diff --git a/pkg/rule/check.go b/pkg/rule/check.go
index 2818839e5..67bafd08b 100644
--- a/pkg/rule/check.go
+++ b/pkg/rule/check.go
@@ -7,6 +7,7 @@ import (
 	"strings"
 
 	"github.com/aquasecurity/tfsec/pkg/provider"
+	"github.com/zclconf/go-cty/cty"
 
 	"github.com/aquasecurity/tfsec/pkg/result"
 
@@ -47,6 +48,23 @@ func CheckRule(r *Rule, block block.Block, ctx *hclcontext.Context, ignoreErrors
 	return resultSet
 }
 
+func isResourceCountZero(block block.Block) bool {
+	if block.Type() == "resource" {
+		countAttr := block.GetAttribute("count")
+		if countAttr != nil {
+			if !countAttr.Value().IsNull() && countAttr.Value().IsKnown() {
+				if countAttr.Value().Type() == cty.Number {
+					if f, _ := countAttr.Value().AsBigFloat().Float64(); f == 0 {
+						return true
+					}
+				}
+			}
+		}
+	}
+	return false
+
+}
+
 // IsRuleRequiredForBlock returns true if the Rule should be applied to the given HCL block
 func IsRuleRequiredForBlock(rule *Rule, block block.Block) bool {
 
@@ -54,6 +72,10 @@ func IsRuleRequiredForBlock(rule *Rule, block block.Block) bool {
 		return false
 	}
 
+	if isResourceCountZero(block) {
+		return false
+	}
+
 	if len(rule.RequiredTypes) > 0 {
 		var found bool
 		for _, requiredType := range rule.RequiredTypes {


Test patch:
diff --git a/internal/app/tfsec/test/count_test.go b/internal/app/tfsec/test/count_test.go
new file mode 100644
index 000000000..0b336fd6d
--- /dev/null
+++ b/internal/app/tfsec/test/count_test.go
@@ -0,0 +1,115 @@
+package test
+
+import (
+	"testing"
+)
+
+func Test_ResourcesWithCount(t *testing.T) {
+	var tests = []struct {
+		name                  string
+		source                string
+		mustIncludeResultCode string
+		mustExcludeResultCode string
+	}{
+		{
+			name: "unspecified count defaults to 1",
+			source: `
+			resource "aws_default_vpc" "this" {}
+`,
+			mustIncludeResultCode: "AWS082",
+		},
+		{
+			name: "count is literal 1",
+			source: `
+			resource "aws_default_vpc" "this" {
+				count = 1
+			}
+`,
+			mustIncludeResultCode: "AWS082",
+		},
+		{
+			name: "count is literal 99",
+			source: `
+			resource "aws_default_vpc" "this" {
+				count = 99
+			}
+`,
+			mustIncludeResultCode: "AWS082",
+		},
+		{
+			name: "count is literal 0",
+			source: `
+			resource "aws_default_vpc" "this" {
+				count = 0
+			}
+`,
+			mustExcludeResultCode: "AWS082",
+		},
+		{
+			name: "count is 0 from variable",
+			source: `
+			variable "count" {
+				default = 0
+			}
+			resource "aws_default_vpc" "this" {
+				count = var.count
+			}
+`,
+			mustExcludeResultCode: "AWS082",
+		},
+		{
+			name: "count is 1 from variable",
+			source: `
+			variable "count" {
+				default = 1
+			}
+			resource "aws_default_vpc" "this" {
+				count =  var.count
+			}
+`,
+			mustIncludeResultCode: "AWS082",
+		},
+		{
+			name: "count is 1 from variable without default",
+			source: `
+			variable "count" {
+			}
+			resource "aws_default_vpc" "this" {
+				count =  var.count
+			}
+`,
+			mustIncludeResultCode: "AWS082",
+		},
+		{
+			name: "count is 0 from conditional",
+			source: `
+			variable "enabled" {
+				default = false
+			}
+			resource "aws_default_vpc" "this" {
+				count = var.enabled ? 1 : 0
+			}
+`,
+			mustExcludeResultCode: "AWS082",
+		},
+		{
+			name: "count is 1 from conditional",
+			source: `
+			variable "enabled" {
+				default = true
+			}
+			resource "aws_default_vpc" "this" {
+				count =  var.enabled ? 1 : 0
+			}
+`,
+			mustIncludeResultCode: "AWS082",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			results := scanHCL(test.source, t)
+			assertCheckCode(t, test.mustIncludeResultCode, test.mustExcludeResultCode, results)
+		})
+	}
+}