diff --git a/docs/README.md b/docs/README.md index 7055293..7729197 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,11 @@ # Rules +## azurerm_eventhub_namespace +|Name|Description|Severity|Enabled|Link| +| --- | --- | --- | --- | --- | +|azurerm_eventhub_namespace_public_network_access_enabled|Consider disabling public network access on eventhubs. |NOTICE|✔|| +|azurerm_eventhub_namespace_minimum_tls_version|Enforce TLS 1.2 on event hubs |WARNING|✔|| + ## azurerm_key_vault |Name|Description|Severity|Enabled|Link| | --- | --- | --- | --- | --- | diff --git a/main.go b/main.go index bab7ac5..b3a689f 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,8 @@ func createRuleSet() *tflint.BuiltinRuleSet { Name: "azurerm-security", Version: "0.1.3", Rules: []tflint.Rule{ + rules.NewAzurermEventhubNamespacePublicNetworkAccessEnabled(), + rules.NewAzurermEventhubNamespaceUnsecureTLS(), rules.NewAzurermKeyVaultPublicNetworkAccessEnabled(), rules.NewAzurermLinuxFunctionAppFtpsState(), rules.NewAzurermLinuxFunctionAppHTTPSOnly(), diff --git a/rules/azurerm_eventhub_namespace_minimum_tls_version.go b/rules/azurerm_eventhub_namespace_minimum_tls_version.go new file mode 100644 index 0000000..1a2568c --- /dev/null +++ b/rules/azurerm_eventhub_namespace_minimum_tls_version.go @@ -0,0 +1,90 @@ +package rules + +import ( + "fmt" + + "github.com/terraform-linters/tflint-plugin-sdk/hclext" + "github.com/terraform-linters/tflint-plugin-sdk/tflint" + // "github.com/terraform-linters/tflint-ruleset-azurerm/project" +) + +// AzurermEventhubNamespaceUnsecureTLS checks the pattern is valid +type AzurermEventhubNamespaceUnsecureTLS struct { + tflint.DefaultRule + + resourceType string + attributeName string + enum []string +} + +// NewAzurermEventhubNamespaceUnsecureTLS returns new rule with default attributes +func NewAzurermEventhubNamespaceUnsecureTLS() *AzurermEventhubNamespaceUnsecureTLS { + return &AzurermEventhubNamespaceUnsecureTLS{ + resourceType: "azurerm_eventhub_namespace", + attributeName: "min_tls_version", + enum: []string{ + "TLS1_2", + "TLS1_3", + }, + } +} + +// Name returns the rule name +func (r *AzurermEventhubNamespaceUnsecureTLS) Name() string { + return "azurerm_eventhub_namespace_unsecure_tls" +} + +// Enabled returns whether the rule is enabled by default +func (r *AzurermEventhubNamespaceUnsecureTLS) Enabled() bool { + return true +} + +// Severity returns the rule severity +func (r *AzurermEventhubNamespaceUnsecureTLS) Severity() tflint.Severity { + return tflint.WARNING +} + +// Link returns the rule reference link +func (r *AzurermEventhubNamespaceUnsecureTLS) Link() string { + return "" +} + +// Check checks the pattern is valid +func (r *AzurermEventhubNamespaceUnsecureTLS) Check(runner tflint.Runner) error { + resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{ + Attributes: []hclext.AttributeSchema{ + {Name: r.attributeName}, + }, + }, nil) + if err != nil { + return err + } + + for _, resource := range resources.Blocks { + attribute, exists := resource.Body.Attributes[r.attributeName] + if !exists { + continue + } + err := runner.EvaluateExpr(attribute.Expr, func (val string) error { + found := false + for _, item := range r.enum { + if item == val { + found = true + } + } + if !found { + runner.EmitIssue( + r, + fmt.Sprintf(`"%s" is an insecure value as min_tls_version`, val), + attribute.Expr.Range(), + ) + } + return nil + }, nil) + if err != nil { + return err + } + } + + return nil +} \ No newline at end of file diff --git a/rules/azurerm_eventhub_namespace_minimum_tls_version_test.go b/rules/azurerm_eventhub_namespace_minimum_tls_version_test.go new file mode 100644 index 0000000..9511c11 --- /dev/null +++ b/rules/azurerm_eventhub_namespace_minimum_tls_version_test.go @@ -0,0 +1,57 @@ +package rules + +import ( + "testing" + + hcl "github.com/hashicorp/hcl/v2" + "github.com/terraform-linters/tflint-plugin-sdk/helper" +) + +func Test_AzurermEventhubNamespaceUnsecureTLS(t *testing.T) { + tests := []struct { + Name string + Content string + Expected helper.Issues + }{ + { + Name: "insecure TLS version found", + Content: ` +resource "azurerm_eventhub_namespace" "example" { + min_tls_version = "TLS1_0" +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermEventhubNamespaceUnsecureTLS(), + Message: `"TLS1_0" is an insecure value as min_tls_version`, + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 3, Column: 23}, + End: hcl.Pos{Line: 3, Column: 31}, + }, + }, + }, + }, + { + Name: "secure TLS version", + Content: ` +resource "azurerm_eventhub_namespace" "example" { + min_tls_version = "TLS1_2" +}`, + Expected: helper.Issues{}, + }, + } + + rule := NewAzurermEventhubNamespaceUnsecureTLS() + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + runner := helper.TestRunner(t, map[string]string{"resource.tf": test.Content}) + + if err := rule.Check(runner); err != nil { + t.Fatalf("Unexpected error occurred: %s", err) + } + + helper.AssertIssues(t, test.Expected, runner.Issues) + }) + } +} \ No newline at end of file diff --git a/rules/azurerm_eventhub_namespace_public_network_access_enabled.go b/rules/azurerm_eventhub_namespace_public_network_access_enabled.go new file mode 100644 index 0000000..30725fe --- /dev/null +++ b/rules/azurerm_eventhub_namespace_public_network_access_enabled.go @@ -0,0 +1,84 @@ +package rules + +import ( + "github.com/terraform-linters/tflint-plugin-sdk/hclext" + "github.com/terraform-linters/tflint-plugin-sdk/tflint" +) + +// AzurermEventhubNamespacePublicNetworkAccessEnabled checks that transparent data encryption is enabled +type AzurermEventhubNamespacePublicNetworkAccessEnabled struct { + tflint.DefaultRule + + resourceType string + attributeName string +} + +// NewAzurermEventhubNamespacePublicNetworkAccessEnabled returns a new rule instance +func NewAzurermEventhubNamespacePublicNetworkAccessEnabled() *AzurermEventhubNamespacePublicNetworkAccessEnabled { + return &AzurermEventhubNamespacePublicNetworkAccessEnabled{ + resourceType: "azurerm_eventhub_namespace", + attributeName: "public_network_access_enabled", + } +} + +// Name returns the rule name +func (r *AzurermEventhubNamespacePublicNetworkAccessEnabled) Name() string { + return "azurerm_eventhub_namespace_public_network_access_enabled" +} + +// Enabled returns whether the rule is enabled by default +func (r *AzurermEventhubNamespacePublicNetworkAccessEnabled) Enabled() bool { + return true +} + +// Severity returns the rule severity +func (r *AzurermEventhubNamespacePublicNetworkAccessEnabled) Severity() tflint.Severity { + return tflint.NOTICE +} + +// Link returns the rule reference link +func (r *AzurermEventhubNamespacePublicNetworkAccessEnabled) Link() string { + return "" +} + +// Check checks if transparent data encryption is enabled +func (r *AzurermEventhubNamespacePublicNetworkAccessEnabled) Check(runner tflint.Runner) error { + resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{ + Attributes: []hclext.AttributeSchema{ + {Name: r.attributeName}, + }, + }, nil) + if err != nil { + return err + } + + for _, resource := range resources.Blocks { + attribute, exists := resource.Body.Attributes[r.attributeName] + if !exists { + // Emit an issue if the attribute does not exist + runner.EmitIssue( + r, + "public_network_access_enabled is not defined and defaults to true, consider disabling it", + resource.DefRange, + ) + continue + } + + err := runner.EvaluateExpr(attribute.Expr, func(val bool) error { + if val { + runner.EmitIssue( + r, + "Consider changing public_network_access_enabled to false", + attribute.Expr.Range(), + ) + } + return nil + }, nil) + + if err != nil { + return err + } + } + + return nil +} diff --git a/rules/azurerm_eventhub_namespace_public_network_access_enabled_test.go b/rules/azurerm_eventhub_namespace_public_network_access_enabled_test.go new file mode 100644 index 0000000..8f90dd7 --- /dev/null +++ b/rules/azurerm_eventhub_namespace_public_network_access_enabled_test.go @@ -0,0 +1,74 @@ +package rules + +import ( + "testing" + + hcl "github.com/hashicorp/hcl/v2" + "github.com/terraform-linters/tflint-plugin-sdk/helper" +) + +func Test_AzurermEventhubNamespacePublicNetworkAccessEnabled(t *testing.T) { + tests := []struct { + Name string + Content string + Expected helper.Issues + }{ + { + Name: "public network access disabled", + Content: ` +resource "azurerm_eventhub_namespace" "example" { + public_network_access_enabled = true +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermEventhubNamespacePublicNetworkAccessEnabled(), + Message: "Consider changing public_network_access_enabled to false", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 3, Column: 37}, + End: hcl.Pos{Line: 3, Column: 41}, + }, + }, + }, + }, + { + Name: "public network access missing", + Content: ` +resource "azurerm_eventhub_namespace" "example" { +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermEventhubNamespacePublicNetworkAccessEnabled(), + Message: "public_network_access_enabled is not defined and defaults to true, consider disabling it", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 2, Column: 1}, + End: hcl.Pos{Line: 2, Column: 48}, + }, + }, + }, + }, + { + Name: "public network access disabled", + Content: ` +resource "azurerm_eventhub_namespace" "example" { + public_network_access_enabled = false +}`, + Expected: helper.Issues{}, + }, + } + + rule := NewAzurermEventhubNamespacePublicNetworkAccessEnabled() + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + runner := helper.TestRunner(t, map[string]string{"resource.tf": test.Content}) + + if err := rule.Check(runner); err != nil { + t.Fatalf("Unexpected error occurred: %s", err) + } + + helper.AssertIssues(t, test.Expected, runner.Issues) + }) + } +}