prowler 5.17.1__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.1.dist-info → prowler-5.18.0.dist-info}/METADATA +11 -9
- {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/RECORD +219 -155
- {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/LICENSE +0 -0
- {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/WHEEL +0 -0
- {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/entry_points.txt +0 -0
|
@@ -14,6 +14,7 @@ from prowler.lib.utils.utils import print_boxes
|
|
|
14
14
|
from prowler.providers.cloudflare.exceptions.exceptions import (
|
|
15
15
|
CloudflareCredentialsError,
|
|
16
16
|
CloudflareIdentityError,
|
|
17
|
+
CloudflareInvalidAccountError,
|
|
17
18
|
CloudflareSessionError,
|
|
18
19
|
)
|
|
19
20
|
from prowler.providers.cloudflare.lib.mutelist.mutelist import CloudflareMutelist
|
|
@@ -36,16 +37,21 @@ class CloudflareProvider(Provider):
|
|
|
36
37
|
_fixer_config: dict
|
|
37
38
|
_mutelist: CloudflareMutelist
|
|
38
39
|
_filter_zones: set[str] | None
|
|
40
|
+
_filter_accounts: set[str] | None
|
|
39
41
|
audit_metadata: Audit_Metadata
|
|
40
42
|
|
|
41
43
|
def __init__(
|
|
42
44
|
self,
|
|
43
45
|
filter_zones: Iterable[str] | None = None,
|
|
46
|
+
filter_accounts: Iterable[str] | None = None,
|
|
44
47
|
config_path: str = None,
|
|
45
48
|
config_content: dict | None = None,
|
|
46
49
|
fixer_config: dict = {},
|
|
47
50
|
mutelist_path: str = None,
|
|
48
51
|
mutelist_content: dict = None,
|
|
52
|
+
api_token: str = None,
|
|
53
|
+
api_key: str = None,
|
|
54
|
+
api_email: str = None,
|
|
49
55
|
):
|
|
50
56
|
logger.info("Instantiating Cloudflare provider...")
|
|
51
57
|
|
|
@@ -58,7 +64,12 @@ class CloudflareProvider(Provider):
|
|
|
58
64
|
|
|
59
65
|
max_retries = self._audit_config.get("max_retries", 2)
|
|
60
66
|
|
|
61
|
-
self._session = CloudflareProvider.setup_session(
|
|
67
|
+
self._session = CloudflareProvider.setup_session(
|
|
68
|
+
max_retries=max_retries,
|
|
69
|
+
api_token=api_token,
|
|
70
|
+
api_key=api_key,
|
|
71
|
+
api_email=api_email,
|
|
72
|
+
)
|
|
62
73
|
|
|
63
74
|
self._identity = CloudflareProvider.setup_identity(self._session)
|
|
64
75
|
|
|
@@ -74,6 +85,23 @@ class CloudflareProvider(Provider):
|
|
|
74
85
|
# Store zone filter for filtering resources across services
|
|
75
86
|
self._filter_zones = set(filter_zones) if filter_zones else None
|
|
76
87
|
|
|
88
|
+
# Store account filter and restrict audited_accounts accordingly
|
|
89
|
+
self._filter_accounts = set(filter_accounts) if filter_accounts else None
|
|
90
|
+
if self._filter_accounts:
|
|
91
|
+
discovered_account_ids = {account.id for account in self._identity.accounts}
|
|
92
|
+
invalid_accounts = self._filter_accounts - discovered_account_ids
|
|
93
|
+
if invalid_accounts:
|
|
94
|
+
invalid_str = ", ".join(sorted(invalid_accounts))
|
|
95
|
+
raise CloudflareInvalidAccountError(
|
|
96
|
+
file=os.path.basename(__file__),
|
|
97
|
+
message=f"Account IDs not found: {invalid_str}.",
|
|
98
|
+
)
|
|
99
|
+
self._identity.audited_accounts = [
|
|
100
|
+
account_id
|
|
101
|
+
for account_id in self._identity.audited_accounts
|
|
102
|
+
if account_id in self._filter_accounts
|
|
103
|
+
]
|
|
104
|
+
|
|
77
105
|
Provider.set_global_provider(self)
|
|
78
106
|
|
|
79
107
|
@property
|
|
@@ -105,24 +133,38 @@ class CloudflareProvider(Provider):
|
|
|
105
133
|
"""Zone filter from --region argument to filter resources."""
|
|
106
134
|
return self._filter_zones
|
|
107
135
|
|
|
136
|
+
@property
|
|
137
|
+
def filter_accounts(self) -> set[str] | None:
|
|
138
|
+
"""Account filter from --account-id argument to restrict scanned accounts."""
|
|
139
|
+
return self._filter_accounts
|
|
140
|
+
|
|
108
141
|
@property
|
|
109
142
|
def accounts(self) -> list[CloudflareAccount]:
|
|
110
143
|
return self._identity.accounts
|
|
111
144
|
|
|
112
145
|
@staticmethod
|
|
113
|
-
def setup_session(
|
|
146
|
+
def setup_session(
|
|
147
|
+
max_retries: int = 2,
|
|
148
|
+
api_token: str = None,
|
|
149
|
+
api_key: str = None,
|
|
150
|
+
api_email: str = None,
|
|
151
|
+
) -> CloudflareSession:
|
|
114
152
|
"""Initialize Cloudflare SDK client.
|
|
115
153
|
|
|
116
|
-
Credentials
|
|
154
|
+
Credentials can be provided as arguments or read from environment variables:
|
|
117
155
|
- CLOUDFLARE_API_TOKEN (recommended)
|
|
118
156
|
- CLOUDFLARE_API_KEY and CLOUDFLARE_API_EMAIL (legacy)
|
|
119
157
|
|
|
120
158
|
Args:
|
|
121
159
|
max_retries: Maximum number of retries for API requests (default is 2).
|
|
160
|
+
api_token: Cloudflare API token (optional, falls back to env var).
|
|
161
|
+
api_key: Cloudflare API key (optional, falls back to env var).
|
|
162
|
+
api_email: Cloudflare API email (optional, falls back to env var).
|
|
122
163
|
"""
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
164
|
+
# Use provided credentials or fall back to environment variables
|
|
165
|
+
token = api_token or os.environ.get("CLOUDFLARE_API_TOKEN", "")
|
|
166
|
+
key = api_key or os.environ.get("CLOUDFLARE_API_KEY", "")
|
|
167
|
+
email = api_email or os.environ.get("CLOUDFLARE_API_EMAIL", "")
|
|
126
168
|
|
|
127
169
|
# Warn if both auth methods are set, use API Token (recommended)
|
|
128
170
|
if token and key and email:
|
|
@@ -248,21 +290,62 @@ class CloudflareProvider(Provider):
|
|
|
248
290
|
if email:
|
|
249
291
|
report_lines.append(f"Email: {Fore.YELLOW}{email}{Style.RESET_ALL}")
|
|
250
292
|
|
|
251
|
-
#
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
293
|
+
# Audited accounts (only the ones that will actually be scanned)
|
|
294
|
+
audited_accounts = self.identity.audited_accounts
|
|
295
|
+
if audited_accounts:
|
|
296
|
+
account_names = {
|
|
297
|
+
account.id: account.name for account in self.identity.accounts
|
|
298
|
+
}
|
|
299
|
+
accounts_str = ", ".join(
|
|
300
|
+
(
|
|
301
|
+
f"{account_id} ({account_names[account_id]})"
|
|
302
|
+
if account_id in account_names and account_names[account_id]
|
|
303
|
+
else account_id
|
|
304
|
+
)
|
|
305
|
+
for account_id in audited_accounts
|
|
306
|
+
)
|
|
307
|
+
report_lines.append(
|
|
308
|
+
f"Audited Accounts: {Fore.YELLOW}{accounts_str}{Style.RESET_ALL}"
|
|
309
|
+
)
|
|
255
310
|
|
|
256
311
|
print_boxes(report_lines, report_title)
|
|
257
312
|
|
|
258
|
-
|
|
313
|
+
@staticmethod
|
|
314
|
+
def test_connection(
|
|
315
|
+
api_token: str = None,
|
|
316
|
+
api_key: str = None,
|
|
317
|
+
api_email: str = None,
|
|
318
|
+
raise_on_exception: bool = True,
|
|
319
|
+
provider_id: str = None,
|
|
320
|
+
) -> Connection:
|
|
321
|
+
"""Test connection to Cloudflare.
|
|
322
|
+
|
|
323
|
+
Test the connection to Cloudflare using the provided credentials.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
api_token: Cloudflare API token (optional, falls back to env var).
|
|
327
|
+
api_key: Cloudflare API key (optional, falls back to env var).
|
|
328
|
+
api_email: Cloudflare API email (optional, falls back to env var).
|
|
329
|
+
raise_on_exception: Flag indicating whether to raise an exception if the connection fails.
|
|
330
|
+
provider_id: The provider ID (Cloudflare account ID).
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
Connection: Connection object with is_connected status.
|
|
334
|
+
"""
|
|
259
335
|
try:
|
|
260
|
-
|
|
336
|
+
session = CloudflareProvider.setup_session(
|
|
337
|
+
api_token=api_token,
|
|
338
|
+
api_key=api_key,
|
|
339
|
+
api_email=api_email,
|
|
340
|
+
)
|
|
341
|
+
_ = session.client.user.get()
|
|
261
342
|
return Connection(is_connected=True)
|
|
262
343
|
except Exception as error:
|
|
263
344
|
logger.error(
|
|
264
345
|
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
265
346
|
)
|
|
347
|
+
if raise_on_exception:
|
|
348
|
+
raise error
|
|
266
349
|
return Connection(is_connected=False, error=error)
|
|
267
350
|
|
|
268
351
|
def validate_arguments(self) -> None:
|
|
@@ -5,6 +5,13 @@ def init_parser(self):
|
|
|
5
5
|
)
|
|
6
6
|
|
|
7
7
|
scope_group = cloudflare_parser.add_argument_group("Scope")
|
|
8
|
+
scope_group.add_argument(
|
|
9
|
+
"--account-id",
|
|
10
|
+
nargs="+",
|
|
11
|
+
default=None,
|
|
12
|
+
metavar="ACCOUNT_ID",
|
|
13
|
+
help="Filter scan to specific Cloudflare account IDs. Only zones belonging to these accounts will be scanned.",
|
|
14
|
+
)
|
|
8
15
|
scope_group.add_argument(
|
|
9
16
|
"--region",
|
|
10
17
|
"--filter-region",
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Provider": "cloudflare",
|
|
3
|
+
"CheckID": "dns_record_cname_target_valid",
|
|
4
|
+
"CheckTitle": "DNS records pointing to hostnames have valid targets without takeover risk",
|
|
5
|
+
"CheckType": [],
|
|
6
|
+
"ServiceName": "dns",
|
|
7
|
+
"SubServiceName": "",
|
|
8
|
+
"ResourceIdTemplate": "",
|
|
9
|
+
"Severity": "high",
|
|
10
|
+
"ResourceType": "DNSRecord",
|
|
11
|
+
"ResourceGroup": "network",
|
|
12
|
+
"Description": "**Cloudflare DNS records** (CNAME, MX, NS, SRV) that point to hostnames are assessed for **dangling record** vulnerabilities by checking if the target domain resolves to a valid address, preventing **subdomain takeover**, **mail interception**, and **service hijacking** attacks.",
|
|
13
|
+
"Risk": "Dangling **DNS records** pointing to non-existent targets create multiple vulnerabilities.\n- **Confidentiality**: dangling CNAME/NS allows subdomain takeover; dangling MX allows mail interception\n- **Integrity**: attackers can impersonate your organization, intercept emails, or hijack services\n- **Availability**: legitimate services may be disrupted or redirected to attacker-controlled infrastructure",
|
|
14
|
+
"RelatedUrl": "",
|
|
15
|
+
"AdditionalURLs": [
|
|
16
|
+
"https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-dns-records/"
|
|
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 DNS > Records\n3. Identify CNAME, MX, NS, or SRV records with dangling targets\n4. Either update the record to point to a valid target or delete the record\n5. If the target service was decommissioned, remove the DNS record",
|
|
23
|
+
"Terraform": ""
|
|
24
|
+
},
|
|
25
|
+
"Recommendation": {
|
|
26
|
+
"Text": "Remove or update **dangling DNS records** to prevent takeover and interception attacks.\n- Regularly audit DNS records when decommissioning services\n- Remove CNAME, MX, NS, and SRV records pointing to deprovisioned resources\n- Monitor for unauthorized changes to DNS records\n- Consider using DNS monitoring tools to detect dangling records",
|
|
27
|
+
"Url": "https://hub.prowler.com/checks/cloudflare/dns_record_cname_target_valid"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"Categories": [
|
|
31
|
+
"internet-exposed"
|
|
32
|
+
],
|
|
33
|
+
"DependsOn": [],
|
|
34
|
+
"RelatedTo": [],
|
|
35
|
+
"Notes": "Subdomain takeover occurs when a CNAME or NS record points to a service that has been deprovisioned, allowing attackers to claim that service and control the subdomain. Similarly, dangling MX records can allow mail interception, and dangling SRV records can expose service discovery vulnerabilities."
|
|
36
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import socket
|
|
2
|
+
|
|
3
|
+
from prowler.lib.check.models import Check, CheckReportCloudflare
|
|
4
|
+
from prowler.providers.cloudflare.services.dns.dns_client import dns_client
|
|
5
|
+
|
|
6
|
+
# Record types that point to hostnames and can be dangling:
|
|
7
|
+
# - CNAME: Alias to another hostname
|
|
8
|
+
# - MX: Mail server hostname (dangling = potential mail interception)
|
|
9
|
+
# - NS: Nameserver delegation (dangling = subdomain takeover)
|
|
10
|
+
# - SRV: Service location hostname
|
|
11
|
+
DANGLING_RISK_TYPES = {"CNAME", "MX", "NS", "SRV"}
|
|
12
|
+
|
|
13
|
+
# Risk descriptions for each record type
|
|
14
|
+
RISK_DESCRIPTIONS = {
|
|
15
|
+
"CNAME": "subdomain takeover risk",
|
|
16
|
+
"MX": "potential mail interception risk",
|
|
17
|
+
"NS": "subdomain delegation takeover risk",
|
|
18
|
+
"SRV": "service discovery vulnerability",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class dns_record_cname_target_valid(Check):
|
|
23
|
+
"""Ensure that DNS records pointing to hostnames have valid, resolvable targets.
|
|
24
|
+
|
|
25
|
+
Dangling DNS records that point to non-existent or unresolvable targets pose
|
|
26
|
+
significant security risks. CNAME and NS records can lead to subdomain takeover,
|
|
27
|
+
MX records can allow mail interception, and SRV records can expose service
|
|
28
|
+
vulnerabilities. Attackers can claim orphaned target resources and serve
|
|
29
|
+
malicious content, intercept email, or hijack services under your domain.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def execute(self) -> list[CheckReportCloudflare]:
|
|
33
|
+
"""Execute the dangling DNS record validation check.
|
|
34
|
+
|
|
35
|
+
Iterates through all DNS records that point to hostnames (CNAME, MX, NS, SRV)
|
|
36
|
+
and attempts to resolve their targets using DNS lookup. Records pointing to
|
|
37
|
+
unresolvable targets are flagged as potential security risks.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
A list of CheckReportCloudflare objects with PASS status if the
|
|
41
|
+
target resolves successfully, or FAIL status if the target
|
|
42
|
+
cannot be resolved (dangling record).
|
|
43
|
+
"""
|
|
44
|
+
findings = []
|
|
45
|
+
|
|
46
|
+
for record in dns_client.records:
|
|
47
|
+
# Check record types that point to hostnames
|
|
48
|
+
if record.type not in DANGLING_RISK_TYPES:
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
report = CheckReportCloudflare(
|
|
52
|
+
metadata=self.metadata(),
|
|
53
|
+
resource=record,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
target = self._extract_target(record.type, record.content)
|
|
57
|
+
is_valid = self._check_target_resolves(target)
|
|
58
|
+
risk_desc = RISK_DESCRIPTIONS.get(record.type, "security risk")
|
|
59
|
+
|
|
60
|
+
if is_valid:
|
|
61
|
+
report.status = "PASS"
|
|
62
|
+
report.status_extended = f"{record.type} record {record.name} points to valid target {target}."
|
|
63
|
+
else:
|
|
64
|
+
report.status = "FAIL"
|
|
65
|
+
report.status_extended = (
|
|
66
|
+
f"{record.type} record {record.name} points to potentially dangling "
|
|
67
|
+
f"target {target} - {risk_desc}."
|
|
68
|
+
)
|
|
69
|
+
findings.append(report)
|
|
70
|
+
|
|
71
|
+
return findings
|
|
72
|
+
|
|
73
|
+
def _extract_target(self, record_type: str, content: str) -> str:
|
|
74
|
+
"""Extract the target hostname from record content.
|
|
75
|
+
|
|
76
|
+
Different record types have different content formats:
|
|
77
|
+
- CNAME: hostname
|
|
78
|
+
- MX: priority hostname (e.g., "10 mail.example.com")
|
|
79
|
+
- NS: hostname
|
|
80
|
+
- SRV: Cloudflare returns "weight port hostname" (e.g., "5 80 sip.example.com")
|
|
81
|
+
"""
|
|
82
|
+
if record_type == "MX":
|
|
83
|
+
# MX format: "priority hostname"
|
|
84
|
+
parts = content.split(None, 1)
|
|
85
|
+
return parts[1] if len(parts) > 1 else content
|
|
86
|
+
elif record_type == "SRV":
|
|
87
|
+
# SRV format from Cloudflare: "weight port hostname"
|
|
88
|
+
parts = content.split()
|
|
89
|
+
# Target is the last part (hostname)
|
|
90
|
+
return parts[-1] if parts else content
|
|
91
|
+
else:
|
|
92
|
+
# CNAME and NS are just hostnames
|
|
93
|
+
return content
|
|
94
|
+
|
|
95
|
+
def _check_target_resolves(self, target: str) -> bool:
|
|
96
|
+
"""Check if target hostname resolves to a valid address."""
|
|
97
|
+
# Remove trailing dot if present
|
|
98
|
+
target = target.rstrip(".")
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
# Attempt DNS resolution
|
|
102
|
+
socket.getaddrinfo(target, None, socket.AF_UNSPEC)
|
|
103
|
+
return True
|
|
104
|
+
except socket.gaierror:
|
|
105
|
+
# DNS resolution failed - potential dangling record
|
|
106
|
+
return False
|
|
107
|
+
except Exception:
|
|
108
|
+
# On any other error, assume valid to avoid false positives
|
|
109
|
+
return True
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Provider": "cloudflare",
|
|
3
|
+
"CheckID": "dns_record_no_internal_ip",
|
|
4
|
+
"CheckTitle": "DNS records do not expose internal IP addresses",
|
|
5
|
+
"CheckType": [],
|
|
6
|
+
"ServiceName": "dns",
|
|
7
|
+
"SubServiceName": "",
|
|
8
|
+
"ResourceIdTemplate": "",
|
|
9
|
+
"Severity": "high",
|
|
10
|
+
"ResourceType": "DNSRecord",
|
|
11
|
+
"ResourceGroup": "network",
|
|
12
|
+
"Description": "**Cloudflare DNS records** are assessed for **internal IP exposure** by checking if A or AAAA records point to private, loopback, or reserved IP addresses which could **leak internal network structure**.",
|
|
13
|
+
"Risk": "DNS records exposing **internal IP addresses** leak sensitive network information.\n- **Confidentiality**: reveals internal network topology and addressing schemes to attackers\n- **Integrity**: provides reconnaissance data for targeted attacks on internal infrastructure\n- **Availability**: internal IPs in public DNS may indicate misconfiguration affecting service routing",
|
|
14
|
+
"RelatedUrl": "",
|
|
15
|
+
"AdditionalURLs": [
|
|
16
|
+
"https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-dns-records/"
|
|
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 DNS > Records\n3. Identify A/AAAA records pointing to internal IP addresses\n4. Update records to point to public IP addresses or remove if not needed\n5. Use split-horizon DNS if internal resolution is required",
|
|
23
|
+
"Terraform": ""
|
|
24
|
+
},
|
|
25
|
+
"Recommendation": {
|
|
26
|
+
"Text": "Remove **internal IP addresses** from public DNS records.\n- Use split-horizon DNS for internal service resolution\n- Ensure DNS records only contain publicly routable IP addresses\n- Review DNS records after network changes or migrations\n- Consider using Cloudflare Access for secure internal service access",
|
|
27
|
+
"Url": "https://hub.prowler.com/checks/cloudflare/dns_record_no_internal_ip"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"Categories": [
|
|
31
|
+
"internet-exposed"
|
|
32
|
+
],
|
|
33
|
+
"DependsOn": [],
|
|
34
|
+
"RelatedTo": [],
|
|
35
|
+
"Notes": "Internal IP ranges include: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 (IPv4), fc00::/7 (IPv6 ULA), and loopback addresses. These should not appear in public DNS records."
|
|
36
|
+
}
|
prowler/providers/cloudflare/services/dns/dns_record_no_internal_ip/dns_record_no_internal_ip.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import ipaddress
|
|
2
|
+
|
|
3
|
+
from prowler.lib.check.models import Check, CheckReportCloudflare
|
|
4
|
+
from prowler.providers.cloudflare.services.dns.dns_client import dns_client
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class dns_record_no_internal_ip(Check):
|
|
8
|
+
"""Ensure that DNS records do not expose internal or private IP addresses.
|
|
9
|
+
|
|
10
|
+
Public DNS records should only contain publicly routable IP addresses.
|
|
11
|
+
Exposing internal, private, loopback, or link-local addresses in DNS records
|
|
12
|
+
can leak information about internal network infrastructure, potentially
|
|
13
|
+
aiding attackers in reconnaissance and targeted attacks against internal
|
|
14
|
+
systems.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def execute(self) -> list[CheckReportCloudflare]:
|
|
18
|
+
"""Execute the internal IP address exposure check.
|
|
19
|
+
|
|
20
|
+
Iterates through all A and AAAA DNS records and checks if they contain
|
|
21
|
+
private, loopback, link-local, or reserved IP addresses that should not
|
|
22
|
+
be exposed publicly.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
A list of CheckReportCloudflare objects with PASS status if the
|
|
26
|
+
record points to a public IP address, or FAIL status if it exposes
|
|
27
|
+
an internal IP address.
|
|
28
|
+
"""
|
|
29
|
+
findings = []
|
|
30
|
+
|
|
31
|
+
for record in dns_client.records:
|
|
32
|
+
# Only check A and AAAA records
|
|
33
|
+
if record.type not in ("A", "AAAA"):
|
|
34
|
+
continue
|
|
35
|
+
|
|
36
|
+
report = CheckReportCloudflare(
|
|
37
|
+
metadata=self.metadata(),
|
|
38
|
+
resource=record,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
is_internal = self._is_internal_ip(record.content)
|
|
42
|
+
|
|
43
|
+
if not is_internal:
|
|
44
|
+
report.status = "PASS"
|
|
45
|
+
report.status_extended = (
|
|
46
|
+
f"DNS record {record.name} ({record.type}) points to "
|
|
47
|
+
f"public IP address {record.content}."
|
|
48
|
+
)
|
|
49
|
+
else:
|
|
50
|
+
report.status = "FAIL"
|
|
51
|
+
report.status_extended = (
|
|
52
|
+
f"DNS record {record.name} ({record.type}) exposes "
|
|
53
|
+
f"internal IP address {record.content} - information disclosure risk."
|
|
54
|
+
)
|
|
55
|
+
findings.append(report)
|
|
56
|
+
|
|
57
|
+
return findings
|
|
58
|
+
|
|
59
|
+
def _is_internal_ip(self, ip_str: str) -> bool:
|
|
60
|
+
"""Check if IP address is internal/private."""
|
|
61
|
+
try:
|
|
62
|
+
ip = ipaddress.ip_address(ip_str)
|
|
63
|
+
# Check for private, loopback, link-local, or reserved addresses
|
|
64
|
+
return (
|
|
65
|
+
ip.is_private
|
|
66
|
+
or ip.is_loopback
|
|
67
|
+
or ip.is_link_local
|
|
68
|
+
or ip.is_reserved
|
|
69
|
+
or ip.is_unspecified
|
|
70
|
+
)
|
|
71
|
+
except ValueError:
|
|
72
|
+
# Invalid IP format, assume not internal
|
|
73
|
+
return False
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Provider": "cloudflare",
|
|
3
|
+
"CheckID": "dns_record_no_wildcard",
|
|
4
|
+
"CheckTitle": "DNS records do not use wildcard entries",
|
|
5
|
+
"CheckType": [],
|
|
6
|
+
"ServiceName": "dns",
|
|
7
|
+
"SubServiceName": "",
|
|
8
|
+
"ResourceIdTemplate": "",
|
|
9
|
+
"Severity": "medium",
|
|
10
|
+
"ResourceType": "DNSRecord",
|
|
11
|
+
"ResourceGroup": "network",
|
|
12
|
+
"Description": "**Cloudflare DNS records** are assessed for **wildcard usage** by checking if A, AAAA, CNAME, MX, or SRV records use wildcard entries (*.example.com) which can **increase attack surface**, expose unintended services, or allow mail interception.",
|
|
13
|
+
"Risk": "**Wildcard DNS records** can expose unintended services and increase attack surface.\n- **Confidentiality**: any subdomain resolves, potentially exposing internal naming conventions; wildcard MX allows mail interception\n- **Integrity**: attackers can access unintended services via arbitrary subdomains\n- **Availability**: wildcard records may route traffic or services not designed for public access",
|
|
14
|
+
"RelatedUrl": "",
|
|
15
|
+
"AdditionalURLs": [
|
|
16
|
+
"https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-dns-records/"
|
|
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 DNS > Records\n3. Identify wildcard DNS records (starting with *.)\n4. Evaluate if the wildcard is necessary for your use case\n5. Replace wildcard records with specific subdomain records where possible",
|
|
23
|
+
"Terraform": ""
|
|
24
|
+
},
|
|
25
|
+
"Recommendation": {
|
|
26
|
+
"Text": "Avoid using **wildcard DNS records** unless absolutely necessary.\n- Use specific subdomain records instead of wildcards\n- If wildcards are required, ensure the target service handles unknown subdomains securely\n- Document the business justification for any wildcard records\n- Combine with proper web server configuration to reject unknown hosts",
|
|
27
|
+
"Url": "https://hub.prowler.com/checks/cloudflare/dns_record_no_wildcard"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"Categories": [
|
|
31
|
+
"internet-exposed"
|
|
32
|
+
],
|
|
33
|
+
"DependsOn": [],
|
|
34
|
+
"RelatedTo": [],
|
|
35
|
+
"Notes": "Wildcard DNS records (*.example.com) cause any subdomain query to resolve. While useful for some applications, they can expose services unintentionally and make subdomain enumeration easier for attackers. Wildcard MX records can accept mail for any subdomain, and wildcard SRV records can expose services on arbitrary subdomains."
|
|
36
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from prowler.lib.check.models import Check, CheckReportCloudflare
|
|
2
|
+
from prowler.providers.cloudflare.services.dns.dns_client import dns_client
|
|
3
|
+
|
|
4
|
+
# Record types where wildcards pose security risks:
|
|
5
|
+
# - A, AAAA: Wildcard resolves any subdomain to an IP, exposing services
|
|
6
|
+
# - CNAME: Wildcard aliases any subdomain, potential for subdomain takeover
|
|
7
|
+
# - MX: Wildcard accepts mail for any subdomain, potential mail interception
|
|
8
|
+
# - SRV: Wildcard exposes services on any subdomain
|
|
9
|
+
WILDCARD_RISK_TYPES = {"A", "AAAA", "CNAME", "MX", "SRV"}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class dns_record_no_wildcard(Check):
|
|
13
|
+
"""Ensure that wildcard DNS records are not configured for the zone.
|
|
14
|
+
|
|
15
|
+
Wildcard DNS records (*.domain.com) match any subdomain that doesn't have
|
|
16
|
+
an explicit record, which can unintentionally expose services or create
|
|
17
|
+
security risks. Attackers may discover hidden services, and wildcard
|
|
18
|
+
certificates combined with wildcard DNS can increase the attack surface
|
|
19
|
+
for subdomain takeover vulnerabilities. Wildcard MX records can allow
|
|
20
|
+
mail interception for arbitrary subdomains.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def execute(self) -> list[CheckReportCloudflare]:
|
|
24
|
+
"""Execute the wildcard DNS record check.
|
|
25
|
+
|
|
26
|
+
Iterates through all security-relevant DNS records (A, AAAA, CNAME, MX, SRV)
|
|
27
|
+
and identifies those configured as wildcard records (starting with *.).
|
|
28
|
+
Wildcard records may expose unintended services or create security risks.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
A list of CheckReportCloudflare objects with PASS status if the
|
|
32
|
+
record is not a wildcard, or FAIL status if it is a wildcard record.
|
|
33
|
+
"""
|
|
34
|
+
findings = []
|
|
35
|
+
|
|
36
|
+
for record in dns_client.records:
|
|
37
|
+
# Check record types where wildcards pose security risks
|
|
38
|
+
if record.type not in WILDCARD_RISK_TYPES:
|
|
39
|
+
continue
|
|
40
|
+
|
|
41
|
+
report = CheckReportCloudflare(
|
|
42
|
+
metadata=self.metadata(),
|
|
43
|
+
resource=record,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Check if record name starts with wildcard
|
|
47
|
+
is_wildcard = record.name.startswith("*.")
|
|
48
|
+
|
|
49
|
+
if not is_wildcard:
|
|
50
|
+
report.status = "PASS"
|
|
51
|
+
report.status_extended = f"DNS record {record.name} ({record.type}) is not a wildcard record."
|
|
52
|
+
else:
|
|
53
|
+
report.status = "FAIL"
|
|
54
|
+
report.status_extended = (
|
|
55
|
+
f"DNS record {record.name} ({record.type}) is a wildcard record - "
|
|
56
|
+
f"may expose unintended services."
|
|
57
|
+
)
|
|
58
|
+
findings.append(report)
|
|
59
|
+
|
|
60
|
+
return findings
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Provider": "cloudflare",
|
|
3
|
+
"CheckID": "dns_record_proxied",
|
|
4
|
+
"CheckTitle": "Cloudflare proxy is enabled for applicable DNS records",
|
|
5
|
+
"CheckType": [],
|
|
6
|
+
"ServiceName": "dns",
|
|
7
|
+
"SubServiceName": "",
|
|
8
|
+
"ResourceIdTemplate": "",
|
|
9
|
+
"Severity": "medium",
|
|
10
|
+
"ResourceType": "DNSRecord",
|
|
11
|
+
"ResourceGroup": "network",
|
|
12
|
+
"Description": "**Cloudflare DNS records** are assessed for **proxy configuration** by checking if A, AAAA, and CNAME records are proxied through Cloudflare to benefit from **DDoS protection**, **WAF**, and **caching** capabilities.",
|
|
13
|
+
"Risk": "Unproxied **DNS records** expose origin server IP addresses directly to the internet.\n- **Confidentiality**: origin IP exposure enables targeted reconnaissance and attacks\n- **Integrity**: direct access to origin bypasses WAF and security controls\n- **Availability**: origin is exposed to DDoS attacks without Cloudflare protection",
|
|
14
|
+
"RelatedUrl": "",
|
|
15
|
+
"AdditionalURLs": [
|
|
16
|
+
"https://developers.cloudflare.com/dns/manage-dns-records/reference/proxied-dns-records/"
|
|
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 DNS > Records\n3. For each A, AAAA, or CNAME record that should be protected\n4. Click Edit and toggle Proxy status to Proxied (orange cloud)\n5. Save the changes and verify traffic flows through Cloudflare",
|
|
23
|
+
"Terraform": "```hcl\n# Enable Cloudflare proxy for DNS records\nresource \"cloudflare_record\" \"proxied_record\" {\n zone_id = \"<ZONE_ID>\"\n name = \"www\"\n content = \"192.0.2.1\"\n type = \"A\"\n proxied = true # Critical: enables DDoS protection, WAF, and caching\n}\n```"
|
|
24
|
+
},
|
|
25
|
+
"Recommendation": {
|
|
26
|
+
"Text": "Enable the **Cloudflare proxy** (orange cloud) for DNS records that should be protected.\n- Proxied records benefit from DDoS protection, WAF, and caching\n- Origin server IP addresses are hidden from public DNS queries\n- Apply defense in depth by combining proxy protection with origin hardening\n- Some record types (MX, TXT) cannot be proxied by design",
|
|
27
|
+
"Url": "https://hub.prowler.com/checks/cloudflare/dns_record_proxied"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"Categories": [
|
|
31
|
+
"internet-exposed"
|
|
32
|
+
],
|
|
33
|
+
"DependsOn": [],
|
|
34
|
+
"RelatedTo": [],
|
|
35
|
+
"Notes": "Only A, AAAA, and CNAME records can be proxied. MX, TXT, and other record types are always DNS-only. Some services may require DNS-only mode for specific use cases."
|
|
36
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from prowler.lib.check.models import Check, CheckReportCloudflare
|
|
2
|
+
from prowler.providers.cloudflare.services.dns.dns_client import dns_client
|
|
3
|
+
|
|
4
|
+
PROXYABLE_TYPES = {"A", "AAAA", "CNAME"}
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class dns_record_proxied(Check):
|
|
8
|
+
"""Ensure that DNS records are proxied through Cloudflare.
|
|
9
|
+
|
|
10
|
+
Proxying DNS records through Cloudflare hides the origin server's IP address
|
|
11
|
+
and provides DDoS protection, WAF capabilities, and performance optimizations.
|
|
12
|
+
Non-proxied (DNS-only) records expose the origin IP directly, bypassing
|
|
13
|
+
Cloudflare's security features and making the origin vulnerable to direct
|
|
14
|
+
attacks.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def execute(self) -> list[CheckReportCloudflare]:
|
|
18
|
+
"""Execute the DNS record proxy status check.
|
|
19
|
+
|
|
20
|
+
Iterates through all proxyable DNS records (A, AAAA, CNAME) and verifies
|
|
21
|
+
that they are configured to be proxied through Cloudflare. Non-proxied
|
|
22
|
+
records bypass Cloudflare's security and performance features.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
A list of CheckReportCloudflare objects with PASS status if the
|
|
26
|
+
record is proxied through Cloudflare, or FAIL status if it is
|
|
27
|
+
DNS-only (not proxied).
|
|
28
|
+
"""
|
|
29
|
+
findings = []
|
|
30
|
+
|
|
31
|
+
for record in dns_client.records:
|
|
32
|
+
# Only check proxyable record types
|
|
33
|
+
if record.type not in PROXYABLE_TYPES:
|
|
34
|
+
continue
|
|
35
|
+
|
|
36
|
+
report = CheckReportCloudflare(
|
|
37
|
+
metadata=self.metadata(),
|
|
38
|
+
resource=record,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if record.proxied:
|
|
42
|
+
report.status = "PASS"
|
|
43
|
+
report.status_extended = f"DNS record {record.name} ({record.type}) is proxied through Cloudflare."
|
|
44
|
+
else:
|
|
45
|
+
report.status = "FAIL"
|
|
46
|
+
report.status_extended = f"DNS record {record.name} ({record.type}) is not proxied through Cloudflare."
|
|
47
|
+
findings.append(report)
|
|
48
|
+
|
|
49
|
+
return findings
|