diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3d9665e..83ac5de 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,3 +1 @@ -- [ ] Rule is added in main -- [ ] Rule is documented in Readme - [ ] Includes tests diff --git a/docs/README.md b/docs/README.md index 06d6139..055fe79 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,73 +1,92 @@ # Rules -## azurerm_eventhub_namespace -|Name|Description|Severity|Enabled| -| --- | --- | --- | --- | -|[azurerm_eventhub_namespace_public_network_access_enabled](./rules/azurerm_eventhub_namespace_public_network_access_enabled.md)|Consider disabling public network access on eventhubs. |NOTICE|✔| -|[azurerm_eventhub_namespace_minimum_tls_version](./rules/azurerm_eventhub_namespace_unsecure_tls.md)|Enforce TLS 1.2 on event hubs |WARNING|✔| - -## azurerm_iothub_endpoint_eventhub -|Name|Description|Severity|Enabled| -| --- | --- | --- | --- | -|[azurerm_iothub_endpoint_eventhub_authentication_type](./rules/azurerm_iothub_endpoint_eventhub_authentication_type.md)|Consider using managed identity to authenticate agains eventhub. |NOTICE|| - -## azurerm_key_vault -|Name|Description|Severity|Enabled| -| --- | --- | --- | --- | -|[azurerm_key_vault_public_network_access_enabled](./rules/azurerm_key_vault_public_network_access_enabled.md)|Consider disabling public network access on keyvaults. |NOTICE|| -|[azurerm_key_vault_network_acls_default_deny](./rules/azurerm_key_vault_network_acls_default_deny.md)|Deny network access to Keyvaults. You can add `bypass = "AzureServices"` to allow azure services to connect to keyvault or add `ip_rules`|WARNING|✔| - -## azurerm_linux_function_app -|Name|Description|Severity|Enabled| -| --- | --- | --- | --- | -|[azurerm_linux_function_app_ftps_state](./rules/azurerm_linux_function_app_ftps_state.md)|Disable sftp to a linux function app |WARNING|✔| -|[azurerm_linux_function_app_https_only](./rules/azurerm_linux_function_app_https_only.md)|Force all traffic over https |WARNING|✔| -|[azurerm_linux_function_app_minimum_tls_version](./rules/azurerm_linux_function_app_minimum_tls_version.md)|Enforce TLS 1.2 on linux function apps |WARNING|✔| - -## azurerm_linux_web_app -|Name|Description|Severity|Enabled| -| --- | --- | --- | --- | -|[azurerm_linux_web_app_ftps_state](./rules/azurerm_linux_web_app_ftps_state.md)|Disable sftp to a linux web app |WARNING|✔| -|[azurerm_linux_web_app_https_only](./rules/azurerm_linux_web_app_https_only.md)|Force all traffic over https |WARNING|✔| -|[azurerm_linux_web_app_minimum_tls_version](./rules/azurerm_linux_web_app_minimum_tls_version.md)|Enforce TLS 1.2 on linux web apps |WARNING|✔| - -## azurerm_mssql_database -|Name|Description|Severity|Enabled| -| --- | --- | --- | --- | -|[azurerm_mssql_database_transparent_data_encryption_enabled](./rules/azurerm_mssql_database_encryption.md)|Enforce transparant data encryption|WARNING|✔| - -## azurerm_mssql_server -|Name|Description|Severity|Enabled| -| --- | --- | --- | --- | -|[azurerm_mssql_server_azuread_authentication_only](./rules/azurerm_mssql_server_azuread_authentication_only.md)|Only user Azure AD authentication to SQL |WARNING|✔| -|[azurerm_mssql_server_public_network_access_enabled](./rules/azurerm_mssql_server_public_network_access_enabled.md)|Consider disabling public network access on SQL servers. |NOTICE|✔| -|[azurerm_mssql_server_minimum_tls_version](./rules/azurerm_mssql_server_unsecure_tls.md)|Enforce TLS 1.2 on SQL servers. |WARNING|✔| - -## azurerm_mssql_firewall_rule - -|Name|Description|Severity|Enabled| -| --- | --- | --- | --- | -|[azurerm_mssql_firewall_rule_all_allowed](./rules/azurerm_mssql_firewall_rule_all_allowed.md)|Remove a firewall rule that allows any ip.|ERROR|✔| - - -## azurerm_storage_account -|Name|Description|Severity|Enabled| -| --- | --- | --- | --- | -|[azurerm_storage_account_https_traffic_only_enabled](./rules/azurerm_storage_account_https_traffic_only_enabled.md)|Enforce all traffic to use https on storage accounts|WARNING|✔| -|[azurerm_storage_account_public_network_access_enabled](./rules/azurerm_storage_account_public_network_access_enabled.md)|Consider disabling public network access on storage accounts. |NOTICE|✔| -|[azurerm_storage_account_tls_version](./rules/azurerm_storage_account_unsecure_tls.md)|Enforce TLS 1.2 on storage accounts |WARNING|✔| - -## azurerm_windows_function_app -|Name|Description|Severity|Enabled| -| --- | --- | --- | --- | -|[azurerm_windows_function_app_ftps_state](./rules/azurerm_windows_function_app_ftps_state.md)|Disable sftp to a windows function app |WARNING|✔| -|[azurerm_windows_function_app_https_only](./rules/azurerm_windows_function_app_https_only.md)|Force all traffic over https |WARNING|✔| -|[azurerm_windows_function_app_minimum_tls_version](./rules/azurerm_windows_function_app_minimum_tls_version.md)|Enforce TLS 1.2 on windows function apps |WARNING|✔| - - -## azurerm_windows_web_app -|Name|Description|Severity|Enabled| -| --- | --- | --- | --- | -|[azurerm_windows_web_app_ftps_state](./rules/azurerm_windows_web_app_ftps_state.md)|Disable sftp to a windows web app |WARNING|✔| -|[azurerm_windows_web_app_https_only](./rules/azurerm_windows_web_app_https_only.)|Force all traffic over https |WARNING|✔| -|[azurerm_windows_web_app_minimum_tls_version](./rules/azurerm_windows_web_app_minimum_tls_version.md)|Enforce TLS 1.2 on windows web apps |WARNING|✔| +## Rules Index + +|Name|Severity|Enabled| +| --- | --- | --- | +|[azurerm_eventhub_namespace_public_network_access_enabled](./rules/azurerm_eventhub_namespace_public_network_access_enabled.md)|Notice|✔| +|[azurerm_eventhub_namespace_unsecure_tls](./rules/azurerm_eventhub_namespace_unsecure_tls.md)|Warning|✔| +|[azurerm_iothub_endpoint_eventhub_authentication_type](./rules/azurerm_iothub_endpoint_eventhub_authentication_type.md)|Notice|✔| +|[azurerm_key_vault_network_acls_default_deny](./rules/azurerm_key_vault_network_acls_default_deny.md)|Warning|✔| +|[azurerm_key_vault_public_network_access_enabled](./rules/azurerm_key_vault_public_network_access_enabled.md)|Notice|| +|[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_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_mssql_database_encryption](./rules/azurerm_mssql_database_encryption.md)|Warning|✔| +|[azurerm_mssql_firewall_rule_all_allowed](./rules/azurerm_mssql_firewall_rule_all_allowed.md)|Error|✔| +|[azurerm_mssql_server_azuread_authentication_only](./rules/azurerm_mssql_server_azuread_authentication_only.md)|Warning|✔| +|[azurerm_mssql_server_public_network_access_enabled](./rules/azurerm_mssql_server_public_network_access_enabled.md)|Notice|✔| +|[azurerm_mssql_server_unsecure_tls](./rules/azurerm_mssql_server_unsecure_tls.md)|Warning|✔| +|[azurerm_storage_account_https_traffic_only_enabled](./rules/azurerm_storage_account_https_traffic_only_enabled.md)|Warning|✔| +|[azurerm_storage_account_public_network_access_enabled](./rules/azurerm_storage_account_public_network_access_enabled.md)|Notice|✔| +|[azurerm_storage_account_unsecure_tls](./rules/azurerm_storage_account_unsecure_tls.md)|Warning|✔| +|[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_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|✔| + +## Rules by Resource + +### azurerm_eventhub_namespace + +- [azurerm_eventhub_namespace_public_network_access_enabled](./rules/azurerm_eventhub_namespace_public_network_access_enabled.md) +- [azurerm_eventhub_namespace_unsecure_tls](./rules/azurerm_eventhub_namespace_unsecure_tls.md) + +### azurerm_iothub_endpoint_eventhub + +- [azurerm_iothub_endpoint_eventhub_authentication_type](./rules/azurerm_iothub_endpoint_eventhub_authentication_type.md) + +### azurerm_key_vault + +- [azurerm_key_vault_network_acls_default_deny](./rules/azurerm_key_vault_network_acls_default_deny.md) +- [azurerm_key_vault_public_network_access_enabled](./rules/azurerm_key_vault_public_network_access_enabled.md) + +### azurerm_linux_function_app + +- [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_web_app + +- [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_mssql_database + +- [azurerm_mssql_database_encryption](./rules/azurerm_mssql_database_encryption.md) + +### azurerm_mssql_firewall_rule + +- [azurerm_mssql_firewall_rule_all_allowed](./rules/azurerm_mssql_firewall_rule_all_allowed.md) + +### azurerm_mssql_server + +- [azurerm_mssql_server_azuread_authentication_only](./rules/azurerm_mssql_server_azuread_authentication_only.md) +- [azurerm_mssql_server_public_network_access_enabled](./rules/azurerm_mssql_server_public_network_access_enabled.md) +- [azurerm_mssql_server_unsecure_tls](./rules/azurerm_mssql_server_unsecure_tls.md) + +### azurerm_storage_account + +- [azurerm_storage_account_https_traffic_only_enabled](./rules/azurerm_storage_account_https_traffic_only_enabled.md) +- [azurerm_storage_account_public_network_access_enabled](./rules/azurerm_storage_account_public_network_access_enabled.md) +- [azurerm_storage_account_unsecure_tls](./rules/azurerm_storage_account_unsecure_tls.md) + +### azurerm_windows_function_app + +- [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_web_app + +- [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) + diff --git a/main_test.go b/main_test.go index 17f0af5..9b0b858 100644 --- a/main_test.go +++ b/main_test.go @@ -3,11 +3,15 @@ package main import ( "bytes" + "fmt" "os" "path/filepath" "reflect" + "sort" "strings" "testing" + + "github.com/terraform-linters/tflint-plugin-sdk/tflint" ) func TestRulesLength(t *testing.T) { @@ -147,4 +151,137 @@ func TestRuleDocumentation(t *testing.T) { } } } +} + +// main_test.go + +func TestRulesReadme(t *testing.T) { + ruleSet := createRuleSet() + readmePath := "./docs/README.md" + + // Create rules map grouped by resource type + rulesByType := make(map[string][]tflint.Rule) + var allRules []tflint.Rule + + // Group rules by resource type and collect all rules for the table + for _, rule := range ruleSet.Rules { + ruleValue := reflect.ValueOf(rule) + resourceTypeField := ruleValue.Elem().FieldByName("resourceType") + + if !resourceTypeField.IsValid() { + t.Errorf("Rule %s does not have a resourceType field", rule.Name()) + continue + } + + resourceType := resourceTypeField.String() + rulesByType[resourceType] = append(rulesByType[resourceType], rule) + allRules = append(allRules, rule) + } + + // Sort all rules by name for the table + sort.Slice(allRules, func(i, j int) bool { + return allRules[i].Name() < allRules[j].Name() + }) + + // Sort resource types + var resourceTypes []string + for resourceType := range rulesByType { + resourceTypes = append(resourceTypes, resourceType) + } + sort.Strings(resourceTypes) + + // Read existing README content + var existingContent string + existingContentBytes, err := os.ReadFile(readmePath) + if err == nil { + existingContent = string(existingContentBytes) + } + + // Generate new content + var content strings.Builder + content.WriteString("# Rules\n\n") + + // Create rules table + content.WriteString("## Rules Index\n\n") + content.WriteString("|Name|Severity|Enabled|\n") + content.WriteString("| --- | --- | --- |\n") + + // Generate table entries + for _, rule := range allRules { + enabled := "" + if rule.Enabled() { + enabled = "✔" + } + content.WriteString(fmt.Sprintf("|[%s](./rules/%s.md)|%s|%s|\n", + rule.Name(), + rule.Name(), + rule.Severity().String(), + enabled)) + } + content.WriteString("\n") + + // Add resource type sections + content.WriteString("## Rules by Resource\n\n") + for _, resourceType := range resourceTypes { + content.WriteString(fmt.Sprintf("### %s\n\n", resourceType)) + rules := rulesByType[resourceType] + + // Sort rules within each resource type + sort.Slice(rules, func(i, j int) bool { + return rules[i].Name() < rules[j].Name() + }) + + for _, rule := range rules { + content.WriteString(fmt.Sprintf("- [%s](./rules/%s.md)\n", rule.Name(), rule.Name())) + } + content.WriteString("\n") + } + + // Check if content needs to be updated + if existingContent != content.String() { + err := os.WriteFile(readmePath, []byte(content.String()), 0644) + if err != nil { + t.Fatalf("Failed to write README.md: %v", err) + } + t.Log("Updated rules/README.md with new content") + } + + // Verify the content structure + currentContent, err := os.ReadFile(readmePath) + if err != nil { + t.Fatalf("Failed to read README.md for verification: %v", err) + } + + // Verify required sections exist + requiredSections := []string{ + "# Rules", + "## Rules Index", + "## Rules by Resource", + } + + for _, section := range requiredSections { + if !strings.Contains(string(currentContent), section) { + t.Errorf("README.md is missing required section: %s", section) + } + } + + // Verify table structure + if !strings.Contains(string(currentContent), "|Name|Severity|Enabled|") { + t.Error("README.md is missing required table header") + } + + // Verify all rules are included + for _, rule := range allRules { + ruleName := rule.Name() + if !strings.Contains(string(currentContent), fmt.Sprintf("[%s](./rules/%s.md)", ruleName, ruleName)) { + t.Errorf("README.md is missing rule: %s", ruleName) + } + } + + // Verify resource type sections + for _, resourceType := range resourceTypes { + if !strings.Contains(string(currentContent), fmt.Sprintf("### %s", resourceType)) { + t.Errorf("README.md is missing resource type section: %s", resourceType) + } + } } \ No newline at end of file