prowler-cloud 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_cloud-5.17.1.dist-info → prowler_cloud-5.18.0.dist-info}/METADATA +11 -9
- {prowler_cloud-5.17.1.dist-info → prowler_cloud-5.18.0.dist-info}/RECORD +219 -155
- {prowler_cloud-5.17.1.dist-info → prowler_cloud-5.18.0.dist-info}/LICENSE +0 -0
- {prowler_cloud-5.17.1.dist-info → prowler_cloud-5.18.0.dist-info}/WHEEL +0 -0
- {prowler_cloud-5.17.1.dist-info → prowler_cloud-5.18.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""CloudTrail timeline service for AWS.
|
|
2
|
+
|
|
3
|
+
Queries AWS CloudTrail to retrieve timeline events for resources,
|
|
4
|
+
showing who performed actions and when.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from datetime import datetime, timedelta, timezone
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
from botocore.exceptions import ClientError
|
|
12
|
+
|
|
13
|
+
from prowler.lib.logger import logger
|
|
14
|
+
from prowler.lib.timeline.models import TimelineEvent
|
|
15
|
+
from prowler.lib.timeline.timeline import TimelineService
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CloudTrailTimeline(TimelineService):
|
|
19
|
+
"""AWS CloudTrail implementation of TimelineService.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
session: boto3.Session for AWS API calls
|
|
23
|
+
lookback_days: Number of days to look back (default 90, max 90 for Event History)
|
|
24
|
+
max_results: Maximum number of events to return
|
|
25
|
+
write_events_only: If True, filter out read-only events (Describe*, Get*, List*, etc.)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
MAX_LOOKBACK_DAYS = 90
|
|
29
|
+
|
|
30
|
+
DEFAULT_MAX_RESULTS = 50 # Default page size for CloudTrail queries
|
|
31
|
+
|
|
32
|
+
# Prefixes for read-only API operations that don't modify resources
|
|
33
|
+
READ_ONLY_PREFIXES = (
|
|
34
|
+
"Describe",
|
|
35
|
+
"Get",
|
|
36
|
+
"List",
|
|
37
|
+
"Head",
|
|
38
|
+
"Check",
|
|
39
|
+
"Lookup",
|
|
40
|
+
"Search",
|
|
41
|
+
"Scan",
|
|
42
|
+
"Query",
|
|
43
|
+
"BatchGet",
|
|
44
|
+
"Select",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
session,
|
|
50
|
+
lookback_days: int = 90,
|
|
51
|
+
max_results: Optional[int] = None,
|
|
52
|
+
write_events_only: bool = True,
|
|
53
|
+
):
|
|
54
|
+
self._session = session
|
|
55
|
+
self._lookback_days = min(lookback_days, self.MAX_LOOKBACK_DAYS)
|
|
56
|
+
self._max_results = max_results or self.DEFAULT_MAX_RESULTS
|
|
57
|
+
self._write_events_only = write_events_only
|
|
58
|
+
self._clients: Dict[str, Any] = {}
|
|
59
|
+
|
|
60
|
+
DEFAULT_REGION = "us-east-1" # Default for global resources in commercial partition
|
|
61
|
+
|
|
62
|
+
def get_resource_timeline(
|
|
63
|
+
self,
|
|
64
|
+
region: Optional[str] = None,
|
|
65
|
+
resource_id: Optional[str] = None,
|
|
66
|
+
resource_uid: Optional[str] = None,
|
|
67
|
+
) -> List[Dict[str, Any]]:
|
|
68
|
+
"""Get CloudTrail timeline events for a resource.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
region: AWS region to query. Defaults to us-east-1 for global resources
|
|
72
|
+
(IAM, S3, Route53, etc.) in the commercial partition. Caller
|
|
73
|
+
should provide the correct region for regional resources.
|
|
74
|
+
resource_id: AWS resource ID (e.g., sg-1234567890abcdef0)
|
|
75
|
+
resource_uid: AWS resource ARN (unique identifier)
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
List of timeline event dictionaries
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
ValueError: If neither resource_id nor resource_uid is provided
|
|
82
|
+
ClientError: If AWS API call fails
|
|
83
|
+
"""
|
|
84
|
+
resource_identifier = resource_uid or resource_id
|
|
85
|
+
if not resource_identifier:
|
|
86
|
+
raise ValueError("Either resource_id or resource_uid must be provided")
|
|
87
|
+
|
|
88
|
+
region = region or self.DEFAULT_REGION
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
raw_events = self._lookup_events(resource_identifier, region)
|
|
92
|
+
|
|
93
|
+
events = []
|
|
94
|
+
for raw_event in raw_events:
|
|
95
|
+
# Filter read-only events if write_events_only is enabled
|
|
96
|
+
if self._write_events_only:
|
|
97
|
+
event_name = raw_event.get("EventName", "")
|
|
98
|
+
if self._is_read_only_event(event_name):
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
parsed = self._parse_event(raw_event)
|
|
102
|
+
if parsed:
|
|
103
|
+
events.append(parsed)
|
|
104
|
+
|
|
105
|
+
return events
|
|
106
|
+
|
|
107
|
+
except ClientError as e:
|
|
108
|
+
logger.error(
|
|
109
|
+
f"CloudTrail timeline error for {resource_identifier} in {region}: "
|
|
110
|
+
f"{e.response['Error']['Code']} - {e.response['Error']['Message']}"
|
|
111
|
+
)
|
|
112
|
+
raise
|
|
113
|
+
except Exception as e:
|
|
114
|
+
lineno = e.__traceback__.tb_lineno if e.__traceback__ else "?"
|
|
115
|
+
logger.error(
|
|
116
|
+
f"CloudTrail timeline unexpected error: "
|
|
117
|
+
f"{e.__class__.__name__}[{lineno}]: {e}"
|
|
118
|
+
)
|
|
119
|
+
return []
|
|
120
|
+
|
|
121
|
+
def _is_read_only_event(self, event_name: str) -> bool:
|
|
122
|
+
"""Check if an event is a read-only operation."""
|
|
123
|
+
return event_name.startswith(self.READ_ONLY_PREFIXES)
|
|
124
|
+
|
|
125
|
+
def _get_client(self, region: str):
|
|
126
|
+
"""Get or create a CloudTrail client for the specified region."""
|
|
127
|
+
if region not in self._clients:
|
|
128
|
+
self._clients[region] = self._session.client(
|
|
129
|
+
"cloudtrail", region_name=region
|
|
130
|
+
)
|
|
131
|
+
return self._clients[region]
|
|
132
|
+
|
|
133
|
+
def _lookup_events(
|
|
134
|
+
self, resource_identifier: str, region: str
|
|
135
|
+
) -> List[Dict[str, Any]]:
|
|
136
|
+
"""Query CloudTrail for events related to a specific resource.
|
|
137
|
+
|
|
138
|
+
Uses MaxResults to limit the number of events returned, preparing
|
|
139
|
+
for API-level pagination. Currently returns up to max_results events
|
|
140
|
+
from the first page only.
|
|
141
|
+
"""
|
|
142
|
+
client = self._get_client(region)
|
|
143
|
+
start_time = datetime.now(timezone.utc) - timedelta(days=self._lookback_days)
|
|
144
|
+
|
|
145
|
+
# Use direct API call with MaxResults instead of paginator
|
|
146
|
+
# This limits CloudTrail to return only max_results events
|
|
147
|
+
response = client.lookup_events(
|
|
148
|
+
LookupAttributes=[
|
|
149
|
+
{"AttributeKey": "ResourceName", "AttributeValue": resource_identifier}
|
|
150
|
+
],
|
|
151
|
+
StartTime=start_time,
|
|
152
|
+
MaxResults=self._max_results,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
return response.get("Events", [])
|
|
156
|
+
|
|
157
|
+
def _parse_event(self, raw_event: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
158
|
+
"""Parse a raw CloudTrail event into a TimelineEvent dictionary."""
|
|
159
|
+
try:
|
|
160
|
+
cloud_trail_event = raw_event.get("CloudTrailEvent", "{}")
|
|
161
|
+
if isinstance(cloud_trail_event, str):
|
|
162
|
+
details = json.loads(cloud_trail_event)
|
|
163
|
+
else:
|
|
164
|
+
details = cloud_trail_event
|
|
165
|
+
|
|
166
|
+
user_identity = details.get("userIdentity", {})
|
|
167
|
+
|
|
168
|
+
event = TimelineEvent(
|
|
169
|
+
event_id=raw_event.get("EventId"),
|
|
170
|
+
event_time=raw_event["EventTime"],
|
|
171
|
+
event_name=raw_event.get("EventName", "Unknown"),
|
|
172
|
+
event_source=raw_event.get("EventSource", "Unknown"),
|
|
173
|
+
actor=self._extract_actor(user_identity),
|
|
174
|
+
actor_uid=user_identity.get("arn"),
|
|
175
|
+
actor_type=user_identity.get("type"),
|
|
176
|
+
source_ip_address=details.get("sourceIPAddress"),
|
|
177
|
+
user_agent=details.get("userAgent"),
|
|
178
|
+
request_data=details.get("requestParameters"),
|
|
179
|
+
response_data=details.get("responseElements"),
|
|
180
|
+
error_code=details.get("errorCode"),
|
|
181
|
+
error_message=details.get("errorMessage"),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
return event.dict()
|
|
185
|
+
|
|
186
|
+
except Exception as e:
|
|
187
|
+
logger.warning(
|
|
188
|
+
f"CloudTrail timeline: failed to parse event: "
|
|
189
|
+
f"{e.__class__.__name__}: {e}"
|
|
190
|
+
)
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
@staticmethod
|
|
194
|
+
def _extract_actor(user_identity: Dict[str, Any]) -> str:
|
|
195
|
+
"""Extract a human-readable actor name from CloudTrail userIdentity."""
|
|
196
|
+
# Try ARN first - most reliable
|
|
197
|
+
if arn := user_identity.get("arn"):
|
|
198
|
+
if "/" in arn:
|
|
199
|
+
parts = arn.split("/")
|
|
200
|
+
# For assumed-role, return the role name (second-to-last part)
|
|
201
|
+
if "assumed-role" in arn and len(parts) >= 2:
|
|
202
|
+
return parts[-2]
|
|
203
|
+
return parts[-1]
|
|
204
|
+
return arn.split(":")[-1]
|
|
205
|
+
|
|
206
|
+
# Fall back to userName
|
|
207
|
+
if username := user_identity.get("userName"):
|
|
208
|
+
return username
|
|
209
|
+
|
|
210
|
+
# Fall back to principalId
|
|
211
|
+
if principal_id := user_identity.get("principalId"):
|
|
212
|
+
return principal_id
|
|
213
|
+
|
|
214
|
+
# For service-invoked actions
|
|
215
|
+
if invoking_service := user_identity.get("invokedBy"):
|
|
216
|
+
return invoking_service
|
|
217
|
+
|
|
218
|
+
return "Unknown"
|
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Provider": "aws",
|
|
3
|
+
"CheckID": "codebuild_project_webhook_filters_use_anchored_patterns",
|
|
4
|
+
"CheckTitle": "CodeBuild project webhook filters use anchored regex patterns",
|
|
5
|
+
"CheckType": [
|
|
6
|
+
"Software and Configuration Checks/AWS Security Best Practices"
|
|
7
|
+
],
|
|
8
|
+
"ServiceName": "codebuild",
|
|
9
|
+
"SubServiceName": "",
|
|
10
|
+
"ResourceIdTemplate": "",
|
|
11
|
+
"Severity": "high",
|
|
12
|
+
"ResourceType": "AwsCodeBuildProject",
|
|
13
|
+
"ResourceGroup": "devops",
|
|
14
|
+
"Description": "AWS CodeBuild webhook filters using `ACTOR_ACCOUNT_ID`, `HEAD_REF`, or `BASE_REF` have regex patterns anchored with `^` (start) and `$` (end) to enforce exact matching and prevent substring bypass attacks.",
|
|
15
|
+
"Risk": "Unanchored patterns expose CI/CD pipelines to **CodeBreach** attacks. Attackers can bypass `ACTOR_ACCOUNT_ID` filters by creating GitHub accounts with IDs containing trusted values as substrings. **Confidentiality**: Credentials leaked via build logs. **Integrity**: Malicious code injected into builds. **Availability**: Resource exhaustion through unauthorized builds.",
|
|
16
|
+
"RelatedUrl": "",
|
|
17
|
+
"Remediation": {
|
|
18
|
+
"Code": {
|
|
19
|
+
"CLI": "aws codebuild update-webhook --project-name <PROJECT_NAME> --filter-groups '[[{\"type\":\"ACTOR_ACCOUNT_ID\",\"pattern\":\"^123456$|^234567$\"}]]'",
|
|
20
|
+
"NativeIaC": "AWSTemplateFormatVersion: '2010-09-09'\nResources:\n CodeBuildWebhook:\n Type: AWS::CodeBuild::Project\n Properties:\n Triggers:\n Webhook: true\n FilterGroups:\n - - Type: ACTOR_ACCOUNT_ID\n Pattern: '^123456$|^234567$' # Anchored pattern",
|
|
21
|
+
"Other": "1. Open AWS Console and navigate to CodeBuild. 2. Select the project with webhook filters. 3. Click Edit and go to Primary source webhook events. 4. For each filter using ACTOR_ACCOUNT_ID, HEAD_REF, or BASE_REF, update patterns to include ^ at start and $ at end (e.g., change '123456|234567' to '^123456$|^234567$'). 5. Save changes.",
|
|
22
|
+
"Terraform": "resource \"aws_codebuild_webhook\" \"example\" {\n project_name = aws_codebuild_project.example.name\n filter_group {\n filter {\n type = \"ACTOR_ACCOUNT_ID\"\n pattern = \"^123456$|^234567$\" # Anchored pattern\n }\n }\n}"
|
|
23
|
+
},
|
|
24
|
+
"Recommendation": {
|
|
25
|
+
"Text": "Anchor all webhook filter patterns with `^` (start) and `$` (end) to enforce exact matching. For multiple values use: `^value1$|^value2$`. This prevents attackers from bypassing filters using substring matches.",
|
|
26
|
+
"Url": "https://hub.prowler.com/check/codebuild_project_webhook_filters_use_anchored_patterns"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"Categories": [
|
|
30
|
+
"software-supply-chain",
|
|
31
|
+
"ci-cd"
|
|
32
|
+
],
|
|
33
|
+
"DependsOn": [],
|
|
34
|
+
"RelatedTo": [],
|
|
35
|
+
"Notes": "This check targets the CodeBreach vulnerability disclosed by Wiz Research. The vulnerability allows attackers to bypass ACTOR_ACCOUNT_ID filters by creating GitHub accounts with IDs that contain trusted IDs as substrings.",
|
|
36
|
+
"AdditionalURLs": [
|
|
37
|
+
"https://www.wiz.io/blog/wiz-research-codebreach-vulnerability-aws-codebuild",
|
|
38
|
+
"https://docs.aws.amazon.com/codebuild/latest/userguide/github-webhook.html"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from prowler.lib.check.models import Check, Check_Report_AWS
|
|
4
|
+
from prowler.providers.aws.services.codebuild.codebuild_client import codebuild_client
|
|
5
|
+
|
|
6
|
+
HIGH_RISK_FILTER_TYPES = {"ACTOR_ACCOUNT_ID", "HEAD_REF", "BASE_REF"}
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def is_pattern_anchored(pattern: str) -> bool:
|
|
10
|
+
"""Check if each alternative in a pipe-separated pattern is anchored with ^ and $."""
|
|
11
|
+
if not pattern:
|
|
12
|
+
return True
|
|
13
|
+
|
|
14
|
+
for alt in pattern.split("|"):
|
|
15
|
+
alt = alt.strip()
|
|
16
|
+
if alt and not (alt.startswith("^") and alt.endswith("$")):
|
|
17
|
+
return False
|
|
18
|
+
return True
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class codebuild_project_webhook_filters_use_anchored_patterns(Check):
|
|
22
|
+
def execute(self) -> List[Check_Report_AWS]:
|
|
23
|
+
findings = []
|
|
24
|
+
|
|
25
|
+
for project in codebuild_client.projects.values():
|
|
26
|
+
report = Check_Report_AWS(metadata=self.metadata(), resource=project)
|
|
27
|
+
report.status = "PASS"
|
|
28
|
+
report.status_extended = (
|
|
29
|
+
f"CodeBuild project {project.name} has no webhook configured or all "
|
|
30
|
+
"webhook filter patterns are properly anchored."
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if not project.webhook or not project.webhook.filter_groups:
|
|
34
|
+
findings.append(report)
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
unanchored_filters = []
|
|
38
|
+
for filter_group in project.webhook.filter_groups:
|
|
39
|
+
for webhook_filter in filter_group.filters:
|
|
40
|
+
if webhook_filter.type in HIGH_RISK_FILTER_TYPES:
|
|
41
|
+
if not is_pattern_anchored(webhook_filter.pattern):
|
|
42
|
+
unanchored_filters.append(
|
|
43
|
+
f"{webhook_filter.type}: '{webhook_filter.pattern}'"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if unanchored_filters:
|
|
47
|
+
report.status = "FAIL"
|
|
48
|
+
filters_str = ", ".join(unanchored_filters[:3])
|
|
49
|
+
if len(unanchored_filters) > 3:
|
|
50
|
+
filters_str += f" and {len(unanchored_filters) - 3} more"
|
|
51
|
+
report.status_extended = (
|
|
52
|
+
f"CodeBuild project {project.name} has webhook filters with "
|
|
53
|
+
f"unanchored patterns that could allow bypass attacks: {filters_str}."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
findings.append(report)
|
|
57
|
+
|
|
58
|
+
return findings
|
|
@@ -122,6 +122,29 @@ class Codebuild(AWSService):
|
|
|
122
122
|
project.tags = project_info.get("tags", [])
|
|
123
123
|
project.service_role_arn = project_info.get("serviceRole", "")
|
|
124
124
|
project.project_visibility = project_info.get("projectVisibility", "")
|
|
125
|
+
|
|
126
|
+
# Extract webhook configuration
|
|
127
|
+
webhook_data = project_info.get("webhook")
|
|
128
|
+
if webhook_data:
|
|
129
|
+
filter_groups = []
|
|
130
|
+
for fg in webhook_data.get("filterGroups", []):
|
|
131
|
+
filters = []
|
|
132
|
+
for f in fg:
|
|
133
|
+
filters.append(
|
|
134
|
+
WebhookFilter(
|
|
135
|
+
type=f.get("type", ""),
|
|
136
|
+
pattern=f.get("pattern", ""),
|
|
137
|
+
exclude_matched_pattern=f.get(
|
|
138
|
+
"excludeMatchedPattern", False
|
|
139
|
+
),
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
filter_groups.append(WebhookFilterGroup(filters=filters))
|
|
143
|
+
|
|
144
|
+
project.webhook = Webhook(
|
|
145
|
+
filter_groups=filter_groups,
|
|
146
|
+
branch_filter=webhook_data.get("branchFilter"),
|
|
147
|
+
)
|
|
125
148
|
except Exception as error:
|
|
126
149
|
logger.error(
|
|
127
150
|
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
@@ -209,6 +232,27 @@ class CloudWatchLogs(BaseModel):
|
|
|
209
232
|
stream_name: str
|
|
210
233
|
|
|
211
234
|
|
|
235
|
+
class WebhookFilter(BaseModel):
|
|
236
|
+
"""Represents a single filter in a webhook filter group."""
|
|
237
|
+
|
|
238
|
+
type: str # ACTOR_ACCOUNT_ID, HEAD_REF, BASE_REF, EVENT, etc.
|
|
239
|
+
pattern: str
|
|
240
|
+
exclude_matched_pattern: bool = False
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class WebhookFilterGroup(BaseModel):
|
|
244
|
+
"""Represents a group of filters (AND logic within group)."""
|
|
245
|
+
|
|
246
|
+
filters: List[WebhookFilter] = []
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class Webhook(BaseModel):
|
|
250
|
+
"""Represents the webhook configuration for a CodeBuild project."""
|
|
251
|
+
|
|
252
|
+
filter_groups: List[WebhookFilterGroup] = []
|
|
253
|
+
branch_filter: Optional[str] = None
|
|
254
|
+
|
|
255
|
+
|
|
212
256
|
class Project(BaseModel):
|
|
213
257
|
name: str
|
|
214
258
|
arn: str
|
|
@@ -224,6 +268,7 @@ class Project(BaseModel):
|
|
|
224
268
|
cloudwatch_logs: Optional[CloudWatchLogs]
|
|
225
269
|
tags: Optional[list]
|
|
226
270
|
project_visibility: Optional[str] = None
|
|
271
|
+
webhook: Optional[Webhook] = None
|
|
227
272
|
|
|
228
273
|
|
|
229
274
|
class ExportConfig(BaseModel):
|
|
@@ -6,6 +6,9 @@ from prowler.providers.aws.services.iam.lib.policy import is_policy_public
|
|
|
6
6
|
class dynamodb_table_cross_account_access(Check):
|
|
7
7
|
def execute(self):
|
|
8
8
|
findings = []
|
|
9
|
+
trusted_account_ids = dynamodb_client.audit_config.get(
|
|
10
|
+
"trusted_account_ids", []
|
|
11
|
+
)
|
|
9
12
|
for table in dynamodb_client.tables.values():
|
|
10
13
|
if table.policy is None:
|
|
11
14
|
continue
|
|
@@ -20,6 +23,7 @@ class dynamodb_table_cross_account_access(Check):
|
|
|
20
23
|
table.policy,
|
|
21
24
|
dynamodb_client.audited_account,
|
|
22
25
|
is_cross_account_allowed=False,
|
|
26
|
+
trusted_account_ids=trusted_account_ids,
|
|
23
27
|
):
|
|
24
28
|
report.status = "FAIL"
|
|
25
29
|
report.status_extended = f"DynamoDB table {table.name} has a resource-based policy allowing cross account access."
|
|
@@ -8,6 +8,9 @@ from prowler.providers.aws.services.iam.lib.policy import is_policy_public
|
|
|
8
8
|
class eventbridge_bus_cross_account_access(Check):
|
|
9
9
|
def execute(self):
|
|
10
10
|
findings = []
|
|
11
|
+
trusted_account_ids = eventbridge_client.audit_config.get(
|
|
12
|
+
"trusted_account_ids", []
|
|
13
|
+
)
|
|
11
14
|
for bus in eventbridge_client.buses.values():
|
|
12
15
|
if bus.policy is None:
|
|
13
16
|
continue
|
|
@@ -20,6 +23,7 @@ class eventbridge_bus_cross_account_access(Check):
|
|
|
20
23
|
bus.policy,
|
|
21
24
|
eventbridge_client.audited_account,
|
|
22
25
|
is_cross_account_allowed=False,
|
|
26
|
+
trusted_account_ids=trusted_account_ids,
|
|
23
27
|
):
|
|
24
28
|
report.status = "FAIL"
|
|
25
29
|
report.status_extended = (
|
|
@@ -6,6 +6,7 @@ from prowler.providers.aws.services.iam.lib.policy import is_policy_public
|
|
|
6
6
|
class eventbridge_schema_registry_cross_account_access(Check):
|
|
7
7
|
def execute(self):
|
|
8
8
|
findings = []
|
|
9
|
+
trusted_account_ids = schema_client.audit_config.get("trusted_account_ids", [])
|
|
9
10
|
for registry in schema_client.registries.values():
|
|
10
11
|
if registry.policy is None:
|
|
11
12
|
continue
|
|
@@ -16,6 +17,7 @@ class eventbridge_schema_registry_cross_account_access(Check):
|
|
|
16
17
|
registry.policy,
|
|
17
18
|
schema_client.audited_account,
|
|
18
19
|
is_cross_account_allowed=False,
|
|
20
|
+
trusted_account_ids=trusted_account_ids,
|
|
19
21
|
):
|
|
20
22
|
report.status = "FAIL"
|
|
21
23
|
report.status_extended = f"EventBridge schema registry {registry.name} allows cross-account access."
|
|
@@ -387,6 +387,7 @@ def is_policy_public(
|
|
|
387
387
|
is_cross_account_allowed=True,
|
|
388
388
|
not_allowed_actions: list = [],
|
|
389
389
|
check_cross_service_confused_deputy=False,
|
|
390
|
+
trusted_account_ids: list = None,
|
|
390
391
|
) -> bool:
|
|
391
392
|
"""
|
|
392
393
|
Check if the policy allows public access to the resource.
|
|
@@ -397,10 +398,19 @@ def is_policy_public(
|
|
|
397
398
|
is_cross_account_allowed (bool): If the policy can allow cross-account access, default: True (https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html#cross-service-confused-deputy-prevention)
|
|
398
399
|
not_allowed_actions (list): List of actions that are not allowed, default: []. If not_allowed_actions is empty, the function will not consider the actions in the policy.
|
|
399
400
|
check_cross_service_confused_deputy (bool): If the policy is checked for cross-service confused deputy, default: False
|
|
401
|
+
trusted_account_ids (list): A list of trusted accound ids to reduce false positives on cross-account checks
|
|
400
402
|
Returns:
|
|
401
403
|
bool: True if the policy allows public access, False otherwise
|
|
402
404
|
"""
|
|
403
405
|
is_public = False
|
|
406
|
+
|
|
407
|
+
if trusted_account_ids is None:
|
|
408
|
+
trusted_account_ids = []
|
|
409
|
+
|
|
410
|
+
trusted_accounts = set(trusted_account_ids)
|
|
411
|
+
if source_account:
|
|
412
|
+
trusted_accounts.add(source_account)
|
|
413
|
+
|
|
404
414
|
if policy:
|
|
405
415
|
for statement in policy.get("Statement", []):
|
|
406
416
|
# Only check allow statements
|
|
@@ -414,13 +424,19 @@ def is_policy_public(
|
|
|
414
424
|
isinstance(principal.get("AWS"), str)
|
|
415
425
|
and source_account
|
|
416
426
|
and not is_cross_account_allowed
|
|
417
|
-
and
|
|
427
|
+
and not any(
|
|
428
|
+
trusted_account in principal.get("AWS", "")
|
|
429
|
+
for trusted_account in trusted_accounts
|
|
430
|
+
)
|
|
418
431
|
) or (
|
|
419
432
|
isinstance(principal.get("AWS"), list)
|
|
420
433
|
and source_account
|
|
421
434
|
and not is_cross_account_allowed
|
|
422
|
-
and not
|
|
423
|
-
|
|
435
|
+
and not all(
|
|
436
|
+
any(
|
|
437
|
+
trusted_account in principal_aws
|
|
438
|
+
for trusted_account in trusted_accounts
|
|
439
|
+
)
|
|
424
440
|
for principal_aws in principal["AWS"]
|
|
425
441
|
)
|
|
426
442
|
):
|
|
File without changes
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Provider": "aws",
|
|
3
|
+
"CheckID": "rds_instance_extended_support",
|
|
4
|
+
"CheckTitle": "RDS instance is not enrolled in RDS Extended Support",
|
|
5
|
+
"CheckType": [
|
|
6
|
+
"Software and Configuration Checks/Patch Management",
|
|
7
|
+
"Software and Configuration Checks/AWS Security Best Practices"
|
|
8
|
+
],
|
|
9
|
+
"ServiceName": "rds",
|
|
10
|
+
"SubServiceName": "",
|
|
11
|
+
"ResourceIdTemplate": "",
|
|
12
|
+
"Severity": "medium",
|
|
13
|
+
"ResourceType": "AwsRdsDbInstance",
|
|
14
|
+
"ResourceGroup": "database",
|
|
15
|
+
"Description": "**RDS DB instances** are evaluated for enrollment in Amazon RDS Extended Support. The check fails if `EngineLifecycleSupportis` set to `open-source-rds-extended-support`, indicating the instance will incur additional charges after standard support ends.",
|
|
16
|
+
"Risk": "DB instances enrolled in RDS Extended Support can incur additional charges after the end of standard support for the running database major version. Remaining on older major versions can also delay necessary upgrades, increasing operational and security risk.",
|
|
17
|
+
"RelatedUrl": "",
|
|
18
|
+
"AdditionalURLs": [
|
|
19
|
+
"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/extended-support-viewing.html",
|
|
20
|
+
"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/extended-support-charges.html",
|
|
21
|
+
"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/extended-support-creating-db-instance.html"
|
|
22
|
+
],
|
|
23
|
+
"Remediation": {
|
|
24
|
+
"Code": {
|
|
25
|
+
"CLI": "aws rds modify-db-instance --db-instance-identifier <DB_INSTANCE_IDENTIFIER> --engine-version <TARGET_ENGINE_VERSION> --allow-major-version-upgrade --apply-immediately\n# For new DB instances created via automation, prevent enrollment by setting the lifecycle option:\naws rds create-db-instance ... --engine-lifecycle-support open-source-rds-extended-support-disabled",
|
|
26
|
+
"NativeIaC": "```yaml\n# CloudFormation: upgrade RDS engine version for an existing instance\nResources:\n <example_resource_name>:\n Type: AWS::RDS::DBInstance\n Properties:\n DBInstanceIdentifier: <example_resource_id>\n Engine: <engine>\n DBInstanceClass: db.t3.micro\n EngineVersion: <SUPPORTED_ENGINE_VERSION> # CRITICAL: move to a supported engine version\n AllowMajorVersionUpgrade: true # CRITICAL: required if upgrading major version\n ApplyImmediately: true # CRITICAL: apply change now to pass the check\n```",
|
|
27
|
+
"Other": "If your automation (CloudFormation/Terraform/SDK) creates or restores DB instances, set EngineLifecycleSupport/LifeCycleSupport to open-source-rds-extended-support-disabled where supported, and ensure your upgrade process keeps engines within standard support.",
|
|
28
|
+
"Terraform": "```hcl\n# Upgrade RDS engine version\nresource \"aws_db_instance\" \"<example_resource_name>\" {\n identifier = \"<example_resource_id>\"\n engine = \"<engine>\"\n instance_class = \"db.t3.micro\"\n allocated_storage = 20\n\n engine_version = \"<SUPPORTED_ENGINE_VERSION>\" # CRITICAL: use a supported version\n allow_major_version_upgrade = true # CRITICAL: needed for major upgrades\n apply_immediately = true # CRITICAL: apply now to pass the check\n}\n```"
|
|
29
|
+
},
|
|
30
|
+
"Recommendation": {
|
|
31
|
+
"Text": "Upgrade enrolled DB instances to an engine version covered under standard support to stop Extended Support charges. For new DB instances and restores created via automation, explicitly set the engine lifecycle support option to avoid unintended enrollment in RDS Extended Support when that is your policy.",
|
|
32
|
+
"Url": "https://hub.prowler.com/check/rds_instance_extended_support"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"Categories": [
|
|
36
|
+
"vulnerabilities"
|
|
37
|
+
],
|
|
38
|
+
"DependsOn": [],
|
|
39
|
+
"RelatedTo": [],
|
|
40
|
+
"Notes": ""
|
|
41
|
+
}
|
prowler/providers/aws/services/rds/rds_instance_extended_support/rds_instance_extended_support.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Prowler check: rds_instance_extended_support
|
|
3
|
+
|
|
4
|
+
This check fails when an RDS DB instance is enrolled in Amazon RDS Extended Support.
|
|
5
|
+
Enrollment is exposed via the "EngineLifecycleSupport" attribute returned by DescribeDBInstances.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from prowler.lib.check.models import Check, Check_Report_AWS
|
|
9
|
+
from prowler.providers.aws.services.rds.rds_client import rds_client
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class rds_instance_extended_support(Check):
|
|
13
|
+
def execute(self):
|
|
14
|
+
findings = []
|
|
15
|
+
|
|
16
|
+
for db_instance in rds_client.db_instances.values():
|
|
17
|
+
report = Check_Report_AWS(metadata=self.metadata(), resource=db_instance)
|
|
18
|
+
|
|
19
|
+
# EngineLifecycleSupport can be absent when Extended Support is not applicable.
|
|
20
|
+
lifecycle_support = getattr(db_instance, "engine_lifecycle_support", None)
|
|
21
|
+
|
|
22
|
+
if lifecycle_support == "open-source-rds-extended-support":
|
|
23
|
+
report.status = "FAIL"
|
|
24
|
+
report.status_extended = (
|
|
25
|
+
f"RDS instance {db_instance.id} ({db_instance.engine} {db_instance.engine_version}) "
|
|
26
|
+
f"is enrolled in RDS Extended Support (EngineLifecycleSupport={lifecycle_support})."
|
|
27
|
+
)
|
|
28
|
+
else:
|
|
29
|
+
report.status = "PASS"
|
|
30
|
+
report.status_extended = (
|
|
31
|
+
f"RDS instance {db_instance.id} ({db_instance.engine} {db_instance.engine_version}) "
|
|
32
|
+
"is not enrolled in RDS Extended Support."
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
findings.append(report)
|
|
36
|
+
|
|
37
|
+
return findings
|
|
@@ -59,6 +59,9 @@ class RDS(AWSService):
|
|
|
59
59
|
endpoint=instance.get("Endpoint", {}),
|
|
60
60
|
engine=instance["Engine"],
|
|
61
61
|
engine_version=instance["EngineVersion"],
|
|
62
|
+
engine_lifecycle_support=instance.get(
|
|
63
|
+
"EngineLifecycleSupport"
|
|
64
|
+
),
|
|
62
65
|
status=instance["DBInstanceStatus"],
|
|
63
66
|
public=instance.get("PubliclyAccessible", False),
|
|
64
67
|
encrypted=instance["StorageEncrypted"],
|
|
@@ -531,6 +534,7 @@ class DBInstance(BaseModel):
|
|
|
531
534
|
endpoint: dict
|
|
532
535
|
engine: str
|
|
533
536
|
engine_version: str
|
|
537
|
+
engine_lifecycle_support: Optional[str] = None
|
|
534
538
|
status: str
|
|
535
539
|
public: bool
|
|
536
540
|
encrypted: bool
|
prowler/providers/aws/services/s3/s3_bucket_cross_account_access/s3_bucket_cross_account_access.py
CHANGED
|
@@ -6,6 +6,7 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
|
|
|
6
6
|
class s3_bucket_cross_account_access(Check):
|
|
7
7
|
def execute(self):
|
|
8
8
|
findings = []
|
|
9
|
+
trusted_account_ids = s3_client.audit_config.get("trusted_account_ids", [])
|
|
9
10
|
for bucket in s3_client.buckets.values():
|
|
10
11
|
if bucket.policy is None:
|
|
11
12
|
continue
|
|
@@ -19,7 +20,10 @@ class s3_bucket_cross_account_access(Check):
|
|
|
19
20
|
f"S3 Bucket {bucket.name} does not have a bucket policy."
|
|
20
21
|
)
|
|
21
22
|
elif is_policy_public(
|
|
22
|
-
bucket.policy,
|
|
23
|
+
bucket.policy,
|
|
24
|
+
s3_client.audited_account,
|
|
25
|
+
is_cross_account_allowed=False,
|
|
26
|
+
trusted_account_ids=trusted_account_ids,
|
|
23
27
|
):
|
|
24
28
|
report.status = "FAIL"
|
|
25
29
|
report.status_extended = f"S3 Bucket {bucket.name} has a bucket policy allowing cross account access."
|