diff --git a/README.md b/README.md index 9ef75e8..e03a57a 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ You can install the plugin with `tflint --init`. Declare a config in `.tflint.hc plugin "azurerm-security" { enabled = true - version = "0.1.10" + version = "0.1.11" source = "github.com/pregress/tflint-ruleset-azurerm-security" } ``` diff --git a/docs/README.md b/docs/README.md index 2841e86..a1a1cce 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,12 +16,14 @@ |[azurerm_linux_function_app_ftps_state](./rules/azurerm_linux_function_app_ftps_state.md)|Warning|✔| |[azurerm_linux_function_app_https_only](./rules/azurerm_linux_function_app_https_only.md)|Warning|✔| |[azurerm_linux_function_app_minimum_tls_version](./rules/azurerm_linux_function_app_minimum_tls_version.md)|Warning|✔| +|[azurerm_linux_function_app_scm_ip_restriction_default_action](./rules/azurerm_linux_function_app_scm_ip_restriction_default_action.md)|Warning|✔| |[azurerm_linux_function_app_slot_ftps_state](./rules/azurerm_linux_function_app_slot_ftps_state.md)|Warning|✔| |[azurerm_linux_function_app_slot_https_only](./rules/azurerm_linux_function_app_slot_https_only.md)|Warning|✔| |[azurerm_linux_function_app_slot_minimum_tls_version](./rules/azurerm_linux_function_app_slot_minimum_tls_version.md)|Warning|✔| |[azurerm_linux_web_app_ftps_state](./rules/azurerm_linux_web_app_ftps_state.md)|Warning|✔| |[azurerm_linux_web_app_https_only](./rules/azurerm_linux_web_app_https_only.md)|Warning|✔| |[azurerm_linux_web_app_minimum_tls_version](./rules/azurerm_linux_web_app_minimum_tls_version.md)|Warning|✔| +|[azurerm_linux_web_app_scm_ip_restriction_default_action](./rules/azurerm_linux_web_app_scm_ip_restriction_default_action.md)|Warning|✔| |[azurerm_linux_web_app_slot_ftps_state](./rules/azurerm_linux_web_app_slot_ftps_state.md)|Warning|✔| |[azurerm_linux_web_app_slot_https_only](./rules/azurerm_linux_web_app_slot_https_only.md)|Warning|✔| |[azurerm_linux_web_app_slot_minimum_tls_version](./rules/azurerm_linux_web_app_slot_minimum_tls_version.md)|Warning|✔| @@ -39,12 +41,14 @@ |[azurerm_windows_function_app_ftps_state](./rules/azurerm_windows_function_app_ftps_state.md)|Warning|✔| |[azurerm_windows_function_app_https_only](./rules/azurerm_windows_function_app_https_only.md)|Warning|✔| |[azurerm_windows_function_app_minimum_tls_version](./rules/azurerm_windows_function_app_minimum_tls_version.md)|Warning|✔| +|[azurerm_windows_function_app_scm_ip_restriction_default_action](./rules/azurerm_windows_function_app_scm_ip_restriction_default_action.md)|Warning|✔| |[azurerm_windows_function_app_slot_ftps_state](./rules/azurerm_windows_function_app_slot_ftps_state.md)|Warning|✔| |[azurerm_windows_function_app_slot_https_only](./rules/azurerm_windows_function_app_slot_https_only.md)|Warning|✔| |[azurerm_windows_function_app_slot_minimum_tls_version](./rules/azurerm_windows_function_app_slot_minimum_tls_version.md)|Warning|✔| |[azurerm_windows_web_app_ftps_state](./rules/azurerm_windows_web_app_ftps_state.md)|Warning|✔| |[azurerm_windows_web_app_https_only](./rules/azurerm_windows_web_app_https_only.md)|Warning|✔| |[azurerm_windows_web_app_minimum_tls_version](./rules/azurerm_windows_web_app_minimum_tls_version.md)|Warning|✔| +|[azurerm_windows_web_app_scm_ip_restriction_default_action](./rules/azurerm_windows_web_app_scm_ip_restriction_default_action.md)|Warning|✔| |[azurerm_windows_web_app_slot_ftps_state](./rules/azurerm_windows_web_app_slot_ftps_state.md)|Warning|✔| |[azurerm_windows_web_app_slot_https_only](./rules/azurerm_windows_web_app_slot_https_only.md)|Warning|✔| |[azurerm_windows_web_app_slot_minimum_tls_version](./rules/azurerm_windows_web_app_slot_minimum_tls_version.md)|Warning|✔| @@ -83,6 +87,7 @@ - [azurerm_linux_function_app_ftps_state](./rules/azurerm_linux_function_app_ftps_state.md) - [azurerm_linux_function_app_https_only](./rules/azurerm_linux_function_app_https_only.md) - [azurerm_linux_function_app_minimum_tls_version](./rules/azurerm_linux_function_app_minimum_tls_version.md) +- [azurerm_linux_function_app_scm_ip_restriction_default_action](./rules/azurerm_linux_function_app_scm_ip_restriction_default_action.md) ### azurerm_linux_function_app_slot @@ -95,6 +100,7 @@ - [azurerm_linux_web_app_ftps_state](./rules/azurerm_linux_web_app_ftps_state.md) - [azurerm_linux_web_app_https_only](./rules/azurerm_linux_web_app_https_only.md) - [azurerm_linux_web_app_minimum_tls_version](./rules/azurerm_linux_web_app_minimum_tls_version.md) +- [azurerm_linux_web_app_scm_ip_restriction_default_action](./rules/azurerm_linux_web_app_scm_ip_restriction_default_action.md) ### azurerm_linux_web_app_slot @@ -133,6 +139,7 @@ - [azurerm_windows_function_app_ftps_state](./rules/azurerm_windows_function_app_ftps_state.md) - [azurerm_windows_function_app_https_only](./rules/azurerm_windows_function_app_https_only.md) - [azurerm_windows_function_app_minimum_tls_version](./rules/azurerm_windows_function_app_minimum_tls_version.md) +- [azurerm_windows_function_app_scm_ip_restriction_default_action](./rules/azurerm_windows_function_app_scm_ip_restriction_default_action.md) ### azurerm_windows_function_app_slot @@ -145,6 +152,7 @@ - [azurerm_windows_web_app_ftps_state](./rules/azurerm_windows_web_app_ftps_state.md) - [azurerm_windows_web_app_https_only](./rules/azurerm_windows_web_app_https_only.md) - [azurerm_windows_web_app_minimum_tls_version](./rules/azurerm_windows_web_app_minimum_tls_version.md) +- [azurerm_windows_web_app_scm_ip_restriction_default_action](./rules/azurerm_windows_web_app_scm_ip_restriction_default_action.md) ### azurerm_windows_web_app_slot diff --git a/docs/rules/azurerm_linux_function_app_scm_ip_restriction_default_action.md b/docs/rules/azurerm_linux_function_app_scm_ip_restriction_default_action.md new file mode 100644 index 0000000..35955dd --- /dev/null +++ b/docs/rules/azurerm_linux_function_app_scm_ip_restriction_default_action.md @@ -0,0 +1,72 @@ +# azurerm_linux_function_app_scm_ip_restriction_default_action + +**Severity:** Warning + + +## Example + +```hcl +resource "azurerm_linux_function_app" "example" { + site_config { + scm_ip_restriction_default_action = "Allow" + } +} +``` +or +```hcl +resource "azurerm_linux_function_app" "example" { + site_config { + # Missing scm_ip_restriction_default_action (defaults to Allow) + } +} +``` + +## Why + +Setting the `scm_ip_restriction_default_action` to "Deny" prevents unauthorized access to the Source Control Manager (SCM) interface, reducing exposure to potential threats and ensuring only trusted networks can connect to the deployment and management endpoints. + +## How to Fix + +Set the `scm_ip_restriction_default_action` to "Deny" and configure specific `scm_ip_restriction` rules to allow legitimate access. + +### Using service tag +```hcl +resource "azurerm_linux_function_app" "example" { + site_config { + scm_ip_restriction_default_action = "Deny" + + scm_ip_restriction { + service_tag = "AzureDevOps" + name = "Allow Azure DevOps" + priority = 100 + action = "Allow" + } + } +} +``` + +### Using IP range +```hcl +resource "azurerm_linux_function_app" "example" { + site_config { + scm_ip_restriction_default_action = "Deny" + + scm_ip_restriction { + ip_address = "203.0.113.0/24" + name = "Corporate Network" + priority = 100 + action = "Allow" + } + } +} +``` + + +## How to disable + +```hcl +rule "azurerm_linux_function_app_scm_ip_restriction_default_action" { + enabled = false +} +``` + diff --git a/docs/rules/azurerm_linux_web_app_scm_ip_restriction_default_action.md b/docs/rules/azurerm_linux_web_app_scm_ip_restriction_default_action.md new file mode 100644 index 0000000..552b3ac --- /dev/null +++ b/docs/rules/azurerm_linux_web_app_scm_ip_restriction_default_action.md @@ -0,0 +1,72 @@ +# azurerm_linux_web_app_scm_ip_restriction_default_action + +**Severity:** Warning + + +## Example + +```hcl +resource "azurerm_linux_web_app" "example" { + site_config { + scm_ip_restriction_default_action = "Allow" + } +} +``` +or +```hcl +resource "azurerm_linux_web_app" "example" { + site_config { + # Missing scm_ip_restriction_default_action (defaults to Allow) + } +} +``` + +## Why + +Setting the `scm_ip_restriction_default_action` to "Deny" prevents unauthorized access to the Source Control Manager (SCM) interface, reducing exposure to potential threats and ensuring only trusted networks can connect to the deployment and management endpoints. + +## How to Fix + +Set the `scm_ip_restriction_default_action` to "Deny" and configure specific `scm_ip_restriction` rules to allow legitimate access. + +### Using service tag +```hcl +resource "azurerm_linux_web_app" "example" { + site_config { + scm_ip_restriction_default_action = "Deny" + + scm_ip_restriction { + service_tag = "AzureDevOps" + name = "Allow Azure DevOps" + priority = 100 + action = "Allow" + } + } +} +``` + +### Using IP range +```hcl +resource "azurerm_linux_web_app" "example" { + site_config { + scm_ip_restriction_default_action = "Deny" + + scm_ip_restriction { + ip_address = "203.0.113.0/24" + name = "Corporate Network" + priority = 100 + action = "Allow" + } + } +} +``` + + +## How to disable + +```hcl +rule "azurerm_linux_web_app_scm_ip_restriction_default_action" { + enabled = false +} +``` + diff --git a/docs/rules/azurerm_windows_function_app_scm_ip_restriction_default_action.md b/docs/rules/azurerm_windows_function_app_scm_ip_restriction_default_action.md new file mode 100644 index 0000000..85eb8fb --- /dev/null +++ b/docs/rules/azurerm_windows_function_app_scm_ip_restriction_default_action.md @@ -0,0 +1,72 @@ +# azurerm_windows_function_app_scm_ip_restriction_default_action + +**Severity:** Warning + + +## Example + +```hcl +resource "azurerm_windows_function_app" "example" { + site_config { + scm_ip_restriction_default_action = "Allow" + } +} +``` +or +```hcl +resource "azurerm_windows_function_app" "example" { + site_config { + # Missing scm_ip_restriction_default_action (defaults to Allow) + } +} +``` + +## Why + +Setting the `scm_ip_restriction_default_action` to "Deny" prevents unauthorized access to the Source Control Manager (SCM) interface, reducing exposure to potential threats and ensuring only trusted networks can connect to the deployment and management endpoints. + +## How to Fix + +Set the `scm_ip_restriction_default_action` to "Deny" and configure specific `scm_ip_restriction` rules to allow legitimate access. + +### Using service tag +```hcl +resource "azurerm_windows_function_app" "example" { + site_config { + scm_ip_restriction_default_action = "Deny" + + scm_ip_restriction { + service_tag = "AzureDevOps" + name = "Allow Azure DevOps" + priority = 100 + action = "Allow" + } + } +} +``` + +### Using IP range +```hcl +resource "azurerm_windows_function_app" "example" { + site_config { + scm_ip_restriction_default_action = "Deny" + + scm_ip_restriction { + ip_address = "203.0.113.0/24" + name = "Corporate Network" + priority = 100 + action = "Allow" + } + } +} +``` + + +## How to disable + +```hcl +rule "azurerm_windows_function_app_scm_ip_restriction_default_action" { + enabled = false +} +``` + diff --git a/docs/rules/azurerm_windows_web_app_scm_ip_restriction_default_action.md b/docs/rules/azurerm_windows_web_app_scm_ip_restriction_default_action.md new file mode 100644 index 0000000..0159b28 --- /dev/null +++ b/docs/rules/azurerm_windows_web_app_scm_ip_restriction_default_action.md @@ -0,0 +1,72 @@ +# azurerm_windows_web_app_scm_ip_restriction_default_action + +**Severity:** Warning + + +## Example + +```hcl +resource "azurerm_windows_web_app" "example" { + site_config { + scm_ip_restriction_default_action = "Allow" + } +} +``` +or +```hcl +resource "azurerm_windows_web_app" "example" { + site_config { + # Missing scm_ip_restriction_default_action (defaults to Allow) + } +} +``` + +## Why + +Setting the `scm_ip_restriction_default_action` to "Deny" prevents unauthorized access to the Source Control Manager (SCM) interface, reducing exposure to potential threats and ensuring only trusted networks can connect to the deployment and management endpoints. + +## How to Fix + +Set the `scm_ip_restriction_default_action` to "Deny" and configure specific `scm_ip_restriction` rules to allow legitimate access. + +### Using service tag +```hcl +resource "azurerm_windows_web_app" "example" { + site_config { + scm_ip_restriction_default_action = "Deny" + + scm_ip_restriction { + service_tag = "AzureDevOps" + name = "Allow Azure DevOps" + priority = 100 + action = "Allow" + } + } +} +``` + +### Using IP range +```hcl +resource "azurerm_windows_web_app" "example" { + site_config { + scm_ip_restriction_default_action = "Deny" + + scm_ip_restriction { + ip_address = "203.0.113.0/24" + name = "Corporate Network" + priority = 100 + action = "Allow" + } + } +} +``` + + +## How to disable + +```hcl +rule "azurerm_windows_web_app_scm_ip_restriction_default_action" { + enabled = false +} +``` + diff --git a/main.go b/main.go index c6f7be5..9cdeefe 100644 --- a/main.go +++ b/main.go @@ -25,12 +25,14 @@ func createRuleSet() *tflint.BuiltinRuleSet { rules.NewAzurermLinuxFunctionAppFtpsState(), rules.NewAzurermLinuxFunctionAppHTTPSOnly(), rules.NewAzurermLinuxFunctionAppMinimumTLSVersion(), + rules.NewAzurermLinuxFunctionAppScmIPRestrictionDefaultAction(), rules.NewAzurermLinuxFunctionAppSlotFtpsState(), rules.NewAzurermLinuxFunctionAppSlotHTTPSOnly(), rules.NewAzurermLinuxFunctionAppSlotMinimumTLSVersion(), rules.NewAzurermLinuxWebAppFtpsState(), rules.NewAzurermLinuxWebAppHTTPSOnly(), rules.NewAzurermLinuxWebAppMinimumTLSVersion(), + rules.NewAzurermLinuxWebAppScmIPRestrictionDefaultAction(), rules.NewAzurermLinuxWebAppSlotFtpsState(), rules.NewAzurermLinuxWebAppSlotHTTPSOnly(), rules.NewAzurermLinuxWebAppSlotMinimumTLSVersion(), @@ -48,12 +50,14 @@ func createRuleSet() *tflint.BuiltinRuleSet { rules.NewAzurermWindowsFunctionAppFtpsState(), rules.NewAzurermWindowsFunctionAppHTTPSOnly(), rules.NewAzurermWindowsFunctionAppMinimumTLSVersion(), + rules.NewAzurermWindowsFunctionAppScmIPRestrictionDefaultAction(), rules.NewAzurermWindowsFunctionAppSlotFtpsState(), rules.NewAzurermWindowsFunctionAppSlotHTTPSOnly(), rules.NewAzurermWindowsFunctionAppSlotMinimumTLSVersion(), rules.NewAzurermWindowsWebAppFtpsState(), rules.NewAzurermWindowsWebAppHTTPSOnly(), rules.NewAzurermWindowsWebAppMinimumTLSVersion(), + rules.NewAzurermWindowsWebAppScmIPRestrictionDefaultAction(), rules.NewAzurermWindowsWebAppSlotFtpsState(), rules.NewAzurermWindowsWebAppSlotHTTPSOnly(), rules.NewAzurermWindowsWebAppSlotMinimumTLSVersion(), diff --git a/project/main.go b/project/main.go index ae2913a..d0abc04 100644 --- a/project/main.go +++ b/project/main.go @@ -3,7 +3,7 @@ package project import "fmt" // Version is ruleset version -const Version string = "0.1.10" +const Version string = "0.1.11" // ReferenceLink returns the rule reference link func ReferenceLink(name string) string { diff --git a/rules/azurerm_linux_function_app_scm_ip_restriction_default_action.go b/rules/azurerm_linux_function_app_scm_ip_restriction_default_action.go new file mode 100644 index 0000000..40723a2 --- /dev/null +++ b/rules/azurerm_linux_function_app_scm_ip_restriction_default_action.go @@ -0,0 +1,107 @@ +package rules + +import ( + "github.com/terraform-linters/tflint-plugin-sdk/hclext" + "github.com/terraform-linters/tflint-plugin-sdk/tflint" + + "github.com/terraform-linters/tflint-ruleset-azurerm-security/project" +) + +// AzurermLinuxFunctionAppScmIPRestrictionDefaultAction checks that scm_ip_restriction_default_action is set to "Deny" +type AzurermLinuxFunctionAppScmIPRestrictionDefaultAction struct { + tflint.DefaultRule + + resourceType string +} + +// NewAzurermLinuxFunctionAppScmIPRestrictionDefaultAction returns a new rule instance +func NewAzurermLinuxFunctionAppScmIPRestrictionDefaultAction() *AzurermLinuxFunctionAppScmIPRestrictionDefaultAction { + return &AzurermLinuxFunctionAppScmIPRestrictionDefaultAction{ + resourceType: "azurerm_linux_function_app", + } +} + +// Name returns the rule name +func (r *AzurermLinuxFunctionAppScmIPRestrictionDefaultAction) Name() string { + return "azurerm_linux_function_app_scm_ip_restriction_default_action" +} + +// Enabled returns whether the rule is enabled by default +func (r *AzurermLinuxFunctionAppScmIPRestrictionDefaultAction) Enabled() bool { + return true +} + +// Severity returns the rule severity +func (r *AzurermLinuxFunctionAppScmIPRestrictionDefaultAction) Severity() tflint.Severity { + return tflint.WARNING +} + +// Link returns the rule reference link +func (r *AzurermLinuxFunctionAppScmIPRestrictionDefaultAction) Link() string { + return project.ReferenceLink(r.Name()) +} + +// Check verifies that scm_ip_restriction_default_action is set to "Deny" +func (r *AzurermLinuxFunctionAppScmIPRestrictionDefaultAction) Check(runner tflint.Runner) error { + resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{ + Blocks: []hclext.BlockSchema{ + { + Type: "site_config", + Body: &hclext.BodySchema{ + Attributes: []hclext.AttributeSchema{ + {Name: "scm_ip_restriction_default_action"}, + }, + }, + }, + }, + }, nil) + if err != nil { + return err + } + + for _, resource := range resources.Blocks { + // Check if site_config block exists + hasSiteConfig := false + for _, block := range resource.Body.Blocks { + if block.Type == "site_config" { + hasSiteConfig = true + + // Check if scm_ip_restriction_default_action attribute exists + if attr, exists := block.Body.Attributes["scm_ip_restriction_default_action"]; exists { + err := runner.EvaluateExpr(attr.Expr, func(val string) error { + if val != "Deny" { + runner.EmitIssue( + r, + "scm_ip_restriction_default_action should be Deny", + attr.Expr.Range(), + ) + } + return nil + }, nil) + if err != nil { + return err + } + } else { + // Attribute is missing in site_config block + runner.EmitIssue( + r, + "scm_ip_restriction_default_action is not defined and should be Deny", + resource.DefRange, + ) + } + break + } + } + + // If site_config block doesn't exist + if !hasSiteConfig { + runner.EmitIssue( + r, + "scm_ip_restriction_default_action is not defined and should be Deny", + resource.DefRange, + ) + } + } + + return nil +} diff --git a/rules/azurerm_linux_function_app_scm_ip_restriction_default_action_test.go b/rules/azurerm_linux_function_app_scm_ip_restriction_default_action_test.go new file mode 100644 index 0000000..ebe5193 --- /dev/null +++ b/rules/azurerm_linux_function_app_scm_ip_restriction_default_action_test.go @@ -0,0 +1,97 @@ +package rules + +import ( + "testing" + + hcl "github.com/hashicorp/hcl/v2" + "github.com/terraform-linters/tflint-plugin-sdk/helper" +) + +func Test_AzurermLinuxFunctionAppScmIPRestrictionDefaultAction(t *testing.T) { + tests := []struct { + Name string + Content string + Expected helper.Issues + }{ + { + Name: "scm_ip_restriction_default_action allowed", + Content: ` +resource "azurerm_linux_function_app" "example" { + site_config { + scm_ip_restriction_default_action = "Allow" + } +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermLinuxFunctionAppScmIPRestrictionDefaultAction(), + Message: "scm_ip_restriction_default_action should be Deny", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 4, Column: 39}, + End: hcl.Pos{Line: 4, Column: 46}, + }, + }, + }, + }, + { + Name: "scm_ip_restriction_default_action attribute missing", + Content: ` +resource "azurerm_linux_function_app" "example" { + site_config { + } +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermLinuxFunctionAppScmIPRestrictionDefaultAction(), + Message: "scm_ip_restriction_default_action is not defined and should be Deny", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 2, Column: 1}, + End: hcl.Pos{Line: 2, Column: 48}, + }, + }, + }, + }, + { + Name: "site_config block missing", + Content: ` +resource "azurerm_linux_function_app" "example" { +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermLinuxFunctionAppScmIPRestrictionDefaultAction(), + Message: "scm_ip_restriction_default_action is not defined and should be Deny", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 2, Column: 1}, + End: hcl.Pos{Line: 2, Column: 48}, + }, + }, + }, + }, + { + Name: "scm_ip_restriction_default_action Deny", + Content: ` +resource "azurerm_linux_function_app" "example" { + site_config { + scm_ip_restriction_default_action = "Deny" + } +}`, + Expected: helper.Issues{}, + }, + } + + rule := NewAzurermLinuxFunctionAppScmIPRestrictionDefaultAction() + + 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) + }) + } +} diff --git a/rules/azurerm_linux_web_app_scm_ip_restriction_default_action.go b/rules/azurerm_linux_web_app_scm_ip_restriction_default_action.go new file mode 100644 index 0000000..f5b3d76 --- /dev/null +++ b/rules/azurerm_linux_web_app_scm_ip_restriction_default_action.go @@ -0,0 +1,107 @@ +package rules + +import ( + "github.com/terraform-linters/tflint-plugin-sdk/hclext" + "github.com/terraform-linters/tflint-plugin-sdk/tflint" + + "github.com/terraform-linters/tflint-ruleset-azurerm-security/project" +) + +// AzurermLinuxWebAppScmIPRestrictionDefaultAction checks that scm_ip_restriction_default_action is set to "Deny" +type AzurermLinuxWebAppScmIPRestrictionDefaultAction struct { + tflint.DefaultRule + + resourceType string +} + +// NewAzurermLinuxWebAppScmIPRestrictionDefaultAction returns a new rule instance +func NewAzurermLinuxWebAppScmIPRestrictionDefaultAction() *AzurermLinuxWebAppScmIPRestrictionDefaultAction { + return &AzurermLinuxWebAppScmIPRestrictionDefaultAction{ + resourceType: "azurerm_linux_web_app", + } +} + +// Name returns the rule name +func (r *AzurermLinuxWebAppScmIPRestrictionDefaultAction) Name() string { + return "azurerm_linux_web_app_scm_ip_restriction_default_action" +} + +// Enabled returns whether the rule is enabled by default +func (r *AzurermLinuxWebAppScmIPRestrictionDefaultAction) Enabled() bool { + return true +} + +// Severity returns the rule severity +func (r *AzurermLinuxWebAppScmIPRestrictionDefaultAction) Severity() tflint.Severity { + return tflint.WARNING +} + +// Link returns the rule reference link +func (r *AzurermLinuxWebAppScmIPRestrictionDefaultAction) Link() string { + return project.ReferenceLink(r.Name()) +} + +// Check verifies that scm_ip_restriction_default_action is set to "Deny" +func (r *AzurermLinuxWebAppScmIPRestrictionDefaultAction) Check(runner tflint.Runner) error { + resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{ + Blocks: []hclext.BlockSchema{ + { + Type: "site_config", + Body: &hclext.BodySchema{ + Attributes: []hclext.AttributeSchema{ + {Name: "scm_ip_restriction_default_action"}, + }, + }, + }, + }, + }, nil) + if err != nil { + return err + } + + for _, resource := range resources.Blocks { + // Check if site_config block exists + hasSiteConfig := false + for _, block := range resource.Body.Blocks { + if block.Type == "site_config" { + hasSiteConfig = true + + // Check if scm_ip_restriction_default_action attribute exists + if attr, exists := block.Body.Attributes["scm_ip_restriction_default_action"]; exists { + err := runner.EvaluateExpr(attr.Expr, func(val string) error { + if val != "Deny" { + runner.EmitIssue( + r, + "scm_ip_restriction_default_action should be Deny", + attr.Expr.Range(), + ) + } + return nil + }, nil) + if err != nil { + return err + } + } else { + // Attribute is missing in site_config block + runner.EmitIssue( + r, + "scm_ip_restriction_default_action is not defined and should be Deny", + resource.DefRange, + ) + } + break + } + } + + // If site_config block doesn't exist + if !hasSiteConfig { + runner.EmitIssue( + r, + "scm_ip_restriction_default_action is not defined and should be Deny", + resource.DefRange, + ) + } + } + + return nil +} diff --git a/rules/azurerm_linux_web_app_scm_ip_restriction_default_action_test.go b/rules/azurerm_linux_web_app_scm_ip_restriction_default_action_test.go new file mode 100644 index 0000000..a2f017d --- /dev/null +++ b/rules/azurerm_linux_web_app_scm_ip_restriction_default_action_test.go @@ -0,0 +1,97 @@ +package rules + +import ( + "testing" + + hcl "github.com/hashicorp/hcl/v2" + "github.com/terraform-linters/tflint-plugin-sdk/helper" +) + +func Test_AzurermLinuxWebAppScmIPRestrictionDefaultAction(t *testing.T) { + tests := []struct { + Name string + Content string + Expected helper.Issues + }{ + { + Name: "scm_ip_restriction_default_action allowed", + Content: ` +resource "azurerm_linux_web_app" "example" { + site_config { + scm_ip_restriction_default_action = "Allow" + } +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermLinuxWebAppScmIPRestrictionDefaultAction(), + Message: "scm_ip_restriction_default_action should be Deny", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 4, Column: 39}, + End: hcl.Pos{Line: 4, Column: 46}, + }, + }, + }, + }, + { + Name: "scm_ip_restriction_default_action attribute missing", + Content: ` +resource "azurerm_linux_web_app" "example" { + site_config { + } +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermLinuxWebAppScmIPRestrictionDefaultAction(), + Message: "scm_ip_restriction_default_action is not defined and should be Deny", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 2, Column: 1}, + End: hcl.Pos{Line: 2, Column: 43}, + }, + }, + }, + }, + { + Name: "site_config block missing", + Content: ` +resource "azurerm_linux_web_app" "example" { +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermLinuxWebAppScmIPRestrictionDefaultAction(), + Message: "scm_ip_restriction_default_action is not defined and should be Deny", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 2, Column: 1}, + End: hcl.Pos{Line: 2, Column: 43}, + }, + }, + }, + }, + { + Name: "scm_ip_restriction_default_action Deny", + Content: ` +resource "azurerm_linux_web_app" "example" { + site_config { + scm_ip_restriction_default_action = "Deny" + } +}`, + Expected: helper.Issues{}, + }, + } + + rule := NewAzurermLinuxWebAppScmIPRestrictionDefaultAction() + + 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) + }) + } +} diff --git a/rules/azurerm_windows_function_app_scm_ip_restriction_default_action.go b/rules/azurerm_windows_function_app_scm_ip_restriction_default_action.go new file mode 100644 index 0000000..48e86ee --- /dev/null +++ b/rules/azurerm_windows_function_app_scm_ip_restriction_default_action.go @@ -0,0 +1,107 @@ +package rules + +import ( + "github.com/terraform-linters/tflint-plugin-sdk/hclext" + "github.com/terraform-linters/tflint-plugin-sdk/tflint" + + "github.com/terraform-linters/tflint-ruleset-azurerm-security/project" +) + +// AzurermWindowsFunctionAppScmIPRestrictionDefaultAction checks that scm_ip_restriction_default_action is set to "Deny" +type AzurermWindowsFunctionAppScmIPRestrictionDefaultAction struct { + tflint.DefaultRule + + resourceType string +} + +// NewAzurermWindowsFunctionAppScmIPRestrictionDefaultAction returns a new rule instance +func NewAzurermWindowsFunctionAppScmIPRestrictionDefaultAction() *AzurermWindowsFunctionAppScmIPRestrictionDefaultAction { + return &AzurermWindowsFunctionAppScmIPRestrictionDefaultAction{ + resourceType: "azurerm_windows_function_app", + } +} + +// Name returns the rule name +func (r *AzurermWindowsFunctionAppScmIPRestrictionDefaultAction) Name() string { + return "azurerm_windows_function_app_scm_ip_restriction_default_action" +} + +// Enabled returns whether the rule is enabled by default +func (r *AzurermWindowsFunctionAppScmIPRestrictionDefaultAction) Enabled() bool { + return true +} + +// Severity returns the rule severity +func (r *AzurermWindowsFunctionAppScmIPRestrictionDefaultAction) Severity() tflint.Severity { + return tflint.WARNING +} + +// Link returns the rule reference link +func (r *AzurermWindowsFunctionAppScmIPRestrictionDefaultAction) Link() string { + return project.ReferenceLink(r.Name()) +} + +// Check verifies that scm_ip_restriction_default_action is set to "Deny" +func (r *AzurermWindowsFunctionAppScmIPRestrictionDefaultAction) Check(runner tflint.Runner) error { + resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{ + Blocks: []hclext.BlockSchema{ + { + Type: "site_config", + Body: &hclext.BodySchema{ + Attributes: []hclext.AttributeSchema{ + {Name: "scm_ip_restriction_default_action"}, + }, + }, + }, + }, + }, nil) + if err != nil { + return err + } + + for _, resource := range resources.Blocks { + // Check if site_config block exists + hasSiteConfig := false + for _, block := range resource.Body.Blocks { + if block.Type == "site_config" { + hasSiteConfig = true + + // Check if scm_ip_restriction_default_action attribute exists + if attr, exists := block.Body.Attributes["scm_ip_restriction_default_action"]; exists { + err := runner.EvaluateExpr(attr.Expr, func(val string) error { + if val != "Deny" { + runner.EmitIssue( + r, + "scm_ip_restriction_default_action should be Deny", + attr.Expr.Range(), + ) + } + return nil + }, nil) + if err != nil { + return err + } + } else { + // Attribute is missing in site_config block + runner.EmitIssue( + r, + "scm_ip_restriction_default_action is not defined and should be Deny", + resource.DefRange, + ) + } + break + } + } + + // If site_config block doesn't exist + if !hasSiteConfig { + runner.EmitIssue( + r, + "scm_ip_restriction_default_action is not defined and should be Deny", + resource.DefRange, + ) + } + } + + return nil +} diff --git a/rules/azurerm_windows_function_app_scm_ip_restriction_default_action_test.go b/rules/azurerm_windows_function_app_scm_ip_restriction_default_action_test.go new file mode 100644 index 0000000..9a3bc44 --- /dev/null +++ b/rules/azurerm_windows_function_app_scm_ip_restriction_default_action_test.go @@ -0,0 +1,97 @@ +package rules + +import ( + "testing" + + hcl "github.com/hashicorp/hcl/v2" + "github.com/terraform-linters/tflint-plugin-sdk/helper" +) + +func Test_AzurermWindowsFunctionAppScmIPRestrictionDefaultAction(t *testing.T) { + tests := []struct { + Name string + Content string + Expected helper.Issues + }{ + { + Name: "scm_ip_restriction_default_action allowed", + Content: ` +resource "azurerm_windows_function_app" "example" { + site_config { + scm_ip_restriction_default_action = "Allow" + } +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermWindowsFunctionAppScmIPRestrictionDefaultAction(), + Message: "scm_ip_restriction_default_action should be Deny", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 4, Column: 39}, + End: hcl.Pos{Line: 4, Column: 46}, + }, + }, + }, + }, + { + Name: "scm_ip_restriction_default_action attribute missing", + Content: ` +resource "azurerm_windows_function_app" "example" { + site_config { + } +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermWindowsFunctionAppScmIPRestrictionDefaultAction(), + Message: "scm_ip_restriction_default_action is not defined and should be Deny", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 2, Column: 1}, + End: hcl.Pos{Line: 2, Column: 50}, + }, + }, + }, + }, + { + Name: "site_config block missing", + Content: ` +resource "azurerm_windows_function_app" "example" { +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermWindowsFunctionAppScmIPRestrictionDefaultAction(), + Message: "scm_ip_restriction_default_action is not defined and should be Deny", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 2, Column: 1}, + End: hcl.Pos{Line: 2, Column: 50}, + }, + }, + }, + }, + { + Name: "scm_ip_restriction_default_action Deny", + Content: ` +resource "azurerm_windows_function_app" "example" { + site_config { + scm_ip_restriction_default_action = "Deny" + } +}`, + Expected: helper.Issues{}, + }, + } + + rule := NewAzurermWindowsFunctionAppScmIPRestrictionDefaultAction() + + 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) + }) + } +} diff --git a/rules/azurerm_windows_web_app_scm_ip_restriction_default_action.go b/rules/azurerm_windows_web_app_scm_ip_restriction_default_action.go new file mode 100644 index 0000000..32c377e --- /dev/null +++ b/rules/azurerm_windows_web_app_scm_ip_restriction_default_action.go @@ -0,0 +1,107 @@ +package rules + +import ( + "github.com/terraform-linters/tflint-plugin-sdk/hclext" + "github.com/terraform-linters/tflint-plugin-sdk/tflint" + + "github.com/terraform-linters/tflint-ruleset-azurerm-security/project" +) + +// AzurermWindowsWebAppScmIPRestrictionDefaultAction checks that scm_ip_restriction_default_action is set to "Deny" +type AzurermWindowsWebAppScmIPRestrictionDefaultAction struct { + tflint.DefaultRule + + resourceType string +} + +// NewAzurermWindowsWebAppScmIPRestrictionDefaultAction returns a new rule instance +func NewAzurermWindowsWebAppScmIPRestrictionDefaultAction() *AzurermWindowsWebAppScmIPRestrictionDefaultAction { + return &AzurermWindowsWebAppScmIPRestrictionDefaultAction{ + resourceType: "azurerm_windows_web_app", + } +} + +// Name returns the rule name +func (r *AzurermWindowsWebAppScmIPRestrictionDefaultAction) Name() string { + return "azurerm_windows_web_app_scm_ip_restriction_default_action" +} + +// Enabled returns whether the rule is enabled by default +func (r *AzurermWindowsWebAppScmIPRestrictionDefaultAction) Enabled() bool { + return true +} + +// Severity returns the rule severity +func (r *AzurermWindowsWebAppScmIPRestrictionDefaultAction) Severity() tflint.Severity { + return tflint.WARNING +} + +// Link returns the rule reference link +func (r *AzurermWindowsWebAppScmIPRestrictionDefaultAction) Link() string { + return project.ReferenceLink(r.Name()) +} + +// Check verifies that scm_ip_restriction_default_action is set to "Deny" +func (r *AzurermWindowsWebAppScmIPRestrictionDefaultAction) Check(runner tflint.Runner) error { + resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{ + Blocks: []hclext.BlockSchema{ + { + Type: "site_config", + Body: &hclext.BodySchema{ + Attributes: []hclext.AttributeSchema{ + {Name: "scm_ip_restriction_default_action"}, + }, + }, + }, + }, + }, nil) + if err != nil { + return err + } + + for _, resource := range resources.Blocks { + // Check if site_config block exists + hasSiteConfig := false + for _, block := range resource.Body.Blocks { + if block.Type == "site_config" { + hasSiteConfig = true + + // Check if scm_ip_restriction_default_action attribute exists + if attr, exists := block.Body.Attributes["scm_ip_restriction_default_action"]; exists { + err := runner.EvaluateExpr(attr.Expr, func(val string) error { + if val != "Deny" { + runner.EmitIssue( + r, + "scm_ip_restriction_default_action should be Deny", + attr.Expr.Range(), + ) + } + return nil + }, nil) + if err != nil { + return err + } + } else { + // Attribute is missing in site_config block + runner.EmitIssue( + r, + "scm_ip_restriction_default_action is not defined and should be Deny", + resource.DefRange, + ) + } + break + } + } + + // If site_config block doesn't exist + if !hasSiteConfig { + runner.EmitIssue( + r, + "scm_ip_restriction_default_action is not defined and should be Deny", + resource.DefRange, + ) + } + } + + return nil +} diff --git a/rules/azurerm_windows_web_app_scm_ip_restriction_default_action_test.go b/rules/azurerm_windows_web_app_scm_ip_restriction_default_action_test.go new file mode 100644 index 0000000..8be08b2 --- /dev/null +++ b/rules/azurerm_windows_web_app_scm_ip_restriction_default_action_test.go @@ -0,0 +1,97 @@ +package rules + +import ( + "testing" + + hcl "github.com/hashicorp/hcl/v2" + "github.com/terraform-linters/tflint-plugin-sdk/helper" +) + +func Test_AzurermWindowsWebAppScmIPRestrictionDefaultAction(t *testing.T) { + tests := []struct { + Name string + Content string + Expected helper.Issues + }{ + { + Name: "scm_ip_restriction_default_action allowed", + Content: ` +resource "azurerm_windows_web_app" "example" { + site_config { + scm_ip_restriction_default_action = "Allow" + } +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermWindowsWebAppScmIPRestrictionDefaultAction(), + Message: "scm_ip_restriction_default_action should be Deny", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 4, Column: 39}, + End: hcl.Pos{Line: 4, Column: 46}, + }, + }, + }, + }, + { + Name: "scm_ip_restriction_default_action attribute missing", + Content: ` +resource "azurerm_windows_web_app" "example" { + site_config { + } +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermWindowsWebAppScmIPRestrictionDefaultAction(), + Message: "scm_ip_restriction_default_action is not defined and should be Deny", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 2, Column: 1}, + End: hcl.Pos{Line: 2, Column: 45}, + }, + }, + }, + }, + { + Name: "site_config block missing", + Content: ` +resource "azurerm_windows_web_app" "example" { +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermWindowsWebAppScmIPRestrictionDefaultAction(), + Message: "scm_ip_restriction_default_action is not defined and should be Deny", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 2, Column: 1}, + End: hcl.Pos{Line: 2, Column: 45}, + }, + }, + }, + }, + { + Name: "scm_ip_restriction_default_action Deny", + Content: ` +resource "azurerm_windows_web_app" "example" { + site_config { + scm_ip_restriction_default_action = "Deny" + } +}`, + Expected: helper.Issues{}, + }, + } + + rule := NewAzurermWindowsWebAppScmIPRestrictionDefaultAction() + + 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) + }) + } +}