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.
Files changed (219) hide show
  1. dashboard/compliance/hipaa_azure.py +25 -0
  2. dashboard/pages/overview.py +20 -11
  3. prowler/AGENTS.md +1 -1
  4. prowler/CHANGELOG.md +43 -0
  5. prowler/__main__.py +5 -0
  6. prowler/compliance/azure/hipaa_azure.json +820 -0
  7. prowler/compliance/m365/cis_4.0_m365.json +6 -2
  8. prowler/compliance/m365/cis_6.0_m365.json +6 -2
  9. prowler/compliance/m365/iso27001_2022_m365.json +13 -11
  10. prowler/compliance/openstack/__init__.py +0 -0
  11. prowler/config/config.py +2 -1
  12. prowler/config/config.yaml +4 -1
  13. prowler/config/openstack_mutelist_example.yaml +60 -0
  14. prowler/lib/check/check.py +4 -0
  15. prowler/lib/check/models.py +27 -2
  16. prowler/lib/cli/parser.py +3 -2
  17. prowler/lib/outputs/finding.py +14 -0
  18. prowler/lib/outputs/html/html.py +72 -0
  19. prowler/lib/outputs/jira/jira.py +3 -3
  20. prowler/lib/outputs/outputs.py +2 -0
  21. prowler/lib/outputs/summary_table.py +7 -0
  22. prowler/lib/timeline/__init__.py +0 -0
  23. prowler/lib/timeline/models.py +27 -0
  24. prowler/lib/timeline/timeline.py +36 -0
  25. prowler/providers/aws/lib/cloudtrail_timeline/__init__.py +0 -0
  26. prowler/providers/aws/lib/cloudtrail_timeline/cloudtrail_timeline.py +218 -0
  27. prowler/providers/aws/services/codebuild/codebuild_project_webhook_filters_use_anchored_patterns/__init__.py +0 -0
  28. prowler/providers/aws/services/codebuild/codebuild_project_webhook_filters_use_anchored_patterns/codebuild_project_webhook_filters_use_anchored_patterns.metadata.json +40 -0
  29. prowler/providers/aws/services/codebuild/codebuild_project_webhook_filters_use_anchored_patterns/codebuild_project_webhook_filters_use_anchored_patterns.py +58 -0
  30. prowler/providers/aws/services/codebuild/codebuild_service.py +45 -0
  31. prowler/providers/aws/services/dynamodb/dynamodb_table_cross_account_access/dynamodb_table_cross_account_access.metadata.json +1 -1
  32. prowler/providers/aws/services/dynamodb/dynamodb_table_cross_account_access/dynamodb_table_cross_account_access.py +4 -0
  33. prowler/providers/aws/services/eventbridge/eventbridge_bus_cross_account_access/eventbridge_bus_cross_account_access.metadata.json +1 -1
  34. prowler/providers/aws/services/eventbridge/eventbridge_bus_cross_account_access/eventbridge_bus_cross_account_access.py +4 -0
  35. prowler/providers/aws/services/eventbridge/eventbridge_schema_registry_cross_account_access/eventbridge_schema_registry_cross_account_access.metadata.json +1 -1
  36. prowler/providers/aws/services/eventbridge/eventbridge_schema_registry_cross_account_access/eventbridge_schema_registry_cross_account_access.py +2 -0
  37. prowler/providers/aws/services/iam/lib/policy.py +19 -3
  38. prowler/providers/aws/services/rds/rds_instance_extended_support/__init__.py +0 -0
  39. prowler/providers/aws/services/rds/rds_instance_extended_support/rds_instance_extended_support.metadata.json +41 -0
  40. prowler/providers/aws/services/rds/rds_instance_extended_support/rds_instance_extended_support.py +37 -0
  41. prowler/providers/aws/services/rds/rds_service.py +4 -0
  42. prowler/providers/aws/services/s3/s3_bucket_cross_account_access/s3_bucket_cross_account_access.metadata.json +1 -1
  43. prowler/providers/aws/services/s3/s3_bucket_cross_account_access/s3_bucket_cross_account_access.py +5 -1
  44. prowler/providers/azure/lib/service/service.py +23 -0
  45. prowler/providers/azure/services/app/app_client_certificates_on/app_client_certificates_on.metadata.json +18 -12
  46. prowler/providers/azure/services/app/app_ensure_auth_is_set_up/app_ensure_auth_is_set_up.metadata.json +18 -11
  47. prowler/providers/azure/services/app/app_ensure_http_is_redirected_to_https/app_ensure_http_is_redirected_to_https.metadata.json +19 -12
  48. prowler/providers/azure/services/app/app_ensure_java_version_is_latest/app_ensure_java_version_is_latest.metadata.json +19 -12
  49. prowler/providers/azure/services/app/app_ensure_php_version_is_latest/app_ensure_php_version_is_latest.metadata.json +19 -12
  50. prowler/providers/azure/services/app/app_ensure_python_version_is_latest/app_ensure_python_version_is_latest.metadata.json +19 -12
  51. prowler/providers/azure/services/app/app_ensure_using_http20/app_ensure_using_http20.metadata.json +18 -11
  52. prowler/providers/azure/services/app/app_ftp_deployment_disabled/app_ftp_deployment_disabled.metadata.json +21 -13
  53. prowler/providers/azure/services/app/app_function_access_keys_configured/app_function_access_keys_configured.metadata.json +19 -11
  54. prowler/providers/azure/services/app/app_function_application_insights_enabled/app_function_application_insights_enabled.metadata.json +21 -14
  55. prowler/providers/azure/services/app/app_function_ftps_deployment_disabled/app_function_ftps_deployment_disabled.metadata.json +18 -13
  56. prowler/providers/azure/services/app/app_function_identity_is_configured/app_function_identity_is_configured.metadata.json +20 -13
  57. prowler/providers/azure/services/app/app_function_identity_without_admin_privileges/app_function_identity_without_admin_privileges.metadata.json +18 -11
  58. prowler/providers/azure/services/app/app_function_latest_runtime_version/app_function_latest_runtime_version.metadata.json +20 -13
  59. prowler/providers/azure/services/app/app_function_not_publicly_accessible/app_function_not_publicly_accessible.metadata.json +20 -13
  60. prowler/providers/azure/services/app/app_function_vnet_integration_enabled/app_function_vnet_integration_enabled.metadata.json +21 -14
  61. prowler/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled.metadata.json +18 -12
  62. prowler/providers/azure/services/app/app_minimum_tls_version_12/app_minimum_tls_version_12.metadata.json +20 -12
  63. prowler/providers/azure/services/app/app_register_with_identity/app_register_with_identity.metadata.json +18 -11
  64. prowler/providers/azure/services/appinsights/appinsights_ensure_is_configured/appinsights_ensure_is_configured.metadata.json +18 -12
  65. prowler/providers/azure/services/containerregistry/containerregistry_admin_user_disabled/containerregistry_admin_user_disabled.metadata.json +17 -11
  66. prowler/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible.metadata.json +18 -12
  67. prowler/providers/azure/services/containerregistry/containerregistry_uses_private_link/containerregistry_uses_private_link.metadata.json +21 -13
  68. prowler/providers/azure/services/cosmosdb/cosmosdb_account_firewall_use_selected_networks/cosmosdb_account_firewall_use_selected_networks.metadata.json +20 -12
  69. prowler/providers/azure/services/cosmosdb/cosmosdb_account_use_aad_and_rbac/cosmosdb_account_use_aad_and_rbac.metadata.json +19 -13
  70. prowler/providers/azure/services/cosmosdb/cosmosdb_account_use_private_endpoints/cosmosdb_account_use_private_endpoints.metadata.json +20 -13
  71. prowler/providers/azure/services/databricks/databricks_workspace_cmk_encryption_enabled/databricks_workspace_cmk_encryption_enabled.metadata.json +20 -14
  72. prowler/providers/azure/services/databricks/databricks_workspace_vnet_injection_enabled/databricks_workspace_vnet_injection_enabled.metadata.json +20 -14
  73. 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
  74. prowler/providers/azure/services/defender/defender_assessments_vm_endpoint_protection_installed/defender_assessments_vm_endpoint_protection_installed.metadata.json +17 -11
  75. prowler/providers/azure/services/defender/defender_attack_path_notifications_properly_configured/defender_attack_path_notifications_properly_configured.metadata.json +19 -13
  76. 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
  77. prowler/providers/azure/services/defender/defender_auto_provisioning_vulnerabilty_assessments_machines_on/defender_auto_provisioning_vulnerabilty_assessments_machines_on.metadata.json +19 -12
  78. prowler/providers/azure/services/defender/defender_container_images_resolved_vulnerabilities/defender_container_images_resolved_vulnerabilities.metadata.json +20 -12
  79. prowler/providers/azure/services/defender/defender_container_images_scan_enabled/defender_container_images_scan_enabled.metadata.json +22 -13
  80. 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
  81. prowler/providers/azure/services/defender/defender_ensure_defender_for_arm_is_on/defender_ensure_defender_for_arm_is_on.metadata.json +17 -11
  82. 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
  83. prowler/providers/azure/services/defender/defender_ensure_defender_for_containers_is_on/defender_ensure_defender_for_containers_is_on.metadata.json +17 -11
  84. prowler/providers/azure/services/defender/defender_ensure_defender_for_cosmosdb_is_on/defender_ensure_defender_for_cosmosdb_is_on.metadata.json +17 -11
  85. prowler/providers/azure/services/defender/defender_ensure_defender_for_databases_is_on/defender_ensure_defender_for_databases_is_on.metadata.json +17 -11
  86. prowler/providers/azure/services/defender/defender_ensure_defender_for_dns_is_on/defender_ensure_defender_for_dns_is_on.metadata.json +17 -11
  87. prowler/providers/azure/services/defender/defender_ensure_defender_for_keyvault_is_on/defender_ensure_defender_for_keyvault_is_on.metadata.json +17 -11
  88. 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
  89. prowler/providers/azure/services/defender/defender_ensure_defender_for_server_is_on/defender_ensure_defender_for_server_is_on.metadata.json +19 -11
  90. 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
  91. prowler/providers/azure/services/defender/defender_ensure_defender_for_storage_is_on/defender_ensure_defender_for_storage_is_on.metadata.json +17 -11
  92. prowler/providers/azure/services/defender/defender_ensure_iot_hub_defender_is_on/defender_ensure_iot_hub_defender_is_on.metadata.json +17 -11
  93. prowler/providers/azure/services/defender/defender_ensure_mcas_is_enabled/defender_ensure_mcas_is_enabled.metadata.json +20 -12
  94. prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high.metadata.json +19 -12
  95. prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners.metadata.json +19 -12
  96. prowler/providers/azure/services/defender/defender_ensure_system_updates_are_applied/defender_ensure_system_updates_are_applied.metadata.json +17 -9
  97. prowler/providers/azure/services/defender/defender_ensure_wdatp_is_enabled/defender_ensure_wdatp_is_enabled.metadata.json +21 -13
  98. prowler/providers/azure/services/entra/entra_service.py +3 -11
  99. prowler/providers/azure/services/entra/entra_user_with_vm_access_has_mfa/entra_user_with_vm_access_has_mfa.py +6 -0
  100. 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
  101. prowler/providers/azure/services/iam/iam_role_user_access_admin_restricted/iam_role_user_access_admin_restricted.metadata.json +16 -10
  102. prowler/providers/azure/services/iam/iam_subscription_roles_owner_custom_not_created/iam_subscription_roles_owner_custom_not_created.metadata.json +18 -12
  103. prowler/providers/azure/services/keyvault/keyvault_rbac_secret_expiration_set/keyvault_rbac_secret_expiration_set.py +10 -11
  104. prowler/providers/azure/services/keyvault/keyvault_service.py +164 -81
  105. prowler/providers/azure/services/mysql/mysql_flexible_server_audit_log_connection_activated/mysql_flexible_server_audit_log_connection_activated.metadata.json +18 -12
  106. prowler/providers/azure/services/mysql/mysql_flexible_server_audit_log_enabled/mysql_flexible_server_audit_log_enabled.metadata.json +19 -12
  107. prowler/providers/azure/services/mysql/mysql_flexible_server_minimum_tls_version_12/mysql_flexible_server_minimum_tls_version_12.metadata.json +18 -12
  108. prowler/providers/azure/services/mysql/mysql_flexible_server_ssl_connection_enabled/mysql_flexible_server_ssl_connection_enabled.metadata.json +19 -12
  109. prowler/providers/azure/services/network/network_bastion_host_exists/network_bastion_host_exists.metadata.json +21 -12
  110. prowler/providers/azure/services/network/network_flow_log_captured_sent/network_flow_log_captured_sent.metadata.json +19 -12
  111. prowler/providers/azure/services/network/network_flow_log_more_than_90_days/network_flow_log_more_than_90_days.metadata.json +21 -12
  112. prowler/providers/azure/services/network/network_http_internet_access_restricted/network_http_internet_access_restricted.metadata.json +18 -12
  113. prowler/providers/azure/services/network/network_public_ip_shodan/network_public_ip_shodan.metadata.json +15 -10
  114. prowler/providers/azure/services/network/network_rdp_internet_access_restricted/network_rdp_internet_access_restricted.metadata.json +20 -12
  115. prowler/providers/azure/services/network/network_ssh_internet_access_restricted/network_ssh_internet_access_restricted.metadata.json +19 -12
  116. prowler/providers/azure/services/network/network_udp_internet_access_restricted/network_udp_internet_access_restricted.metadata.json +19 -12
  117. prowler/providers/azure/services/network/network_watcher_enabled/network_watcher_enabled.metadata.json +21 -13
  118. prowler/providers/azure/services/policy/policy_ensure_asc_enforcement_enabled/policy_ensure_asc_enforcement_enabled.metadata.json +16 -11
  119. prowler/providers/azure/services/postgresql/postgresql_flexible_server_allow_access_services_disabled/postgresql_flexible_server_allow_access_services_disabled.metadata.json +20 -13
  120. prowler/providers/azure/services/postgresql/postgresql_flexible_server_connection_throttling_on/postgresql_flexible_server_connection_throttling_on.metadata.json +18 -12
  121. prowler/providers/azure/services/postgresql/postgresql_flexible_server_enforce_ssl_enabled/postgresql_flexible_server_enforce_ssl_enabled.metadata.json +19 -13
  122. prowler/providers/azure/services/postgresql/postgresql_flexible_server_entra_id_authentication_enabled/postgresql_flexible_server_entra_id_authentication_enabled.metadata.json +4 -4
  123. prowler/providers/azure/services/postgresql/postgresql_flexible_server_log_checkpoints_on/postgresql_flexible_server_log_checkpoints_on.metadata.json +19 -13
  124. prowler/providers/azure/services/postgresql/postgresql_flexible_server_log_connections_on/postgresql_flexible_server_log_connections_on.metadata.json +18 -11
  125. prowler/providers/azure/services/postgresql/postgresql_flexible_server_log_disconnections_on/postgresql_flexible_server_log_disconnections_on.metadata.json +18 -12
  126. 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
  127. prowler/providers/azure/services/sqlserver/sqlserver_auditing_enabled/sqlserver_auditing_enabled.metadata.json +20 -13
  128. prowler/providers/azure/services/sqlserver/sqlserver_auditing_retention_90_days/sqlserver_auditing_retention_90_days.metadata.json +20 -12
  129. prowler/providers/azure/services/sqlserver/sqlserver_azuread_administrator_enabled/sqlserver_azuread_administrator_enabled.metadata.json +18 -12
  130. prowler/providers/azure/services/sqlserver/sqlserver_microsoft_defender_enabled/sqlserver_microsoft_defender_enabled.metadata.json +23 -13
  131. prowler/providers/azure/services/sqlserver/sqlserver_recommended_minimal_tls_version/sqlserver_recommended_minimal_tls_version.metadata.json +19 -12
  132. prowler/providers/azure/services/sqlserver/sqlserver_tde_encrypted_with_cmk/sqlserver_tde_encrypted_with_cmk.metadata.json +20 -13
  133. prowler/providers/azure/services/sqlserver/sqlserver_tde_encryption_enabled/sqlserver_tde_encryption_enabled.metadata.json +20 -13
  134. prowler/providers/azure/services/sqlserver/sqlserver_unrestricted_inbound_access/sqlserver_unrestricted_inbound_access.metadata.json +18 -12
  135. prowler/providers/azure/services/sqlserver/sqlserver_va_emails_notifications_admins_enabled/sqlserver_va_emails_notifications_admins_enabled.metadata.json +19 -12
  136. prowler/providers/azure/services/sqlserver/sqlserver_va_periodic_recurring_scans_enabled/sqlserver_va_periodic_recurring_scans_enabled.metadata.json +19 -12
  137. prowler/providers/azure/services/sqlserver/sqlserver_va_scan_reports_configured/sqlserver_va_scan_reports_configured.metadata.json +18 -12
  138. prowler/providers/azure/services/sqlserver/sqlserver_vulnerability_assessment_enabled/sqlserver_vulnerability_assessment_enabled.metadata.json +19 -12
  139. prowler/providers/azure/services/storage/storage_account_key_access_disabled/storage_account_key_access_disabled.metadata.json +17 -12
  140. prowler/providers/azure/services/storage/storage_blob_public_access_level_is_disabled/storage_blob_public_access_level_is_disabled.metadata.json +18 -12
  141. prowler/providers/azure/services/storage/storage_blob_versioning_is_enabled/storage_blob_versioning_is_enabled.metadata.json +19 -11
  142. prowler/providers/azure/services/storage/storage_cross_tenant_replication_disabled/storage_cross_tenant_replication_disabled.metadata.json +19 -13
  143. prowler/providers/azure/services/storage/storage_default_network_access_rule_is_denied/storage_default_network_access_rule_is_denied.metadata.json +19 -12
  144. prowler/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/storage_default_to_entra_authorization_enabled.metadata.json +20 -13
  145. 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
  146. prowler/providers/azure/services/storage/storage_ensure_encryption_with_customer_managed_keys/storage_ensure_encryption_with_customer_managed_keys.metadata.json +15 -10
  147. 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
  148. prowler/providers/azure/services/storage/storage_ensure_minimum_tls_version_12/storage_ensure_minimum_tls_version_12.metadata.json +14 -10
  149. prowler/providers/azure/services/storage/storage_ensure_private_endpoints_in_storage_accounts/storage_ensure_private_endpoints_in_storage_accounts.metadata.json +19 -11
  150. prowler/providers/azure/services/storage/storage_ensure_soft_delete_is_enabled/storage_ensure_soft_delete_is_enabled.metadata.json +17 -12
  151. prowler/providers/azure/services/storage/storage_geo_redundant_enabled/storage_geo_redundant_enabled.metadata.json +19 -12
  152. prowler/providers/azure/services/storage/storage_infrastructure_encryption_is_enabled/storage_infrastructure_encryption_is_enabled.metadata.json +13 -9
  153. prowler/providers/azure/services/storage/storage_key_rotation_90_days/storage_key_rotation_90_days.metadata.json +17 -12
  154. prowler/providers/azure/services/storage/storage_secure_transfer_required_is_enabled/storage_secure_transfer_required_is_enabled.metadata.json +15 -11
  155. prowler/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm.metadata.json +19 -12
  156. prowler/providers/azure/services/storage/storage_smb_protocol_version_is_latest/storage_smb_protocol_version_is_latest.metadata.json +19 -13
  157. prowler/providers/cloudflare/cloudflare_provider.py +95 -12
  158. prowler/providers/cloudflare/lib/arguments/arguments.py +7 -0
  159. prowler/providers/cloudflare/services/dns/dns_record_cname_target_valid/__init__.py +0 -0
  160. prowler/providers/cloudflare/services/dns/dns_record_cname_target_valid/dns_record_cname_target_valid.metadata.json +36 -0
  161. prowler/providers/cloudflare/services/dns/dns_record_cname_target_valid/dns_record_cname_target_valid.py +109 -0
  162. prowler/providers/cloudflare/services/dns/dns_record_no_internal_ip/__init__.py +0 -0
  163. prowler/providers/cloudflare/services/dns/dns_record_no_internal_ip/dns_record_no_internal_ip.metadata.json +36 -0
  164. prowler/providers/cloudflare/services/dns/dns_record_no_internal_ip/dns_record_no_internal_ip.py +73 -0
  165. prowler/providers/cloudflare/services/dns/dns_record_no_wildcard/__init__.py +0 -0
  166. prowler/providers/cloudflare/services/dns/dns_record_no_wildcard/dns_record_no_wildcard.metadata.json +36 -0
  167. prowler/providers/cloudflare/services/dns/dns_record_no_wildcard/dns_record_no_wildcard.py +60 -0
  168. prowler/providers/cloudflare/services/dns/dns_record_proxied/__init__.py +0 -0
  169. prowler/providers/cloudflare/services/dns/dns_record_proxied/dns_record_proxied.metadata.json +36 -0
  170. prowler/providers/cloudflare/services/dns/dns_record_proxied/dns_record_proxied.py +49 -0
  171. prowler/providers/cloudflare/services/dns/dns_service.py +52 -6
  172. prowler/providers/cloudflare/services/firewall/__init__.py +0 -0
  173. prowler/providers/cloudflare/services/firewall/firewall_client.py +4 -0
  174. prowler/providers/cloudflare/services/firewall/firewall_service.py +123 -0
  175. prowler/providers/cloudflare/services/zone/zone_firewall_blocking_rules_configured/__init__.py +0 -0
  176. prowler/providers/cloudflare/services/zone/zone_firewall_blocking_rules_configured/zone_firewall_blocking_rules_configured.metadata.json +36 -0
  177. prowler/providers/cloudflare/services/zone/zone_firewall_blocking_rules_configured/zone_firewall_blocking_rules_configured.py +53 -0
  178. prowler/providers/cloudflare/services/zone/zone_service.py +133 -1
  179. prowler/providers/cloudflare/services/zone/zone_waf_owasp_ruleset_enabled/__init__.py +0 -0
  180. prowler/providers/cloudflare/services/zone/zone_waf_owasp_ruleset_enabled/zone_waf_owasp_ruleset_enabled.metadata.json +36 -0
  181. prowler/providers/cloudflare/services/zone/zone_waf_owasp_ruleset_enabled/zone_waf_owasp_ruleset_enabled.py +58 -0
  182. prowler/providers/common/provider.py +23 -0
  183. prowler/providers/gcp/services/compute/compute_instance_suspended_without_persistent_disks/__init__.py +0 -0
  184. prowler/providers/gcp/services/compute/compute_instance_suspended_without_persistent_disks/compute_instance_suspended_without_persistent_disks.metadata.json +37 -0
  185. prowler/providers/gcp/services/compute/compute_instance_suspended_without_persistent_disks/compute_instance_suspended_without_persistent_disks.py +35 -0
  186. prowler/providers/gcp/services/compute/compute_service.py +2 -0
  187. prowler/providers/m365/lib/powershell/m365_powershell.py +47 -1
  188. prowler/providers/m365/services/defender/defender_service.py +52 -0
  189. prowler/providers/m365/services/defender/defender_zap_for_teams_enabled/__init__.py +0 -0
  190. prowler/providers/m365/services/defender/defender_zap_for_teams_enabled/defender_zap_for_teams_enabled.metadata.json +38 -0
  191. prowler/providers/m365/services/defender/defender_zap_for_teams_enabled/defender_zap_for_teams_enabled.py +53 -0
  192. prowler/providers/m365/services/exchange/exchange_service.py +78 -0
  193. prowler/providers/m365/services/exchange/exchange_shared_mailbox_sign_in_disabled/__init__.py +0 -0
  194. prowler/providers/m365/services/exchange/exchange_shared_mailbox_sign_in_disabled/exchange_shared_mailbox_sign_in_disabled.metadata.json +37 -0
  195. prowler/providers/m365/services/exchange/exchange_shared_mailbox_sign_in_disabled/exchange_shared_mailbox_sign_in_disabled.py +59 -0
  196. prowler/providers/openstack/__init__.py +0 -0
  197. prowler/providers/openstack/exceptions/__init__.py +0 -0
  198. prowler/providers/openstack/exceptions/exceptions.py +166 -0
  199. prowler/providers/openstack/lib/__init__.py +0 -0
  200. prowler/providers/openstack/lib/arguments/__init__.py +0 -0
  201. prowler/providers/openstack/lib/arguments/arguments.py +113 -0
  202. prowler/providers/openstack/lib/mutelist/__init__.py +0 -0
  203. prowler/providers/openstack/lib/mutelist/mutelist.py +31 -0
  204. prowler/providers/openstack/lib/service/__init__.py +0 -0
  205. prowler/providers/openstack/lib/service/service.py +21 -0
  206. prowler/providers/openstack/models.py +100 -0
  207. prowler/providers/openstack/openstack_provider.py +515 -0
  208. prowler/providers/openstack/services/__init__.py +0 -0
  209. prowler/providers/openstack/services/compute/__init__.py +0 -0
  210. prowler/providers/openstack/services/compute/compute_client.py +4 -0
  211. prowler/providers/openstack/services/compute/compute_instance_security_groups_attached/__init__.py +0 -0
  212. prowler/providers/openstack/services/compute/compute_instance_security_groups_attached/compute_instance_security_groups_attached.metadata.json +40 -0
  213. prowler/providers/openstack/services/compute/compute_instance_security_groups_attached/compute_instance_security_groups_attached.py +35 -0
  214. prowler/providers/openstack/services/compute/compute_service.py +63 -0
  215. {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/METADATA +11 -9
  216. {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/RECORD +219 -155
  217. {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/LICENSE +0 -0
  218. {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/WHEEL +0 -0
  219. {prowler-5.17.1.dist-info → prowler-5.18.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,515 @@
1
+ from os import environ
2
+ from pathlib import Path
3
+ from typing import Optional
4
+
5
+ from colorama import Fore, Style
6
+ from openstack import config, connect
7
+ from openstack import exceptions as openstack_exceptions
8
+ from openstack.connection import Connection as OpenStackConnection
9
+
10
+ from prowler.config.config import (
11
+ default_config_file_path,
12
+ get_default_mute_file_path,
13
+ load_and_validate_config_file,
14
+ )
15
+ from prowler.lib.logger import logger
16
+ from prowler.lib.utils.utils import print_boxes
17
+ from prowler.providers.common.models import Audit_Metadata, Connection
18
+ from prowler.providers.common.provider import Provider
19
+ from prowler.providers.openstack.exceptions.exceptions import (
20
+ OpenStackAuthenticationError,
21
+ OpenStackCloudNotFoundError,
22
+ OpenStackConfigFileNotFoundError,
23
+ OpenStackCredentialsError,
24
+ OpenStackInvalidConfigError,
25
+ OpenStackSessionError,
26
+ )
27
+ from prowler.providers.openstack.lib.mutelist.mutelist import OpenStackMutelist
28
+ from prowler.providers.openstack.models import OpenStackIdentityInfo, OpenStackSession
29
+
30
+
31
+ class OpenstackProvider(Provider):
32
+ """OpenStack provider responsible for bootstrapping the SDK session."""
33
+
34
+ _type: str = "openstack"
35
+ _session: OpenStackSession
36
+ _identity: OpenStackIdentityInfo
37
+ _audit_config: dict
38
+ _mutelist: OpenStackMutelist
39
+ _connection: OpenStackConnection
40
+ audit_metadata: Audit_Metadata
41
+
42
+ REQUIRED_ENVIRONMENT_VARIABLES = [
43
+ "OS_AUTH_URL",
44
+ "OS_USERNAME",
45
+ "OS_PASSWORD",
46
+ "OS_REGION_NAME",
47
+ ]
48
+
49
+ def __init__(
50
+ self,
51
+ clouds_yaml_file: Optional[str] = None,
52
+ clouds_yaml_cloud: Optional[str] = None,
53
+ auth_url: Optional[str] = None,
54
+ identity_api_version: Optional[str] = None,
55
+ username: Optional[str] = None,
56
+ password: Optional[str] = None,
57
+ project_id: Optional[str] = None,
58
+ region_name: Optional[str] = None,
59
+ user_domain_name: Optional[str] = None,
60
+ project_domain_name: Optional[str] = None,
61
+ config_path: Optional[str] = None,
62
+ config_content: Optional[dict] = None,
63
+ fixer_config: Optional[dict] = None,
64
+ mutelist_path: Optional[str] = None,
65
+ mutelist_content: Optional[dict] = None,
66
+ ) -> None:
67
+ logger.info("Instantiating OpenStack Provider...")
68
+
69
+ self._session = self.setup_session(
70
+ clouds_yaml_file=clouds_yaml_file,
71
+ clouds_yaml_cloud=clouds_yaml_cloud,
72
+ auth_url=auth_url,
73
+ identity_api_version=identity_api_version,
74
+ username=username,
75
+ password=password,
76
+ project_id=project_id,
77
+ region_name=region_name,
78
+ user_domain_name=user_domain_name,
79
+ project_domain_name=project_domain_name,
80
+ )
81
+ self._connection = OpenstackProvider._create_connection(self._session)
82
+ self._identity = OpenstackProvider.setup_identity(
83
+ self._connection, self._session
84
+ )
85
+
86
+ if config_content:
87
+ self._audit_config = config_content
88
+ else:
89
+ if not config_path:
90
+ config_path = default_config_file_path
91
+ self._audit_config = load_and_validate_config_file(self._type, config_path)
92
+
93
+ self._fixer_config = fixer_config or {}
94
+
95
+ if mutelist_content:
96
+ self._mutelist = OpenStackMutelist(mutelist_content=mutelist_content)
97
+ else:
98
+ if not mutelist_path:
99
+ mutelist_path = get_default_mute_file_path(self.type)
100
+ self._mutelist = OpenStackMutelist(mutelist_path=mutelist_path)
101
+
102
+ Provider.set_global_provider(self)
103
+
104
+ @property
105
+ def type(self) -> str:
106
+ return self._type
107
+
108
+ @property
109
+ def session(self) -> OpenStackSession:
110
+ return self._session
111
+
112
+ @property
113
+ def identity(self) -> OpenStackIdentityInfo:
114
+ return self._identity
115
+
116
+ @property
117
+ def audit_config(self) -> dict:
118
+ return self._audit_config
119
+
120
+ @property
121
+ def fixer_config(self) -> dict:
122
+ return self._fixer_config
123
+
124
+ @property
125
+ def mutelist(self) -> OpenStackMutelist:
126
+ return self._mutelist
127
+
128
+ @property
129
+ def connection(self) -> OpenStackConnection:
130
+ return self._connection
131
+
132
+ @staticmethod
133
+ def setup_session(
134
+ clouds_yaml_file: Optional[str] = None,
135
+ clouds_yaml_cloud: Optional[str] = None,
136
+ auth_url: Optional[str] = None,
137
+ identity_api_version: Optional[str] = None,
138
+ username: Optional[str] = None,
139
+ password: Optional[str] = None,
140
+ project_id: Optional[str] = None,
141
+ region_name: Optional[str] = None,
142
+ user_domain_name: Optional[str] = None,
143
+ project_domain_name: Optional[str] = None,
144
+ ) -> OpenStackSession:
145
+ """Collect authentication information from clouds.yaml, explicit parameters, or environment variables.
146
+
147
+ Authentication priority:
148
+ 1. clouds.yaml file (if clouds_yaml_file or clouds_yaml_cloud provided)
149
+ 2. Explicit parameters + environment variable fallback
150
+ """
151
+ # Priority 1: clouds.yaml authentication
152
+ if clouds_yaml_file or clouds_yaml_cloud:
153
+ logger.info("Using clouds.yaml configuration for authentication")
154
+ return OpenstackProvider._setup_session_from_clouds_yaml(
155
+ clouds_yaml_file=clouds_yaml_file,
156
+ clouds_yaml_cloud=clouds_yaml_cloud,
157
+ )
158
+
159
+ # Priority 2: Explicit parameters + environment variable fallback (existing behavior)
160
+ provided_overrides = {
161
+ "OS_AUTH_URL": auth_url,
162
+ "OS_USERNAME": username,
163
+ "OS_PASSWORD": password,
164
+ "OS_REGION_NAME": region_name,
165
+ }
166
+ missing_variables = [
167
+ env_var
168
+ for env_var in OpenstackProvider.REQUIRED_ENVIRONMENT_VARIABLES
169
+ if not (provided_overrides.get(env_var) or environ.get(env_var))
170
+ ]
171
+
172
+ # Resolve project_id from parameters or environment
173
+ resolved_project_id = project_id or environ.get("OS_PROJECT_ID")
174
+
175
+ # OS_PROJECT_ID is mandatory
176
+ if not resolved_project_id:
177
+ missing_variables.append("OS_PROJECT_ID")
178
+
179
+ if missing_variables:
180
+ pretty_missing = ", ".join(missing_variables)
181
+ raise OpenStackCredentialsError(
182
+ message=f"Missing mandatory OpenStack environment variables: {pretty_missing}"
183
+ )
184
+
185
+ resolved_identity_api_version = (
186
+ identity_api_version or environ.get("OS_IDENTITY_API_VERSION") or "3"
187
+ )
188
+ resolved_user_domain = (
189
+ user_domain_name or environ.get("OS_USER_DOMAIN_NAME") or "Default"
190
+ )
191
+ resolved_project_domain = (
192
+ project_domain_name or environ.get("OS_PROJECT_DOMAIN_NAME") or "Default"
193
+ )
194
+
195
+ return OpenStackSession(
196
+ auth_url=auth_url or environ.get("OS_AUTH_URL"),
197
+ identity_api_version=resolved_identity_api_version,
198
+ username=username or environ.get("OS_USERNAME"),
199
+ password=password or environ.get("OS_PASSWORD"),
200
+ project_id=resolved_project_id,
201
+ region_name=region_name or environ.get("OS_REGION_NAME"),
202
+ user_domain_name=resolved_user_domain,
203
+ project_domain_name=resolved_project_domain,
204
+ )
205
+
206
+ @staticmethod
207
+ def _setup_session_from_clouds_yaml(
208
+ clouds_yaml_file: Optional[str] = None,
209
+ clouds_yaml_cloud: Optional[str] = None,
210
+ ) -> OpenStackSession:
211
+ """Setup session from clouds.yaml configuration file.
212
+
213
+ Args:
214
+ clouds_yaml_file: Path to clouds.yaml file. If None, standard locations are searched.
215
+ clouds_yaml_cloud: Cloud name to use from clouds.yaml. Required when using clouds.yaml.
216
+
217
+ Returns:
218
+ OpenStackSession configured from clouds.yaml
219
+
220
+ Raises:
221
+ OpenStackConfigFileNotFoundError: If clouds.yaml file not found
222
+ OpenStackCloudNotFoundError: If specified cloud not found in clouds.yaml
223
+ OpenStackInvalidConfigError: If clouds.yaml is malformed or missing required fields
224
+ """
225
+ try:
226
+ # Cloud name is required when using clouds.yaml
227
+ if not clouds_yaml_cloud:
228
+ raise OpenStackInvalidConfigError(
229
+ file=clouds_yaml_file,
230
+ message="Cloud name (--clouds-yaml-cloud) is required when using clouds.yaml file",
231
+ )
232
+
233
+ # Determine config file path
234
+ if clouds_yaml_file:
235
+ # Use explicit path
236
+ config_path = Path(clouds_yaml_file).expanduser()
237
+ if not config_path.exists():
238
+ raise OpenStackConfigFileNotFoundError(
239
+ file=str(config_path),
240
+ message=f"clouds.yaml file not found at {config_path}",
241
+ )
242
+ logger.info(f"Loading clouds.yaml from {config_path}")
243
+ # Load OpenStack configuration with explicit file
244
+ os_config = config.OpenStackConfig(config_files=[str(config_path)])
245
+ else:
246
+ # Search standard locations if cloud name is provided
247
+ logger.info(
248
+ "Searching for clouds.yaml in standard locations: "
249
+ "~/.config/openstack/clouds.yaml, /etc/openstack/clouds.yaml, ./clouds.yaml"
250
+ )
251
+ # Load OpenStack configuration from standard locations (don't pass config_files)
252
+ os_config = config.OpenStackConfig()
253
+
254
+ # Get cloud configuration
255
+ logger.info(f"Loading cloud configuration for '{clouds_yaml_cloud}'")
256
+
257
+ try:
258
+ cloud_config = os_config.get_one(cloud=clouds_yaml_cloud)
259
+ except openstack_exceptions.OpenStackCloudException as error:
260
+ if "cloud" in str(error).lower() and "not found" in str(error).lower():
261
+ raise OpenStackCloudNotFoundError(
262
+ file=clouds_yaml_file,
263
+ original_exception=error,
264
+ message=f"Cloud '{clouds_yaml_cloud}' not found in clouds.yaml configuration",
265
+ )
266
+ raise OpenStackInvalidConfigError(
267
+ file=clouds_yaml_file,
268
+ original_exception=error,
269
+ message=f"Failed to load cloud configuration: {error}",
270
+ )
271
+
272
+ # Extract authentication parameters from cloud config
273
+ auth_dict = cloud_config.config.get("auth", {})
274
+
275
+ # Validate required fields
276
+ required_fields = ["auth_url", "username", "password"]
277
+ missing_fields = [
278
+ field for field in required_fields if not auth_dict.get(field)
279
+ ]
280
+ if missing_fields:
281
+ raise OpenStackInvalidConfigError(
282
+ file=clouds_yaml_file,
283
+ message=f"Missing required fields in clouds.yaml for cloud '{clouds_yaml_cloud}': {', '.join(missing_fields)}",
284
+ )
285
+
286
+ # Build OpenStackSession from cloud config
287
+ return OpenStackSession(
288
+ auth_url=auth_dict.get("auth_url"),
289
+ identity_api_version=str(
290
+ cloud_config.config.get("identity_api_version", "3")
291
+ ),
292
+ username=auth_dict.get("username"),
293
+ password=auth_dict.get("password"),
294
+ project_id=auth_dict.get("project_id") or auth_dict.get("project_name"),
295
+ region_name=cloud_config.config.get("region_name"),
296
+ user_domain_name=auth_dict.get("user_domain_name", "Default"),
297
+ project_domain_name=auth_dict.get("project_domain_name", "Default"),
298
+ )
299
+
300
+ except (
301
+ OpenStackConfigFileNotFoundError,
302
+ OpenStackCloudNotFoundError,
303
+ OpenStackInvalidConfigError,
304
+ ):
305
+ # Re-raise our custom exceptions
306
+ raise
307
+ except Exception as error:
308
+ logger.critical(
309
+ f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- "
310
+ f"Failed to load clouds.yaml configuration: {error}"
311
+ )
312
+ raise OpenStackInvalidConfigError(
313
+ file=clouds_yaml_file,
314
+ original_exception=error,
315
+ message=f"Failed to load clouds.yaml configuration: {error}",
316
+ )
317
+
318
+ @staticmethod
319
+ def _create_connection(
320
+ session: OpenStackSession,
321
+ ) -> OpenStackConnection:
322
+ """Initialize the OpenStack SDK connection.
323
+
324
+ Note: We explicitly disable loading configuration from clouds.yaml
325
+ and environment variables to ensure Prowler uses only the credentials
326
+ provided through its own configuration mechanisms (CLI args, config file,
327
+ or environment variables read by Prowler itself in setup_session()).
328
+ """
329
+ try:
330
+ # Don't load from clouds.yaml or environment variables, we configure this in setup_session()
331
+ conn = connect(
332
+ load_yaml_config=False,
333
+ load_envvars=False,
334
+ **session.as_sdk_config(),
335
+ )
336
+ conn.authorize()
337
+ return conn
338
+ except openstack_exceptions.SDKException as error:
339
+ logger.critical(
340
+ f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- "
341
+ f"Failed to create OpenStack connection: {error}"
342
+ )
343
+ raise OpenStackAuthenticationError(
344
+ original_exception=error,
345
+ message=f"Failed to create OpenStack connection: {error}",
346
+ )
347
+ except Exception as error:
348
+ logger.critical(
349
+ f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- "
350
+ f"Unexpected error while creating OpenStack connection: {error}"
351
+ )
352
+ raise OpenStackSessionError(
353
+ original_exception=error,
354
+ message=f"Unexpected error while creating OpenStack connection: {error}",
355
+ )
356
+
357
+ @staticmethod
358
+ def setup_identity(
359
+ conn: OpenStackConnection, session: OpenStackSession
360
+ ) -> OpenStackIdentityInfo:
361
+ """Build identity information for CLI/logging purposes."""
362
+ user_name = session.username
363
+ project_name = None
364
+ user_id = None
365
+ project_id = session.project_id
366
+ try:
367
+ user_id = conn.current_user_id
368
+ if user_id:
369
+ user = conn.identity.get_user(user_id)
370
+ if user and getattr(user, "name", None):
371
+ user_name = user.name
372
+
373
+ project_identifier = conn.current_project_id or session.project_id
374
+ if project_identifier:
375
+ project = conn.identity.get_project(project_identifier)
376
+ if project:
377
+ project_name = getattr(project, "name", None)
378
+ project_id = project_identifier
379
+ except openstack_exceptions.SDKException as error:
380
+ logger.warning(f"Unable to enrich OpenStack identity information: {error}")
381
+ except Exception as error:
382
+ logger.warning(f"Unexpected error building OpenStack identity: {error}")
383
+
384
+ return OpenStackIdentityInfo(
385
+ user_id=user_id,
386
+ username=user_name,
387
+ project_id=project_id,
388
+ project_name=project_name,
389
+ region_name=session.region_name,
390
+ user_domain_name=session.user_domain_name,
391
+ project_domain_name=session.project_domain_name,
392
+ )
393
+
394
+ @staticmethod
395
+ def test_connection(
396
+ clouds_yaml_file: Optional[str] = None,
397
+ clouds_yaml_cloud: Optional[str] = None,
398
+ auth_url: Optional[str] = None,
399
+ identity_api_version: Optional[str] = None,
400
+ username: Optional[str] = None,
401
+ password: Optional[str] = None,
402
+ project_id: Optional[str] = None,
403
+ region_name: Optional[str] = None,
404
+ user_domain_name: Optional[str] = None,
405
+ project_domain_name: Optional[str] = None,
406
+ raise_on_exception: bool = True,
407
+ ) -> Connection:
408
+ """Test connection to OpenStack without creating a full provider instance.
409
+
410
+ This static method allows testing OpenStack credentials without initializing
411
+ the entire provider. Useful for API validation before storing credentials.
412
+
413
+ Args:
414
+ clouds_yaml_file: Path to clouds.yaml configuration file
415
+ clouds_yaml_cloud: Cloud name from clouds.yaml to use
416
+ auth_url: OpenStack Keystone authentication URL
417
+ identity_api_version: Keystone API version (default: "3")
418
+ username: OpenStack username
419
+ password: OpenStack password
420
+ project_id: OpenStack project identifier (can be UUID or string ID)
421
+ region_name: OpenStack region name
422
+ user_domain_name: User domain name (default: "Default")
423
+ project_domain_name: Project domain name (default: "Default")
424
+ raise_on_exception: Whether to raise exception on failure (default: True)
425
+
426
+ Returns:
427
+ Connection object with is_connected=True on success, or error on failure
428
+
429
+ Raises:
430
+ OpenStackCredentialsError: If raise_on_exception=True and credentials are invalid
431
+ OpenStackAuthenticationError: If raise_on_exception=True and authentication fails
432
+ OpenStackSessionError: If raise_on_exception=True and connection fails
433
+ OpenStackConfigFileNotFoundError: If raise_on_exception=True and clouds.yaml not found
434
+ OpenStackCloudNotFoundError: If raise_on_exception=True and cloud not in clouds.yaml
435
+ OpenStackInvalidConfigError: If raise_on_exception=True and clouds.yaml is malformed
436
+
437
+ Examples:
438
+ >>> # Test with explicit credentials
439
+ >>> OpenstackProvider.test_connection(
440
+ ... auth_url="https://openstack.example.com:5000/v3",
441
+ ... username="admin",
442
+ ... password="secret",
443
+ ... project_id="my-project-id",
444
+ ... region_name="RegionOne"
445
+ ... )
446
+ Connection(is_connected=True, error=None)
447
+
448
+ >>> # Test with clouds.yaml
449
+ >>> OpenstackProvider.test_connection(
450
+ ... clouds_yaml_file="~/.config/openstack/clouds.yaml",
451
+ ... clouds_yaml_cloud="production"
452
+ ... )
453
+ Connection(is_connected=True, error=None)
454
+ """
455
+ try:
456
+ # Setup session with provided credentials
457
+ session = OpenstackProvider.setup_session(
458
+ clouds_yaml_file=clouds_yaml_file,
459
+ clouds_yaml_cloud=clouds_yaml_cloud,
460
+ auth_url=auth_url,
461
+ identity_api_version=identity_api_version,
462
+ username=username,
463
+ password=password,
464
+ project_id=project_id,
465
+ region_name=region_name,
466
+ user_domain_name=user_domain_name,
467
+ project_domain_name=project_domain_name,
468
+ )
469
+
470
+ # Create and test connection
471
+ OpenstackProvider._create_connection(session)
472
+
473
+ logger.info("OpenStack provider: Connection test successful")
474
+ return Connection(is_connected=True)
475
+
476
+ except (
477
+ OpenStackCredentialsError,
478
+ OpenStackAuthenticationError,
479
+ OpenStackSessionError,
480
+ OpenStackConfigFileNotFoundError,
481
+ OpenStackCloudNotFoundError,
482
+ OpenStackInvalidConfigError,
483
+ ) as error:
484
+ logger.error(f"OpenStack connection test failed: {error}")
485
+ if raise_on_exception:
486
+ raise
487
+ return Connection(is_connected=False, error=error)
488
+ except Exception as error:
489
+ logger.error(
490
+ f"OpenStack connection test failed with unexpected error: {error}"
491
+ )
492
+ if raise_on_exception:
493
+ raise OpenStackSessionError(
494
+ original_exception=error,
495
+ message=f"Unexpected error during connection test: {error}",
496
+ )
497
+ return Connection(is_connected=False, error=error)
498
+
499
+ def print_credentials(self) -> None:
500
+ """Output sanitized credential summary."""
501
+ region = self._session.region_name
502
+ auth_url = self._session.auth_url
503
+ project_id = self._session.project_id
504
+ username = self._identity.username
505
+ messages = [
506
+ f"Auth URL: {auth_url}",
507
+ f"Project ID: {project_id}",
508
+ f"Username: {username}",
509
+ f"Region: {region}",
510
+ ]
511
+ print_boxes(messages, "OpenStack Credentials")
512
+ logger.info(
513
+ f"Using OpenStack endpoint {Fore.YELLOW}{auth_url}{Style.RESET_ALL} "
514
+ f"in region {Fore.YELLOW}{region}{Style.RESET_ALL}"
515
+ )
File without changes
@@ -0,0 +1,4 @@
1
+ from prowler.providers.common.provider import Provider
2
+ from prowler.providers.openstack.services.compute.compute_service import Compute
3
+
4
+ compute_client = Compute(Provider.get_global_provider())
@@ -0,0 +1,40 @@
1
+ {
2
+ "Provider": "openstack",
3
+ "CheckID": "compute_instance_security_groups_attached",
4
+ "CheckTitle": "Compute instances have security groups attached",
5
+ "CheckType": [],
6
+ "ServiceName": "compute",
7
+ "SubServiceName": "",
8
+ "ResourceIdTemplate": "",
9
+ "Severity": "high",
10
+ "ResourceType": "OpenStackInstance",
11
+ "ResourceGroup": "compute",
12
+ "Description": "**OpenStack compute instances** (VMs) are evaluated to verify that at least one **security group** is attached. Security groups act as virtual firewalls, controlling ingress and egress traffic. Instances without security groups may have **unrestricted network access**, violating defense-in-depth principles.",
13
+ "Risk": "Compute instances without security groups are exposed to unrestricted network traffic, enabling:\n- **Confidentiality**: unauthorized access to services and data exfiltration\n- **Integrity**: injection attacks, tampering, and lateral movement across workloads\n- **Availability**: DDoS, port scanning, and resource exhaustion\nAttackers can probe open ports, exploit vulnerable services, and establish persistence without firewall barriers.",
14
+ "RelatedUrl": "",
15
+ "AdditionalURLs": [
16
+ "https://docs.openstack.org/nova/latest/user/security-groups.html",
17
+ "https://docs.openstack.org/api-ref/compute/",
18
+ "https://docs.openstack.org/neutron/latest/admin/intro-os-networking.html",
19
+ "https://docs.openstack.org/security-guide/networking/architecture.html"
20
+ ],
21
+ "Remediation": {
22
+ "Code": {
23
+ "CLI": "openstack server add security group <instance_id> <security_group_name>",
24
+ "NativeIaC": "",
25
+ "Other": "1. Access the Horizon dashboard\n2. Navigate to **Compute > Instances**\n3. Locate instances without security groups (check the Security Groups column)\n4. Select the instance, then **Actions > Edit Security Groups**\n5. Add at least one security group (e.g., `default` or a custom group with least-privilege rules)\n6. Click **Save** and verify the attachment\n7. Test connectivity to ensure proper ingress/egress rules",
26
+ "Terraform": "```hcl\n# Terraform: ensure compute instance has security groups attached\nresource \"openstack_compute_instance_v2\" \"instance\" {\n name = \"example-instance\"\n image_id = var.image_id\n flavor_id = var.flavor_id\n key_pair = var.key_pair_name\n # Critical: attach at least one security group\n security_groups = [\"default\", \"web-sg\", \"app-sg\"]\n\n network {\n name = var.network_name\n }\n}\n```"
27
+ },
28
+ "Recommendation": {
29
+ "Text": "Apply **defense-in-depth** for network security:\n- Attach **security groups** to all instances; never deploy without firewall rules\n- Follow **least privilege**: allow only required ports and sources; deny all by default\n- Use **separate security groups** per tier (web, app, database) with explicit rules\n- Avoid overly permissive rules like `0.0.0.0/0` ingress on sensitive ports\n- Implement **network segmentation** with isolated networks or VLANs\n- Enable **flow logs** for traffic analysis and anomaly detection\n- Regularly **audit** security group rules and remove stale or overly broad permissions",
30
+ "Url": "https://hub.prowler.com/check/compute_instance_security_groups_attached"
31
+ }
32
+ },
33
+ "Categories": [
34
+ "internet-exposed",
35
+ "trust-boundaries"
36
+ ],
37
+ "DependsOn": [],
38
+ "RelatedTo": [],
39
+ "Notes": ""
40
+ }
@@ -0,0 +1,35 @@
1
+ from typing import List
2
+
3
+ from prowler.lib.check.models import Check, CheckReportOpenStack
4
+ from prowler.providers.openstack.services.compute.compute_client import compute_client
5
+
6
+
7
+ class compute_instance_security_groups_attached(Check):
8
+ """Ensure compute instances have security groups attached."""
9
+
10
+ def execute(self) -> List[CheckReportOpenStack]:
11
+ findings: List[CheckReportOpenStack] = []
12
+
13
+ for instance in compute_client.instances:
14
+ report = CheckReportOpenStack(metadata=self.metadata(), resource=instance)
15
+ report.resource_id = instance.id
16
+ report.resource_name = instance.name
17
+ report.region = instance.region
18
+
19
+ if instance.security_groups and len(instance.security_groups) > 0:
20
+ report.status = "PASS"
21
+ sg_names = ", ".join(instance.security_groups)
22
+ report.status_extended = (
23
+ f"Instance {instance.name} ({instance.id}) has security groups "
24
+ f"attached: {sg_names}."
25
+ )
26
+ else:
27
+ report.status = "FAIL"
28
+ report.status_extended = (
29
+ f"Instance {instance.name} ({instance.id}) does not have any "
30
+ f"security groups attached."
31
+ )
32
+
33
+ findings.append(report)
34
+
35
+ return findings
@@ -0,0 +1,63 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import List
5
+
6
+ from openstack import exceptions as openstack_exceptions
7
+
8
+ from prowler.lib.logger import logger
9
+ from prowler.providers.openstack.lib.service.service import OpenStackService
10
+
11
+
12
+ class Compute(OpenStackService):
13
+ """Service wrapper using openstacksdk compute APIs."""
14
+
15
+ def __init__(self, provider) -> None:
16
+ super().__init__(__class__.__name__, provider)
17
+ self.client = self.connection.compute
18
+ self.instances: List[ComputeInstance] = []
19
+ self._list_instances()
20
+
21
+ def _list_instances(self) -> None:
22
+ """List all compute instances in the current project."""
23
+ logger.info("Compute - Listing instances...")
24
+ try:
25
+ for server in self.client.servers():
26
+ # Extract security group names (handle None case)
27
+ sg_list = getattr(server, "security_groups", None) or []
28
+ security_groups = [sg.get("name", "") for sg in sg_list]
29
+
30
+ self.instances.append(
31
+ ComputeInstance(
32
+ id=getattr(server, "id", ""),
33
+ name=getattr(server, "name", ""),
34
+ status=getattr(server, "status", ""),
35
+ flavor_id=getattr(server, "flavor", {}).get("id", ""),
36
+ security_groups=security_groups,
37
+ region=self.region,
38
+ project_id=self.project_id,
39
+ )
40
+ )
41
+ except openstack_exceptions.SDKException as error:
42
+ logger.error(
43
+ f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- "
44
+ f"Failed to list compute instances: {error}"
45
+ )
46
+ except Exception as error:
47
+ logger.error(
48
+ f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- "
49
+ f"Unexpected error listing compute instances: {error}"
50
+ )
51
+
52
+
53
+ @dataclass
54
+ class ComputeInstance:
55
+ """Represents an OpenStack compute instance (VM)."""
56
+
57
+ id: str
58
+ name: str
59
+ status: str
60
+ flavor_id: str
61
+ security_groups: List[str]
62
+ region: str
63
+ project_id: str