prowler 5.17.0__py3-none-any.whl → 5.18.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dashboard/compliance/hipaa_azure.py +25 -0
- dashboard/pages/overview.py +20 -11
- prowler/AGENTS.md +1 -1
- prowler/CHANGELOG.md +43 -0
- prowler/__main__.py +5 -0
- prowler/compliance/azure/hipaa_azure.json +820 -0
- prowler/compliance/m365/cis_4.0_m365.json +6 -2
- prowler/compliance/m365/cis_6.0_m365.json +6 -2
- prowler/compliance/m365/iso27001_2022_m365.json +13 -11
- prowler/compliance/openstack/__init__.py +0 -0
- prowler/config/config.py +2 -1
- prowler/config/config.yaml +4 -1
- prowler/config/openstack_mutelist_example.yaml +60 -0
- prowler/lib/check/check.py +4 -0
- prowler/lib/check/models.py +27 -2
- prowler/lib/cli/parser.py +3 -2
- prowler/lib/outputs/finding.py +14 -0
- prowler/lib/outputs/html/html.py +72 -0
- prowler/lib/outputs/jira/jira.py +3 -3
- prowler/lib/outputs/outputs.py +2 -0
- prowler/lib/outputs/summary_table.py +7 -0
- prowler/lib/timeline/__init__.py +0 -0
- prowler/lib/timeline/models.py +27 -0
- prowler/lib/timeline/timeline.py +36 -0
- prowler/providers/aws/lib/cloudtrail_timeline/__init__.py +0 -0
- prowler/providers/aws/lib/cloudtrail_timeline/cloudtrail_timeline.py +218 -0
- prowler/providers/aws/services/codebuild/codebuild_project_webhook_filters_use_anchored_patterns/__init__.py +0 -0
- prowler/providers/aws/services/codebuild/codebuild_project_webhook_filters_use_anchored_patterns/codebuild_project_webhook_filters_use_anchored_patterns.metadata.json +40 -0
- prowler/providers/aws/services/codebuild/codebuild_project_webhook_filters_use_anchored_patterns/codebuild_project_webhook_filters_use_anchored_patterns.py +58 -0
- prowler/providers/aws/services/codebuild/codebuild_service.py +45 -0
- prowler/providers/aws/services/dynamodb/dynamodb_table_cross_account_access/dynamodb_table_cross_account_access.metadata.json +1 -1
- prowler/providers/aws/services/dynamodb/dynamodb_table_cross_account_access/dynamodb_table_cross_account_access.py +4 -0
- prowler/providers/aws/services/eventbridge/eventbridge_bus_cross_account_access/eventbridge_bus_cross_account_access.metadata.json +1 -1
- prowler/providers/aws/services/eventbridge/eventbridge_bus_cross_account_access/eventbridge_bus_cross_account_access.py +4 -0
- prowler/providers/aws/services/eventbridge/eventbridge_schema_registry_cross_account_access/eventbridge_schema_registry_cross_account_access.metadata.json +1 -1
- prowler/providers/aws/services/eventbridge/eventbridge_schema_registry_cross_account_access/eventbridge_schema_registry_cross_account_access.py +2 -0
- prowler/providers/aws/services/iam/lib/policy.py +19 -3
- prowler/providers/aws/services/rds/rds_instance_extended_support/__init__.py +0 -0
- prowler/providers/aws/services/rds/rds_instance_extended_support/rds_instance_extended_support.metadata.json +41 -0
- prowler/providers/aws/services/rds/rds_instance_extended_support/rds_instance_extended_support.py +37 -0
- prowler/providers/aws/services/rds/rds_service.py +4 -0
- prowler/providers/aws/services/s3/s3_bucket_cross_account_access/s3_bucket_cross_account_access.metadata.json +1 -1
- prowler/providers/aws/services/s3/s3_bucket_cross_account_access/s3_bucket_cross_account_access.py +5 -1
- prowler/providers/azure/lib/service/service.py +23 -0
- prowler/providers/azure/services/app/app_client_certificates_on/app_client_certificates_on.metadata.json +18 -12
- prowler/providers/azure/services/app/app_ensure_auth_is_set_up/app_ensure_auth_is_set_up.metadata.json +18 -11
- prowler/providers/azure/services/app/app_ensure_http_is_redirected_to_https/app_ensure_http_is_redirected_to_https.metadata.json +19 -12
- prowler/providers/azure/services/app/app_ensure_java_version_is_latest/app_ensure_java_version_is_latest.metadata.json +19 -12
- prowler/providers/azure/services/app/app_ensure_php_version_is_latest/app_ensure_php_version_is_latest.metadata.json +19 -12
- prowler/providers/azure/services/app/app_ensure_python_version_is_latest/app_ensure_python_version_is_latest.metadata.json +19 -12
- prowler/providers/azure/services/app/app_ensure_using_http20/app_ensure_using_http20.metadata.json +18 -11
- prowler/providers/azure/services/app/app_ftp_deployment_disabled/app_ftp_deployment_disabled.metadata.json +21 -13
- prowler/providers/azure/services/app/app_function_access_keys_configured/app_function_access_keys_configured.metadata.json +19 -11
- prowler/providers/azure/services/app/app_function_application_insights_enabled/app_function_application_insights_enabled.metadata.json +21 -14
- prowler/providers/azure/services/app/app_function_ftps_deployment_disabled/app_function_ftps_deployment_disabled.metadata.json +18 -13
- prowler/providers/azure/services/app/app_function_identity_is_configured/app_function_identity_is_configured.metadata.json +20 -13
- prowler/providers/azure/services/app/app_function_identity_without_admin_privileges/app_function_identity_without_admin_privileges.metadata.json +18 -11
- prowler/providers/azure/services/app/app_function_latest_runtime_version/app_function_latest_runtime_version.metadata.json +20 -13
- prowler/providers/azure/services/app/app_function_not_publicly_accessible/app_function_not_publicly_accessible.metadata.json +20 -13
- prowler/providers/azure/services/app/app_function_vnet_integration_enabled/app_function_vnet_integration_enabled.metadata.json +21 -14
- prowler/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled.metadata.json +18 -12
- prowler/providers/azure/services/app/app_minimum_tls_version_12/app_minimum_tls_version_12.metadata.json +20 -12
- prowler/providers/azure/services/app/app_register_with_identity/app_register_with_identity.metadata.json +18 -11
- prowler/providers/azure/services/appinsights/appinsights_ensure_is_configured/appinsights_ensure_is_configured.metadata.json +18 -12
- prowler/providers/azure/services/containerregistry/containerregistry_admin_user_disabled/containerregistry_admin_user_disabled.metadata.json +17 -11
- prowler/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible.metadata.json +18 -12
- prowler/providers/azure/services/containerregistry/containerregistry_uses_private_link/containerregistry_uses_private_link.metadata.json +21 -13
- prowler/providers/azure/services/cosmosdb/cosmosdb_account_firewall_use_selected_networks/cosmosdb_account_firewall_use_selected_networks.metadata.json +20 -12
- prowler/providers/azure/services/cosmosdb/cosmosdb_account_use_aad_and_rbac/cosmosdb_account_use_aad_and_rbac.metadata.json +19 -13
- prowler/providers/azure/services/cosmosdb/cosmosdb_account_use_private_endpoints/cosmosdb_account_use_private_endpoints.metadata.json +20 -13
- prowler/providers/azure/services/databricks/databricks_workspace_cmk_encryption_enabled/databricks_workspace_cmk_encryption_enabled.metadata.json +20 -14
- prowler/providers/azure/services/databricks/databricks_workspace_vnet_injection_enabled/databricks_workspace_vnet_injection_enabled.metadata.json +20 -14
- prowler/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact.metadata.json +20 -13
- prowler/providers/azure/services/defender/defender_assessments_vm_endpoint_protection_installed/defender_assessments_vm_endpoint_protection_installed.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_attack_path_notifications_properly_configured/defender_attack_path_notifications_properly_configured.metadata.json +19 -13
- prowler/providers/azure/services/defender/defender_auto_provisioning_log_analytics_agent_vms_on/defender_auto_provisioning_log_analytics_agent_vms_on.metadata.json +20 -13
- prowler/providers/azure/services/defender/defender_auto_provisioning_vulnerabilty_assessments_machines_on/defender_auto_provisioning_vulnerabilty_assessments_machines_on.metadata.json +19 -12
- prowler/providers/azure/services/defender/defender_container_images_resolved_vulnerabilities/defender_container_images_resolved_vulnerabilities.metadata.json +20 -12
- prowler/providers/azure/services/defender/defender_container_images_scan_enabled/defender_container_images_scan_enabled.metadata.json +22 -13
- prowler/providers/azure/services/defender/defender_ensure_defender_for_app_services_is_on/defender_ensure_defender_for_app_services_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_arm_is_on/defender_ensure_defender_for_arm_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_azure_sql_databases_is_on/defender_ensure_defender_for_azure_sql_databases_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_containers_is_on/defender_ensure_defender_for_containers_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_cosmosdb_is_on/defender_ensure_defender_for_cosmosdb_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_databases_is_on/defender_ensure_defender_for_databases_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_dns_is_on/defender_ensure_defender_for_dns_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_keyvault_is_on/defender_ensure_defender_for_keyvault_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_os_relational_databases_is_on/defender_ensure_defender_for_os_relational_databases_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_server_is_on/defender_ensure_defender_for_server_is_on.metadata.json +19 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_sql_servers_is_on/defender_ensure_defender_for_sql_servers_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_defender_for_storage_is_on/defender_ensure_defender_for_storage_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_iot_hub_defender_is_on/defender_ensure_iot_hub_defender_is_on.metadata.json +17 -11
- prowler/providers/azure/services/defender/defender_ensure_mcas_is_enabled/defender_ensure_mcas_is_enabled.metadata.json +20 -12
- prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high.metadata.json +19 -12
- prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners.metadata.json +19 -12
- prowler/providers/azure/services/defender/defender_ensure_system_updates_are_applied/defender_ensure_system_updates_are_applied.metadata.json +17 -9
- prowler/providers/azure/services/defender/defender_ensure_wdatp_is_enabled/defender_ensure_wdatp_is_enabled.metadata.json +21 -13
- prowler/providers/azure/services/entra/entra_service.py +3 -11
- prowler/providers/azure/services/entra/entra_user_with_vm_access_has_mfa/entra_user_with_vm_access_has_mfa.py +6 -0
- prowler/providers/azure/services/iam/iam_custom_role_has_permissions_to_administer_resource_locks/iam_custom_role_has_permissions_to_administer_resource_locks.metadata.json +19 -13
- prowler/providers/azure/services/iam/iam_role_user_access_admin_restricted/iam_role_user_access_admin_restricted.metadata.json +16 -10
- prowler/providers/azure/services/iam/iam_subscription_roles_owner_custom_not_created/iam_subscription_roles_owner_custom_not_created.metadata.json +18 -12
- prowler/providers/azure/services/keyvault/keyvault_rbac_secret_expiration_set/keyvault_rbac_secret_expiration_set.py +10 -11
- prowler/providers/azure/services/keyvault/keyvault_service.py +164 -81
- prowler/providers/azure/services/mysql/mysql_flexible_server_audit_log_connection_activated/mysql_flexible_server_audit_log_connection_activated.metadata.json +18 -12
- prowler/providers/azure/services/mysql/mysql_flexible_server_audit_log_enabled/mysql_flexible_server_audit_log_enabled.metadata.json +19 -12
- prowler/providers/azure/services/mysql/mysql_flexible_server_minimum_tls_version_12/mysql_flexible_server_minimum_tls_version_12.metadata.json +18 -12
- prowler/providers/azure/services/mysql/mysql_flexible_server_ssl_connection_enabled/mysql_flexible_server_ssl_connection_enabled.metadata.json +19 -12
- prowler/providers/azure/services/network/network_bastion_host_exists/network_bastion_host_exists.metadata.json +21 -12
- prowler/providers/azure/services/network/network_flow_log_captured_sent/network_flow_log_captured_sent.metadata.json +19 -12
- prowler/providers/azure/services/network/network_flow_log_more_than_90_days/network_flow_log_more_than_90_days.metadata.json +21 -12
- prowler/providers/azure/services/network/network_http_internet_access_restricted/network_http_internet_access_restricted.metadata.json +18 -12
- prowler/providers/azure/services/network/network_public_ip_shodan/network_public_ip_shodan.metadata.json +15 -10
- prowler/providers/azure/services/network/network_rdp_internet_access_restricted/network_rdp_internet_access_restricted.metadata.json +20 -12
- prowler/providers/azure/services/network/network_ssh_internet_access_restricted/network_ssh_internet_access_restricted.metadata.json +19 -12
- prowler/providers/azure/services/network/network_udp_internet_access_restricted/network_udp_internet_access_restricted.metadata.json +19 -12
- prowler/providers/azure/services/network/network_watcher_enabled/network_watcher_enabled.metadata.json +21 -13
- prowler/providers/azure/services/policy/policy_ensure_asc_enforcement_enabled/policy_ensure_asc_enforcement_enabled.metadata.json +16 -11
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_allow_access_services_disabled/postgresql_flexible_server_allow_access_services_disabled.metadata.json +20 -13
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_connection_throttling_on/postgresql_flexible_server_connection_throttling_on.metadata.json +18 -12
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_enforce_ssl_enabled/postgresql_flexible_server_enforce_ssl_enabled.metadata.json +19 -13
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_entra_id_authentication_enabled/postgresql_flexible_server_entra_id_authentication_enabled.metadata.json +4 -4
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_log_checkpoints_on/postgresql_flexible_server_log_checkpoints_on.metadata.json +19 -13
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_log_connections_on/postgresql_flexible_server_log_connections_on.metadata.json +18 -11
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_log_disconnections_on/postgresql_flexible_server_log_disconnections_on.metadata.json +18 -12
- prowler/providers/azure/services/postgresql/postgresql_flexible_server_log_retention_days_greater_3/postgresql_flexible_server_log_retention_days_greater_3.metadata.json +18 -12
- prowler/providers/azure/services/sqlserver/sqlserver_auditing_enabled/sqlserver_auditing_enabled.metadata.json +20 -13
- prowler/providers/azure/services/sqlserver/sqlserver_auditing_retention_90_days/sqlserver_auditing_retention_90_days.metadata.json +20 -12
- prowler/providers/azure/services/sqlserver/sqlserver_azuread_administrator_enabled/sqlserver_azuread_administrator_enabled.metadata.json +18 -12
- prowler/providers/azure/services/sqlserver/sqlserver_microsoft_defender_enabled/sqlserver_microsoft_defender_enabled.metadata.json +23 -13
- prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version.metadata.json +19 -12
- prowler/providers/azure/services/sqlserver/sqlserver_tde_encrypted_with_cmk/sqlserver_tde_encrypted_with_cmk.metadata.json +20 -13
- prowler/providers/azure/services/sqlserver/sqlserver_tde_encryption_enabled/sqlserver_tde_encryption_enabled.metadata.json +20 -13
- prowler/providers/azure/services/sqlserver/sqlserver_unrestricted_inbound_access/sqlserver_unrestricted_inbound_access.metadata.json +18 -12
- prowler/providers/azure/services/sqlserver/sqlserver_va_emails_notifications_admins_enabled/sqlserver_va_emails_notifications_admins_enabled.metadata.json +19 -12
- prowler/providers/azure/services/sqlserver/sqlserver_va_periodic_recurring_scans_enabled/sqlserver_va_periodic_recurring_scans_enabled.metadata.json +19 -12
- prowler/providers/azure/services/sqlserver/sqlserver_va_scan_reports_configured/sqlserver_va_scan_reports_configured.metadata.json +18 -12
- prowler/providers/azure/services/sqlserver/sqlserver_vulnerability_assessment_enabled/sqlserver_vulnerability_assessment_enabled.metadata.json +19 -12
- prowler/providers/azure/services/storage/storage_account_key_access_disabled/storage_account_key_access_disabled.metadata.json +17 -12
- prowler/providers/azure/services/storage/storage_blob_public_access_level_is_disabled/storage_blob_public_access_level_is_disabled.metadata.json +18 -12
- prowler/providers/azure/services/storage/storage_blob_versioning_is_enabled/storage_blob_versioning_is_enabled.metadata.json +19 -11
- prowler/providers/azure/services/storage/storage_cross_tenant_replication_disabled/storage_cross_tenant_replication_disabled.metadata.json +19 -13
- prowler/providers/azure/services/storage/storage_default_network_access_rule_is_denied/storage_default_network_access_rule_is_denied.metadata.json +19 -12
- prowler/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/storage_default_to_entra_authorization_enabled.metadata.json +20 -13
- prowler/providers/azure/services/storage/storage_ensure_azure_services_are_trusted_to_access_is_enabled/storage_ensure_azure_services_are_trusted_to_access_is_enabled.metadata.json +17 -10
- prowler/providers/azure/services/storage/storage_ensure_encryption_with_customer_managed_keys/storage_ensure_encryption_with_customer_managed_keys.metadata.json +15 -10
- prowler/providers/azure/services/storage/storage_ensure_file_shares_soft_delete_is_enabled/storage_ensure_file_shares_soft_delete_is_enabled.metadata.json +18 -12
- prowler/providers/azure/services/storage/storage_ensure_minimum_tls_version_12/storage_ensure_minimum_tls_version_12.metadata.json +14 -10
- prowler/providers/azure/services/storage/storage_ensure_private_endpoints_in_storage_accounts/storage_ensure_private_endpoints_in_storage_accounts.metadata.json +19 -11
- prowler/providers/azure/services/storage/storage_ensure_soft_delete_is_enabled/storage_ensure_soft_delete_is_enabled.metadata.json +17 -12
- prowler/providers/azure/services/storage/storage_geo_redundant_enabled/storage_geo_redundant_enabled.metadata.json +19 -12
- prowler/providers/azure/services/storage/storage_infrastructure_encryption_is_enabled/storage_infrastructure_encryption_is_enabled.metadata.json +13 -9
- prowler/providers/azure/services/storage/storage_key_rotation_90_days/storage_key_rotation_90_days.metadata.json +17 -12
- prowler/providers/azure/services/storage/storage_secure_transfer_required_is_enabled/storage_secure_transfer_required_is_enabled.metadata.json +15 -11
- prowler/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm.metadata.json +19 -12
- prowler/providers/azure/services/storage/storage_smb_protocol_version_is_latest/storage_smb_protocol_version_is_latest.metadata.json +19 -13
- prowler/providers/cloudflare/cloudflare_provider.py +95 -12
- prowler/providers/cloudflare/lib/arguments/arguments.py +7 -0
- prowler/providers/cloudflare/services/dns/dns_record_cname_target_valid/__init__.py +0 -0
- prowler/providers/cloudflare/services/dns/dns_record_cname_target_valid/dns_record_cname_target_valid.metadata.json +36 -0
- prowler/providers/cloudflare/services/dns/dns_record_cname_target_valid/dns_record_cname_target_valid.py +109 -0
- prowler/providers/cloudflare/services/dns/dns_record_no_internal_ip/__init__.py +0 -0
- prowler/providers/cloudflare/services/dns/dns_record_no_internal_ip/dns_record_no_internal_ip.metadata.json +36 -0
- prowler/providers/cloudflare/services/dns/dns_record_no_internal_ip/dns_record_no_internal_ip.py +73 -0
- prowler/providers/cloudflare/services/dns/dns_record_no_wildcard/__init__.py +0 -0
- prowler/providers/cloudflare/services/dns/dns_record_no_wildcard/dns_record_no_wildcard.metadata.json +36 -0
- prowler/providers/cloudflare/services/dns/dns_record_no_wildcard/dns_record_no_wildcard.py +60 -0
- prowler/providers/cloudflare/services/dns/dns_record_proxied/__init__.py +0 -0
- prowler/providers/cloudflare/services/dns/dns_record_proxied/dns_record_proxied.metadata.json +36 -0
- prowler/providers/cloudflare/services/dns/dns_record_proxied/dns_record_proxied.py +49 -0
- prowler/providers/cloudflare/services/dns/dns_service.py +52 -6
- prowler/providers/cloudflare/services/firewall/__init__.py +0 -0
- prowler/providers/cloudflare/services/firewall/firewall_client.py +4 -0
- prowler/providers/cloudflare/services/firewall/firewall_service.py +123 -0
- prowler/providers/cloudflare/services/zone/zone_firewall_blocking_rules_configured/__init__.py +0 -0
- prowler/providers/cloudflare/services/zone/zone_firewall_blocking_rules_configured/zone_firewall_blocking_rules_configured.metadata.json +36 -0
- prowler/providers/cloudflare/services/zone/zone_firewall_blocking_rules_configured/zone_firewall_blocking_rules_configured.py +53 -0
- prowler/providers/cloudflare/services/zone/zone_service.py +133 -1
- prowler/providers/cloudflare/services/zone/zone_waf_owasp_ruleset_enabled/__init__.py +0 -0
- prowler/providers/cloudflare/services/zone/zone_waf_owasp_ruleset_enabled/zone_waf_owasp_ruleset_enabled.metadata.json +36 -0
- prowler/providers/cloudflare/services/zone/zone_waf_owasp_ruleset_enabled/zone_waf_owasp_ruleset_enabled.py +58 -0
- prowler/providers/common/provider.py +23 -0
- prowler/providers/gcp/services/compute/compute_instance_suspended_without_persistent_disks/__init__.py +0 -0
- prowler/providers/gcp/services/compute/compute_instance_suspended_without_persistent_disks/compute_instance_suspended_without_persistent_disks.metadata.json +37 -0
- prowler/providers/gcp/services/compute/compute_instance_suspended_without_persistent_disks/compute_instance_suspended_without_persistent_disks.py +35 -0
- prowler/providers/gcp/services/compute/compute_service.py +2 -0
- prowler/providers/m365/lib/powershell/m365_powershell.py +47 -1
- prowler/providers/m365/services/defender/defender_service.py +52 -0
- prowler/providers/m365/services/defender/defender_zap_for_teams_enabled/__init__.py +0 -0
- prowler/providers/m365/services/defender/defender_zap_for_teams_enabled/defender_zap_for_teams_enabled.metadata.json +38 -0
- prowler/providers/m365/services/defender/defender_zap_for_teams_enabled/defender_zap_for_teams_enabled.py +53 -0
- prowler/providers/m365/services/exchange/exchange_service.py +78 -0
- prowler/providers/m365/services/exchange/exchange_shared_mailbox_sign_in_disabled/__init__.py +0 -0
- prowler/providers/m365/services/exchange/exchange_shared_mailbox_sign_in_disabled/exchange_shared_mailbox_sign_in_disabled.metadata.json +37 -0
- prowler/providers/m365/services/exchange/exchange_shared_mailbox_sign_in_disabled/exchange_shared_mailbox_sign_in_disabled.py +59 -0
- prowler/providers/openstack/__init__.py +0 -0
- prowler/providers/openstack/exceptions/__init__.py +0 -0
- prowler/providers/openstack/exceptions/exceptions.py +166 -0
- prowler/providers/openstack/lib/__init__.py +0 -0
- prowler/providers/openstack/lib/arguments/__init__.py +0 -0
- prowler/providers/openstack/lib/arguments/arguments.py +113 -0
- prowler/providers/openstack/lib/mutelist/__init__.py +0 -0
- prowler/providers/openstack/lib/mutelist/mutelist.py +31 -0
- prowler/providers/openstack/lib/service/__init__.py +0 -0
- prowler/providers/openstack/lib/service/service.py +21 -0
- prowler/providers/openstack/models.py +100 -0
- prowler/providers/openstack/openstack_provider.py +515 -0
- prowler/providers/openstack/services/__init__.py +0 -0
- prowler/providers/openstack/services/compute/__init__.py +0 -0
- prowler/providers/openstack/services/compute/compute_client.py +4 -0
- prowler/providers/openstack/services/compute/compute_instance_security_groups_attached/__init__.py +0 -0
- prowler/providers/openstack/services/compute/compute_instance_security_groups_attached/compute_instance_security_groups_attached.metadata.json +40 -0
- prowler/providers/openstack/services/compute/compute_instance_security_groups_attached/compute_instance_security_groups_attached.py +35 -0
- prowler/providers/openstack/services/compute/compute_service.py +63 -0
- {prowler-5.17.0.dist-info → prowler-5.18.0.dist-info}/METADATA +11 -9
- {prowler-5.17.0.dist-info → prowler-5.18.0.dist-info}/RECORD +219 -155
- {prowler-5.17.0.dist-info → prowler-5.18.0.dist-info}/LICENSE +0 -0
- {prowler-5.17.0.dist-info → prowler-5.18.0.dist-info}/WHEEL +0 -0
- {prowler-5.17.0.dist-info → prowler-5.18.0.dist-info}/entry_points.txt +0 -0
|
@@ -4,7 +4,6 @@ from pydantic import BaseModel
|
|
|
4
4
|
|
|
5
5
|
from prowler.lib.logger import logger
|
|
6
6
|
from prowler.providers.cloudflare.lib.service.service import CloudflareService
|
|
7
|
-
from prowler.providers.cloudflare.services.zone.zone_client import zone_client
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
class DNS(CloudflareService):
|
|
@@ -19,10 +18,13 @@ class DNS(CloudflareService):
|
|
|
19
18
|
"""List DNS records for all zones."""
|
|
20
19
|
logger.info("DNS - Listing DNS records...")
|
|
21
20
|
try:
|
|
22
|
-
|
|
21
|
+
# Get zones directly from API to avoid circular dependency with zone_client
|
|
22
|
+
zones = self._get_zones()
|
|
23
|
+
|
|
24
|
+
for zone_id, zone_name in zones.items():
|
|
23
25
|
seen_record_ids: set[str] = set()
|
|
24
26
|
try:
|
|
25
|
-
for record in self.client.dns.records.list(zone_id=
|
|
27
|
+
for record in self.client.dns.records.list(zone_id=zone_id):
|
|
26
28
|
record_id = getattr(record, "id", None)
|
|
27
29
|
# Prevent infinite loop
|
|
28
30
|
if record_id in seen_record_ids:
|
|
@@ -32,8 +34,8 @@ class DNS(CloudflareService):
|
|
|
32
34
|
self.records.append(
|
|
33
35
|
CloudflareDNSRecord(
|
|
34
36
|
id=record_id,
|
|
35
|
-
zone_id=
|
|
36
|
-
zone_name=
|
|
37
|
+
zone_id=zone_id,
|
|
38
|
+
zone_name=zone_name,
|
|
37
39
|
name=getattr(record, "name", None),
|
|
38
40
|
type=getattr(record, "type", None),
|
|
39
41
|
content=getattr(record, "content", ""),
|
|
@@ -43,13 +45,57 @@ class DNS(CloudflareService):
|
|
|
43
45
|
)
|
|
44
46
|
except Exception as error:
|
|
45
47
|
logger.error(
|
|
46
|
-
f"{
|
|
48
|
+
f"{zone_id} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
47
49
|
)
|
|
48
50
|
except Exception as error:
|
|
49
51
|
logger.error(
|
|
50
52
|
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
51
53
|
)
|
|
52
54
|
|
|
55
|
+
def _get_zones(self) -> dict[str, str]:
|
|
56
|
+
"""Get zones directly from Cloudflare API.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Dictionary mapping zone_id to zone_name.
|
|
60
|
+
"""
|
|
61
|
+
zones = {}
|
|
62
|
+
audited_accounts = self.provider.identity.audited_accounts
|
|
63
|
+
filter_zones = self.provider.filter_zones
|
|
64
|
+
seen_zone_ids: set[str] = set()
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
for zone in self.client.zones.list():
|
|
68
|
+
zone_id = getattr(zone, "id", None)
|
|
69
|
+
# Prevent infinite loop - skip if we've seen this zone
|
|
70
|
+
if zone_id in seen_zone_ids:
|
|
71
|
+
break
|
|
72
|
+
seen_zone_ids.add(zone_id)
|
|
73
|
+
|
|
74
|
+
zone_account = getattr(zone, "account", None)
|
|
75
|
+
account_id = getattr(zone_account, "id", None) if zone_account else None
|
|
76
|
+
|
|
77
|
+
# Filter by audited accounts
|
|
78
|
+
if audited_accounts and account_id not in audited_accounts:
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
zone_name = getattr(zone, "name", None)
|
|
82
|
+
|
|
83
|
+
# Apply zone filter if specified via --region
|
|
84
|
+
if (
|
|
85
|
+
filter_zones
|
|
86
|
+
and zone_id not in filter_zones
|
|
87
|
+
and zone_name not in filter_zones
|
|
88
|
+
):
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
zones[zone_id] = zone_name
|
|
92
|
+
except Exception as error:
|
|
93
|
+
logger.error(
|
|
94
|
+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return zones
|
|
98
|
+
|
|
53
99
|
|
|
54
100
|
class CloudflareDNSRecord(BaseModel):
|
|
55
101
|
"""Cloudflare DNS record representation."""
|
|
File without changes
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from prowler.lib.logger import logger
|
|
6
|
+
from prowler.providers.cloudflare.lib.service.service import CloudflareService
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Firewall(CloudflareService):
|
|
10
|
+
"""Retrieve Cloudflare firewall rules for all zones."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, provider):
|
|
13
|
+
super().__init__(__class__.__name__, provider)
|
|
14
|
+
self.rules: list["CloudflareFirewallRule"] = []
|
|
15
|
+
self._list_rulesets()
|
|
16
|
+
|
|
17
|
+
def _list_rulesets(self) -> None:
|
|
18
|
+
"""List firewall rulesets for all zones."""
|
|
19
|
+
logger.info("Firewall - Listing firewall rulesets...")
|
|
20
|
+
try:
|
|
21
|
+
# Get zones directly from API to avoid circular dependency with zone_client
|
|
22
|
+
zones = self._get_zones()
|
|
23
|
+
|
|
24
|
+
for zone_id, zone_name in zones.items():
|
|
25
|
+
try:
|
|
26
|
+
# Get all rulesets for the zone
|
|
27
|
+
rulesets = self.client.rulesets.list(zone_id=zone_id)
|
|
28
|
+
for ruleset in rulesets:
|
|
29
|
+
ruleset_id = getattr(ruleset, "id", None)
|
|
30
|
+
phase = getattr(ruleset, "phase", None)
|
|
31
|
+
if not ruleset_id:
|
|
32
|
+
continue
|
|
33
|
+
|
|
34
|
+
# Get rules within each ruleset
|
|
35
|
+
try:
|
|
36
|
+
ruleset_detail = self.client.rulesets.get(
|
|
37
|
+
ruleset_id=ruleset_id, zone_id=zone_id
|
|
38
|
+
)
|
|
39
|
+
rules = getattr(ruleset_detail, "rules", []) or []
|
|
40
|
+
for rule in rules:
|
|
41
|
+
self.rules.append(
|
|
42
|
+
CloudflareFirewallRule(
|
|
43
|
+
id=getattr(rule, "id", None),
|
|
44
|
+
zone_id=zone_id,
|
|
45
|
+
zone_name=zone_name,
|
|
46
|
+
ruleset_id=ruleset_id,
|
|
47
|
+
phase=phase,
|
|
48
|
+
action=getattr(rule, "action", None),
|
|
49
|
+
expression=getattr(rule, "expression", None),
|
|
50
|
+
description=getattr(rule, "description", None),
|
|
51
|
+
enabled=getattr(rule, "enabled", True),
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
except Exception as error:
|
|
55
|
+
logger.debug(
|
|
56
|
+
f"{zone_id} ruleset {ruleset_id} -- {error.__class__.__name__}: {error}"
|
|
57
|
+
)
|
|
58
|
+
except Exception as error:
|
|
59
|
+
logger.error(
|
|
60
|
+
f"{zone_id} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
61
|
+
)
|
|
62
|
+
except Exception as error:
|
|
63
|
+
logger.error(
|
|
64
|
+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def _get_zones(self) -> dict[str, str]:
|
|
68
|
+
"""Get zones directly from Cloudflare API.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Dictionary mapping zone_id to zone_name.
|
|
72
|
+
"""
|
|
73
|
+
zones = {}
|
|
74
|
+
audited_accounts = self.provider.identity.audited_accounts
|
|
75
|
+
filter_zones = self.provider.filter_zones
|
|
76
|
+
seen_zone_ids: set[str] = set()
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
for zone in self.client.zones.list():
|
|
80
|
+
zone_id = getattr(zone, "id", None)
|
|
81
|
+
# Prevent infinite loop - skip if we've seen this zone
|
|
82
|
+
if zone_id in seen_zone_ids:
|
|
83
|
+
break
|
|
84
|
+
seen_zone_ids.add(zone_id)
|
|
85
|
+
|
|
86
|
+
zone_account = getattr(zone, "account", None)
|
|
87
|
+
account_id = getattr(zone_account, "id", None) if zone_account else None
|
|
88
|
+
|
|
89
|
+
# Filter by audited accounts
|
|
90
|
+
if audited_accounts and account_id not in audited_accounts:
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
zone_name = getattr(zone, "name", None)
|
|
94
|
+
|
|
95
|
+
# Apply zone filter if specified via --region
|
|
96
|
+
if (
|
|
97
|
+
filter_zones
|
|
98
|
+
and zone_id not in filter_zones
|
|
99
|
+
and zone_name not in filter_zones
|
|
100
|
+
):
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
zones[zone_id] = zone_name
|
|
104
|
+
except Exception as error:
|
|
105
|
+
logger.error(
|
|
106
|
+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return zones
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class CloudflareFirewallRule(BaseModel):
|
|
113
|
+
"""Cloudflare firewall rule representation."""
|
|
114
|
+
|
|
115
|
+
id: Optional[str] = None
|
|
116
|
+
zone_id: str
|
|
117
|
+
zone_name: str
|
|
118
|
+
ruleset_id: Optional[str] = None
|
|
119
|
+
phase: Optional[str] = None
|
|
120
|
+
action: Optional[str] = None
|
|
121
|
+
expression: Optional[str] = None
|
|
122
|
+
description: Optional[str] = None
|
|
123
|
+
enabled: bool = True
|
prowler/providers/cloudflare/services/zone/zone_firewall_blocking_rules_configured/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Provider": "cloudflare",
|
|
3
|
+
"CheckID": "zone_firewall_blocking_rules_configured",
|
|
4
|
+
"CheckTitle": "Cloudflare Zone Firewall Rules Use Blocking Actions to Protect Against Threats",
|
|
5
|
+
"CheckType": [],
|
|
6
|
+
"ServiceName": "zone",
|
|
7
|
+
"SubServiceName": "",
|
|
8
|
+
"ResourceIdTemplate": "",
|
|
9
|
+
"Severity": "medium",
|
|
10
|
+
"ResourceType": "Zone",
|
|
11
|
+
"ResourceGroup": "network",
|
|
12
|
+
"Description": "**Cloudflare zones** are assessed for **firewall blocking rules** by checking if custom rules use block, challenge, js_challenge, or managed_challenge actions to actively protect against threats rather than only logging.",
|
|
13
|
+
"Risk": "Firewall rules configured only for **logging** provide visibility but no protection.\n- **Confidentiality**: malicious traffic can access and exfiltrate sensitive data\n- **Integrity**: application exploits can modify data without being blocked\n- **Availability**: credential stuffing and abuse attacks reach the origin unimpeded",
|
|
14
|
+
"RelatedUrl": "",
|
|
15
|
+
"AdditionalURLs": [
|
|
16
|
+
"https://developers.cloudflare.com/waf/custom-rules/"
|
|
17
|
+
],
|
|
18
|
+
"Remediation": {
|
|
19
|
+
"Code": {
|
|
20
|
+
"CLI": "",
|
|
21
|
+
"NativeIaC": "",
|
|
22
|
+
"Other": "1. Log in to the Cloudflare dashboard and select your account and domain\n2. Go to Security > WAF > Custom rules\n3. Review existing rules and their actions\n4. Update rules to use blocking actions (Block, Challenge, JS Challenge, Managed Challenge)\n5. Test rules in log mode first, then enable blocking actions",
|
|
23
|
+
"Terraform": "```hcl\n# Configure firewall rule with blocking action\nresource \"cloudflare_ruleset\" \"blocking_rule\" {\n zone_id = \"<ZONE_ID>\"\n name = \"Block malicious requests\"\n kind = \"zone\"\n phase = \"http_request_firewall_custom\"\n rules {\n action = \"block\" # Actively blocks matching traffic\n expression = \"(ip.geoip.country eq \\\"XX\\\")\"\n description = \"Block traffic from high-risk country\"\n }\n}\n```"
|
|
24
|
+
},
|
|
25
|
+
"Recommendation": {
|
|
26
|
+
"Text": "Configure **firewall rules** with blocking actions to enforce security policies.\n- Use challenge actions for suspicious traffic to verify human visitors\n- Use block actions for known malicious patterns and high-risk sources\n- Test rules in log mode before enabling blocking to avoid false positives\n- Follow the principle of least privilege in rule configuration",
|
|
27
|
+
"Url": "https://hub.prowler.com/checks/cloudflare/zone_firewall_blocking_rules_configured"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"Categories": [
|
|
31
|
+
"internet-exposed"
|
|
32
|
+
],
|
|
33
|
+
"DependsOn": [],
|
|
34
|
+
"RelatedTo": [],
|
|
35
|
+
"Notes": "Blocking actions include: block, challenge, js_challenge, managed_challenge. Log-only rules provide visibility but do not prevent attacks."
|
|
36
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from prowler.lib.check.models import Check, CheckReportCloudflare
|
|
2
|
+
from prowler.providers.cloudflare.services.zone.zone_client import zone_client
|
|
3
|
+
|
|
4
|
+
BLOCKING_ACTIONS = {"block", "challenge", "js_challenge", "managed_challenge"}
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class zone_firewall_blocking_rules_configured(Check):
|
|
8
|
+
"""Ensure that firewall rules with blocking actions are configured for Cloudflare zones.
|
|
9
|
+
|
|
10
|
+
Firewall rules should use blocking actions (block, challenge, js_challenge,
|
|
11
|
+
managed_challenge) to actively protect against threats rather than only logging
|
|
12
|
+
traffic. Without blocking actions, malicious requests can reach the origin server
|
|
13
|
+
and potentially compromise the application's security.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def execute(self) -> list[CheckReportCloudflare]:
|
|
17
|
+
"""Execute the firewall blocking rules configured check.
|
|
18
|
+
|
|
19
|
+
Iterates through all Cloudflare zones and verifies that at least one
|
|
20
|
+
firewall rule exists with a blocking action. Blocking actions include
|
|
21
|
+
block, challenge, js_challenge, and managed_challenge.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
A list of CheckReportCloudflare objects with PASS status if blocking
|
|
25
|
+
rules are configured, or FAIL status if no blocking rules exist.
|
|
26
|
+
"""
|
|
27
|
+
findings = []
|
|
28
|
+
|
|
29
|
+
for zone in zone_client.zones.values():
|
|
30
|
+
report = CheckReportCloudflare(
|
|
31
|
+
metadata=self.metadata(),
|
|
32
|
+
resource=zone,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Find blocking rules for this zone
|
|
36
|
+
blocking_rules = [
|
|
37
|
+
rule for rule in zone.firewall_rules if rule.action in BLOCKING_ACTIONS
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
if blocking_rules:
|
|
41
|
+
report.status = "PASS"
|
|
42
|
+
report.status_extended = (
|
|
43
|
+
f"Zone {zone.name} has firewall rules with blocking actions "
|
|
44
|
+
f"({len(blocking_rules)} rule(s))."
|
|
45
|
+
)
|
|
46
|
+
else:
|
|
47
|
+
report.status = "FAIL"
|
|
48
|
+
report.status_extended = (
|
|
49
|
+
f"Zone {zone.name} has no firewall rules with blocking actions."
|
|
50
|
+
)
|
|
51
|
+
findings.append(report)
|
|
52
|
+
|
|
53
|
+
return findings
|
|
@@ -17,6 +17,31 @@ class CloudflareRateLimitRule(BaseModel):
|
|
|
17
17
|
expression: Optional[str] = None
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
class CloudflareFirewallRule(BaseModel):
|
|
21
|
+
"""Represents a firewall rule from custom rulesets."""
|
|
22
|
+
|
|
23
|
+
id: str
|
|
24
|
+
name: str = ""
|
|
25
|
+
description: Optional[str] = None
|
|
26
|
+
action: Optional[str] = None
|
|
27
|
+
enabled: bool = True
|
|
28
|
+
expression: Optional[str] = None
|
|
29
|
+
phase: Optional[str] = None
|
|
30
|
+
|
|
31
|
+
class Config:
|
|
32
|
+
arbitrary_types_allowed = True
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class CloudflareWAFRuleset(BaseModel):
|
|
36
|
+
"""Represents a WAF ruleset (managed rules) for a zone."""
|
|
37
|
+
|
|
38
|
+
id: str
|
|
39
|
+
name: str
|
|
40
|
+
kind: Optional[str] = None
|
|
41
|
+
phase: Optional[str] = None
|
|
42
|
+
enabled: bool = True
|
|
43
|
+
|
|
44
|
+
|
|
20
45
|
class Zone(CloudflareService):
|
|
21
46
|
"""Retrieve Cloudflare zones with security-relevant settings."""
|
|
22
47
|
|
|
@@ -29,6 +54,8 @@ class Zone(CloudflareService):
|
|
|
29
54
|
self._get_zones_universal_ssl()
|
|
30
55
|
self._get_zones_rate_limit_rules()
|
|
31
56
|
self._get_zones_bot_management()
|
|
57
|
+
self._get_zones_firewall_rules()
|
|
58
|
+
self._get_zones_waf_rulesets()
|
|
32
59
|
|
|
33
60
|
def _list_zones(self) -> None:
|
|
34
61
|
"""List all Cloudflare zones with their basic information."""
|
|
@@ -122,7 +149,7 @@ class Zone(CloudflareService):
|
|
|
122
149
|
|
|
123
150
|
def _get_zones_universal_ssl(self) -> None:
|
|
124
151
|
"""Get Universal SSL settings for all zones."""
|
|
125
|
-
logger.info("
|
|
152
|
+
logger.info("Zone - Getting Universal SSL settings...")
|
|
126
153
|
for zone in self.zones.values():
|
|
127
154
|
try:
|
|
128
155
|
universal_ssl = self.client.ssl.universal.settings.get(zone_id=zone.id)
|
|
@@ -191,6 +218,109 @@ class Zone(CloudflareService):
|
|
|
191
218
|
f"{zone.id} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
192
219
|
)
|
|
193
220
|
|
|
221
|
+
def _get_zones_firewall_rules(self) -> None:
|
|
222
|
+
"""Get firewall rules for all zones."""
|
|
223
|
+
logger.info("Zone - Getting firewall rules...")
|
|
224
|
+
for zone in self.zones.values():
|
|
225
|
+
try:
|
|
226
|
+
self._get_zone_firewall_rules(zone)
|
|
227
|
+
except Exception as error:
|
|
228
|
+
logger.error(
|
|
229
|
+
f"{zone.id} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
def _get_zone_firewall_rules(self, zone: "CloudflareZone") -> None:
|
|
233
|
+
"""List firewall rules from custom rulesets for a zone."""
|
|
234
|
+
seen_ruleset_ids: set[str] = set()
|
|
235
|
+
try:
|
|
236
|
+
for ruleset in self.client.rulesets.list(zone_id=zone.id):
|
|
237
|
+
ruleset_id = getattr(ruleset, "id", "")
|
|
238
|
+
if ruleset_id in seen_ruleset_ids:
|
|
239
|
+
break
|
|
240
|
+
seen_ruleset_ids.add(ruleset_id)
|
|
241
|
+
|
|
242
|
+
ruleset_phase = getattr(ruleset, "phase", "")
|
|
243
|
+
if ruleset_phase in [
|
|
244
|
+
"http_request_firewall_custom",
|
|
245
|
+
"http_ratelimit",
|
|
246
|
+
"http_request_firewall_managed",
|
|
247
|
+
]:
|
|
248
|
+
try:
|
|
249
|
+
ruleset_detail = self.client.rulesets.get(
|
|
250
|
+
ruleset_id=ruleset_id, zone_id=zone.id
|
|
251
|
+
)
|
|
252
|
+
rules = getattr(ruleset_detail, "rules", []) or []
|
|
253
|
+
seen_rule_ids: set[str] = set()
|
|
254
|
+
for rule in rules:
|
|
255
|
+
rule_id = getattr(rule, "id", "")
|
|
256
|
+
if rule_id in seen_rule_ids:
|
|
257
|
+
break
|
|
258
|
+
seen_rule_ids.add(rule_id)
|
|
259
|
+
try:
|
|
260
|
+
zone.firewall_rules.append(
|
|
261
|
+
CloudflareFirewallRule(
|
|
262
|
+
id=rule_id,
|
|
263
|
+
name=getattr(rule, "description", "")
|
|
264
|
+
or rule_id,
|
|
265
|
+
description=getattr(rule, "description", None),
|
|
266
|
+
action=getattr(rule, "action", None),
|
|
267
|
+
enabled=getattr(rule, "enabled", True),
|
|
268
|
+
expression=getattr(rule, "expression", None),
|
|
269
|
+
phase=ruleset_phase,
|
|
270
|
+
)
|
|
271
|
+
)
|
|
272
|
+
except Exception as error:
|
|
273
|
+
logger.error(
|
|
274
|
+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
275
|
+
)
|
|
276
|
+
except Exception as error:
|
|
277
|
+
logger.error(
|
|
278
|
+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
279
|
+
)
|
|
280
|
+
except Exception as error:
|
|
281
|
+
logger.error(
|
|
282
|
+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
def _get_zones_waf_rulesets(self) -> None:
|
|
286
|
+
"""Get WAF rulesets for all zones."""
|
|
287
|
+
logger.info("Zone - Getting WAF rulesets...")
|
|
288
|
+
for zone in self.zones.values():
|
|
289
|
+
try:
|
|
290
|
+
self._get_zone_waf_rulesets(zone)
|
|
291
|
+
except Exception as error:
|
|
292
|
+
logger.error(
|
|
293
|
+
f"{zone.id} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
def _get_zone_waf_rulesets(self, zone: "CloudflareZone") -> None:
|
|
297
|
+
"""List WAF rulesets for a zone using the rulesets API."""
|
|
298
|
+
seen_ids: set[str] = set()
|
|
299
|
+
try:
|
|
300
|
+
for ruleset in self.client.rulesets.list(zone_id=zone.id):
|
|
301
|
+
ruleset_id = getattr(ruleset, "id", "")
|
|
302
|
+
if ruleset_id in seen_ids:
|
|
303
|
+
break
|
|
304
|
+
seen_ids.add(ruleset_id)
|
|
305
|
+
try:
|
|
306
|
+
zone.waf_rulesets.append(
|
|
307
|
+
CloudflareWAFRuleset(
|
|
308
|
+
id=ruleset_id,
|
|
309
|
+
name=getattr(ruleset, "name", ""),
|
|
310
|
+
kind=getattr(ruleset, "kind", None),
|
|
311
|
+
phase=getattr(ruleset, "phase", None),
|
|
312
|
+
enabled=True,
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
except Exception as error:
|
|
316
|
+
logger.error(
|
|
317
|
+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
318
|
+
)
|
|
319
|
+
except Exception as error:
|
|
320
|
+
logger.error(
|
|
321
|
+
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
322
|
+
)
|
|
323
|
+
|
|
194
324
|
def _get_zone_setting(self, zone_id: str, setting_id: str):
|
|
195
325
|
"""Get a single zone setting by ID."""
|
|
196
326
|
try:
|
|
@@ -326,3 +456,5 @@ class CloudflareZone(BaseModel):
|
|
|
326
456
|
settings: CloudflareZoneSettings = Field(default_factory=CloudflareZoneSettings)
|
|
327
457
|
dnssec_status: Optional[str] = None
|
|
328
458
|
rate_limit_rules: list[CloudflareRateLimitRule] = Field(default_factory=list)
|
|
459
|
+
firewall_rules: list[CloudflareFirewallRule] = Field(default_factory=list)
|
|
460
|
+
waf_rulesets: list[CloudflareWAFRuleset] = Field(default_factory=list)
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Provider": "cloudflare",
|
|
3
|
+
"CheckID": "zone_waf_owasp_ruleset_enabled",
|
|
4
|
+
"CheckTitle": "Cloudflare Zone OWASP Managed WAF Rulesets Are Enabled",
|
|
5
|
+
"CheckType": [],
|
|
6
|
+
"ServiceName": "zone",
|
|
7
|
+
"SubServiceName": "",
|
|
8
|
+
"ResourceIdTemplate": "",
|
|
9
|
+
"Severity": "high",
|
|
10
|
+
"ResourceType": "Zone",
|
|
11
|
+
"ResourceGroup": "network",
|
|
12
|
+
"Description": "**Cloudflare zones** are assessed for **OWASP managed rulesets** by checking if they are enabled to protect against common web application vulnerabilities including **SQL injection**, **XSS**, and other **OWASP Top 10** threats.",
|
|
13
|
+
"Risk": "Without **OWASP managed rulesets**, web applications are exposed to well-known attack vectors.\n- **Confidentiality**: SQL injection attacks can exfiltrate sensitive database contents\n- **Integrity**: XSS attacks can modify page content and steal session tokens\n- **Availability**: remote code execution can compromise server availability",
|
|
14
|
+
"RelatedUrl": "",
|
|
15
|
+
"AdditionalURLs": [
|
|
16
|
+
"https://developers.cloudflare.com/waf/managed-rules/"
|
|
17
|
+
],
|
|
18
|
+
"Remediation": {
|
|
19
|
+
"Code": {
|
|
20
|
+
"CLI": "",
|
|
21
|
+
"NativeIaC": "",
|
|
22
|
+
"Other": "1. Log in to the Cloudflare dashboard and select your account and domain\n2. Go to Security > WAF > Managed rules\n3. Enable the Cloudflare OWASP Core Ruleset\n4. Review and configure rule sensitivity based on your application\n5. Monitor WAF analytics to tune rules and reduce false positives",
|
|
23
|
+
"Terraform": "```hcl\n# Enable OWASP managed WAF rulesets\nresource \"cloudflare_ruleset\" \"waf_owasp\" {\n zone_id = \"<ZONE_ID>\"\n name = \"OWASP Managed Rules\"\n kind = \"zone\"\n phase = \"http_request_firewall_managed\"\n rules {\n action = \"execute\"\n action_parameters {\n id = \"4814384a9e5d4991b9815dcfc25d2f1f\" # Cloudflare OWASP Core Ruleset\n }\n expression = \"true\"\n description = \"Execute Cloudflare OWASP Core Ruleset\"\n }\n}\n```"
|
|
24
|
+
},
|
|
25
|
+
"Recommendation": {
|
|
26
|
+
"Text": "Enable **OWASP Core Ruleset** managed rules as part of a defense in depth strategy.\n- Protects against OWASP Top 10 vulnerabilities including SQLi and XSS\n- Regularly review and tune rule sensitivity based on application requirements\n- Monitor WAF analytics to identify and address false positives\n- Combine with custom rules for application-specific protection",
|
|
27
|
+
"Url": "https://hub.prowler.com/checks/cloudflare/zone_waf_owasp_ruleset_enabled"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"Categories": [
|
|
31
|
+
"vulnerabilities"
|
|
32
|
+
],
|
|
33
|
+
"DependsOn": [],
|
|
34
|
+
"RelatedTo": [],
|
|
35
|
+
"Notes": "OWASP managed rulesets are available on Pro, Business, and Enterprise plans. The Cloudflare OWASP Core Ruleset provides protection against common web application vulnerabilities."
|
|
36
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from prowler.lib.check.models import Check, CheckReportCloudflare
|
|
2
|
+
from prowler.providers.cloudflare.services.zone.zone_client import zone_client
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class zone_waf_owasp_ruleset_enabled(Check):
|
|
6
|
+
"""Ensure that OWASP managed WAF rulesets are enabled for Cloudflare zones.
|
|
7
|
+
|
|
8
|
+
The OWASP Core Ruleset provides protection against common web application
|
|
9
|
+
vulnerabilities including SQL injection, cross-site scripting (XSS), and other
|
|
10
|
+
OWASP Top 10 threats. These managed rulesets are essential for defense in depth
|
|
11
|
+
and protecting applications from well-known attack vectors.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def execute(self) -> list[CheckReportCloudflare]:
|
|
15
|
+
"""Execute the OWASP WAF ruleset enabled check.
|
|
16
|
+
|
|
17
|
+
Iterates through all Cloudflare zones and verifies that OWASP managed
|
|
18
|
+
WAF rulesets are enabled. The check identifies OWASP rulesets by name
|
|
19
|
+
containing "owasp" or by the http_request_firewall_managed phase.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
A list of CheckReportCloudflare objects with PASS status if OWASP
|
|
23
|
+
rulesets are enabled, or FAIL status if no OWASP protection exists.
|
|
24
|
+
"""
|
|
25
|
+
findings = []
|
|
26
|
+
|
|
27
|
+
for zone in zone_client.zones.values():
|
|
28
|
+
report = CheckReportCloudflare(
|
|
29
|
+
metadata=self.metadata(),
|
|
30
|
+
resource=zone,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Find OWASP managed rulesets for this zone
|
|
34
|
+
# Only match rulesets that explicitly contain "owasp" in the name
|
|
35
|
+
# The phase check was too broad as it matched any managed ruleset
|
|
36
|
+
owasp_rulesets = [
|
|
37
|
+
ruleset
|
|
38
|
+
for ruleset in zone.waf_rulesets
|
|
39
|
+
if "owasp" in (ruleset.name or "").lower()
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
if owasp_rulesets:
|
|
43
|
+
report.status = "PASS"
|
|
44
|
+
ruleset_descriptions = ", ".join(
|
|
45
|
+
ruleset.name for ruleset in owasp_rulesets
|
|
46
|
+
)
|
|
47
|
+
report.status_extended = (
|
|
48
|
+
f"Zone {zone.name} has OWASP managed WAF ruleset enabled: "
|
|
49
|
+
f"{ruleset_descriptions}."
|
|
50
|
+
)
|
|
51
|
+
else:
|
|
52
|
+
report.status = "FAIL"
|
|
53
|
+
report.status_extended = (
|
|
54
|
+
f"Zone {zone.name} does not have OWASP managed WAF ruleset enabled."
|
|
55
|
+
)
|
|
56
|
+
findings.append(report)
|
|
57
|
+
|
|
58
|
+
return findings
|
|
@@ -251,6 +251,7 @@ class Provider(ABC):
|
|
|
251
251
|
elif "cloudflare" in provider_class_name.lower():
|
|
252
252
|
provider_class(
|
|
253
253
|
filter_zones=arguments.region,
|
|
254
|
+
filter_accounts=arguments.account_id,
|
|
254
255
|
config_path=arguments.config_file,
|
|
255
256
|
mutelist_path=arguments.mutelist_file,
|
|
256
257
|
fixer_config=fixer_config,
|
|
@@ -293,6 +294,28 @@ class Provider(ABC):
|
|
|
293
294
|
fixer_config=fixer_config,
|
|
294
295
|
use_instance_principal=arguments.use_instance_principal,
|
|
295
296
|
)
|
|
297
|
+
elif "openstack" in provider_class_name.lower():
|
|
298
|
+
provider_class(
|
|
299
|
+
clouds_yaml_file=getattr(arguments, "clouds_yaml_file", None),
|
|
300
|
+
clouds_yaml_cloud=getattr(arguments, "clouds_yaml_cloud", None),
|
|
301
|
+
auth_url=getattr(arguments, "os_auth_url", None),
|
|
302
|
+
identity_api_version=getattr(
|
|
303
|
+
arguments, "os_identity_api_version", None
|
|
304
|
+
),
|
|
305
|
+
username=getattr(arguments, "os_username", None),
|
|
306
|
+
password=getattr(arguments, "os_password", None),
|
|
307
|
+
project_id=getattr(arguments, "os_project_id", None),
|
|
308
|
+
region_name=getattr(arguments, "os_region_name", None),
|
|
309
|
+
user_domain_name=getattr(
|
|
310
|
+
arguments, "os_user_domain_name", None
|
|
311
|
+
),
|
|
312
|
+
project_domain_name=getattr(
|
|
313
|
+
arguments, "os_project_domain_name", None
|
|
314
|
+
),
|
|
315
|
+
config_path=arguments.config_file,
|
|
316
|
+
mutelist_path=arguments.mutelist_file,
|
|
317
|
+
fixer_config=fixer_config,
|
|
318
|
+
)
|
|
296
319
|
elif "alibabacloud" in provider_class_name.lower():
|
|
297
320
|
provider_class(
|
|
298
321
|
role_arn=arguments.role_arn,
|
|
File without changes
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Provider": "gcp",
|
|
3
|
+
"CheckID": "compute_instance_suspended_without_persistent_disks",
|
|
4
|
+
"CheckTitle": "Suspended VM instance does not have persistent disks attached",
|
|
5
|
+
"CheckType": [],
|
|
6
|
+
"ServiceName": "compute",
|
|
7
|
+
"SubServiceName": "",
|
|
8
|
+
"ResourceIdTemplate": "",
|
|
9
|
+
"Severity": "medium",
|
|
10
|
+
"ResourceType": "compute.googleapis.com/Instance",
|
|
11
|
+
"ResourceGroup": "compute",
|
|
12
|
+
"Description": "This check identifies VM instances in a **SUSPENDED** or **SUSPENDING** state with persistent disks still attached.\n\nPersistent disks on suspended VMs remain accessible through the GCP API and could contain **sensitive data** while the instance is inactive, potentially creating security blind spots in long-forgotten infrastructure.",
|
|
13
|
+
"Risk": "Persistent disks on suspended VM instances remain accessible through the GCP API and may contain **sensitive data**, creating potential security risks:\n\n- **Unauthorized data access** if credentials are compromised or permissions are misconfigured\n- **Data exposure** from forgotten infrastructure that is no longer actively monitored\n- **Security blind spots** where suspended resources are overlooked during security reviews and audits",
|
|
14
|
+
"RelatedUrl": "",
|
|
15
|
+
"AdditionalURLs": [
|
|
16
|
+
"https://cloud.google.com/icompute/docs/instances/suspend-resume-instance",
|
|
17
|
+
"https://www.trendmicro.com/cloudoneconformity/knowledge-base/gcp/ComputeEngine/persistent-disks-attached-to-suspended-vms.html"
|
|
18
|
+
],
|
|
19
|
+
"Remediation": {
|
|
20
|
+
"Code": {
|
|
21
|
+
"CLI": "gcloud compute instances delete INSTANCE_NAME --zone=ZONE",
|
|
22
|
+
"NativeIaC": "",
|
|
23
|
+
"Other": "1. Open the Google Cloud Console\n2. Navigate to Compute Engine > VM instances\n3. Identify suspended instances with attached disks\n4. If the instance is no longer needed, select it and click DELETE\n5. If the instance will be resumed, take no action or resume it with: gcloud compute instances resume INSTANCE_NAME --zone=ZONE",
|
|
24
|
+
"Terraform": "```hcl\n# To remediate, either delete the suspended instance or resume it\n# Delete by removing the resource from your Terraform configuration\n# Or resume by changing the desired_status\nresource \"google_compute_instance\" \"example_resource\" {\n name = \"example-instance\"\n machine_type = \"e2-medium\"\n zone = \"us-central1-a\"\n\n # Set desired_status to RUNNING to resume the instance\n desired_status = \"RUNNING\"\n\n boot_disk {\n initialize_params {\n image = \"debian-cloud/debian-11\"\n }\n }\n\n network_interface {\n network = \"default\"\n }\n}\n```"
|
|
25
|
+
},
|
|
26
|
+
"Recommendation": {
|
|
27
|
+
"Text": "Regularly review suspended VM instances to reduce your attack surface. Either **resume** instances if still needed, or **delete** them along with their attached disks to eliminate potential data exposure. Implement automated policies to detect and alert on long-suspended instances as part of your security monitoring.",
|
|
28
|
+
"Url": "https://hub.prowler.com/check/compute_instance_suspended_without_persistent_disks"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"Categories": [],
|
|
32
|
+
"DependsOn": [],
|
|
33
|
+
"RelatedTo": [
|
|
34
|
+
"compute_instance_disk_auto_delete_disabled"
|
|
35
|
+
],
|
|
36
|
+
"Notes": "This check is focused on security risks rather than cost optimization. Persistent disks on suspended VMs remain accessible and may contain sensitive data, creating potential unauthorized access risks."
|
|
37
|
+
}
|