diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index c6a5adc6cd..00de06d8ad 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -18,6 +18,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - Order requirements by ID in Prowler ThreatScore AWS compliance framework [(#8495)](https://github.com/prowler-cloud/prowler/pull/8495) - Add explicit resource name to GCP and Azure Defender checks [(#8352)](https://github.com/prowler-cloud/prowler/pull/8352) - Validation errors in Azure and M365 providers [(#8353)](https://github.com/prowler-cloud/prowler/pull/8353) +- Azure `storage_geo_redundant_enabled` check false positives [(#8504)](https://github.com/prowler-cloud/prowler/pull/8504) --- diff --git a/prowler/providers/azure/services/storage/storage_geo_redundant_enabled/storage_geo_redundant_enabled.py b/prowler/providers/azure/services/storage/storage_geo_redundant_enabled/storage_geo_redundant_enabled.py index 971b440b0e..ee2d49e53d 100644 --- a/prowler/providers/azure/services/storage/storage_geo_redundant_enabled/storage_geo_redundant_enabled.py +++ b/prowler/providers/azure/services/storage/storage_geo_redundant_enabled/storage_geo_redundant_enabled.py @@ -1,6 +1,5 @@ from prowler.lib.check.models import Check, Check_Report_Azure from prowler.providers.azure.services.storage.storage_client import storage_client -from prowler.providers.azure.services.storage.storage_service import ReplicationSettings class storage_geo_redundant_enabled(Check): @@ -27,14 +26,16 @@ class storage_geo_redundant_enabled(Check): report.subscription = subscription if ( - storage_account.replication_settings - == ReplicationSettings.STANDARD_GRS + storage_account.replication_settings == "Standard_GRS" + or storage_account.replication_settings == "Standard_GZRS" + or storage_account.replication_settings == "Standard_RAGRS" + or storage_account.replication_settings == "Standard_RAGZRS" ): report.status = "PASS" - report.status_extended = f"Storage account {storage_account.name} from subscription {subscription} has Geo-redundant storage (GRS) enabled." + report.status_extended = f"Storage account {storage_account.name} from subscription {subscription} has Geo-redundant storage {storage_account.replication_settings} enabled." else: report.status = "FAIL" - report.status_extended = f"Storage account {storage_account.name} from subscription {subscription} does not have Geo-redundant storage (GRS) enabled." + report.status_extended = f"Storage account {storage_account.name} from subscription {subscription} does not have Geo-redundant storage enabled, it has {storage_account.replication_settings} instead." findings.append(report) diff --git a/prowler/providers/azure/services/storage/storage_service.py b/prowler/providers/azure/services/storage/storage_service.py index e93b920380..cd5cd07d98 100644 --- a/prowler/providers/azure/services/storage/storage_service.py +++ b/prowler/providers/azure/services/storage/storage_service.py @@ -1,4 +1,3 @@ -from enum import Enum from typing import Optional from azure.mgmt.storage import StorageManagementClient @@ -35,7 +34,6 @@ class Storage(AzureService): key_expiration_period_in_days = int( storage_account.key_policy.key_expiration_period_in_days ) - replication_settings = ReplicationSettings(storage_account.sku.name) storage_accounts[subscription].append( Account( id=storage_account.id, @@ -84,7 +82,7 @@ class Storage(AzureService): False, ) ), - replication_settings=replication_settings, + replication_settings=storage_account.sku.name, allow_cross_tenant_replication=( True if getattr( @@ -273,17 +271,6 @@ class PrivateEndpointConnection(BaseModel): type: str -class ReplicationSettings(Enum): - STANDARD_LRS = "Standard_LRS" - STANDARD_GRS = "Standard_GRS" - STANDARD_RAGRS = "Standard_RAGRS" - STANDARD_ZRS = "Standard_ZRS" - PREMIUM_LRS = "Premium_LRS" - PREMIUM_ZRS = "Premium_ZRS" - STANDARD_GZRS = "Standard_GZRS" - STANDARD_RAGZRS = "Standard_RAGZRS" - - class SMBProtocolSettings(BaseModel): channel_encryption: list[str] supported_versions: list[str] @@ -310,7 +297,7 @@ class Account(BaseModel): minimum_tls_version: str private_endpoint_connections: list[PrivateEndpointConnection] key_expiration_period_in_days: Optional[int] = None - replication_settings: ReplicationSettings = ReplicationSettings.STANDARD_LRS + replication_settings: str = "Standard_LRS" allow_cross_tenant_replication: bool = True allow_shared_key_access: bool = True blob_properties: Optional[BlobProperties] = None diff --git a/tests/providers/azure/services/storage/storage_geo_redundant_enabled/storage_geo_redundant_enabled_test.py b/tests/providers/azure/services/storage/storage_geo_redundant_enabled/storage_geo_redundant_enabled_test.py index 7ae8dc32eb..cfe2f5a00b 100644 --- a/tests/providers/azure/services/storage/storage_geo_redundant_enabled/storage_geo_redundant_enabled_test.py +++ b/tests/providers/azure/services/storage/storage_geo_redundant_enabled/storage_geo_redundant_enabled_test.py @@ -4,7 +4,6 @@ from uuid import uuid4 from prowler.providers.azure.services.storage.storage_service import ( Account, NetworkRuleSet, - ReplicationSettings, ) from tests.providers.azure.azure_fixtures import ( AZURE_SUBSCRIPTION_ID, @@ -35,10 +34,11 @@ class Test_storage_geo_redundant_enabled: result = check.execute() assert len(result) == 0 - def test_storage_geo_redundant_enabled(self): + def test_storage_account_standard_grs_enabled(self): storage_account_id = str(uuid4()) storage_account_name = "Test Storage Account GRS" storage_client = mock.MagicMock() + replication_setting = "Standard_GRS" storage_client.storage_accounts = { AZURE_SUBSCRIPTION_ID: [ Account( @@ -56,7 +56,7 @@ class Test_storage_geo_redundant_enabled: private_endpoint_connections=[], key_expiration_period_in_days=None, location="westeurope", - replication_settings=ReplicationSettings.STANDARD_GRS, + replication_settings=replication_setting, ) ] } @@ -81,17 +81,18 @@ class Test_storage_geo_redundant_enabled: assert result[0].status == "PASS" assert ( result[0].status_extended - == f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_ID} has Geo-redundant storage (GRS) enabled." + == f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_ID} has Geo-redundant storage {replication_setting} enabled." ) assert result[0].subscription == AZURE_SUBSCRIPTION_ID assert result[0].resource_name == storage_account_name assert result[0].resource_id == storage_account_id assert result[0].location == "westeurope" - def test_storage_account_geo_redundant_disabled(self): + def test_storage_account_standard_ragrs_enabled(self): storage_account_id = str(uuid4()) - storage_account_name = "Test Storage Account LRS" + storage_account_name = "Test Storage Account RAGRS" storage_client = mock.MagicMock() + replication_setting = "Standard_RAGRS" storage_client.storage_accounts = { AZURE_SUBSCRIPTION_ID: [ Account( @@ -109,7 +110,169 @@ class Test_storage_geo_redundant_enabled: private_endpoint_connections=[], key_expiration_period_in_days=None, location="westeurope", - replication_settings=ReplicationSettings.STANDARD_LRS, + replication_settings=replication_setting, + ) + ] + } + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.storage.storage_geo_redundant_enabled.storage_geo_redundant_enabled.storage_client", + new=storage_client, + ), + ): + from prowler.providers.azure.services.storage.storage_geo_redundant_enabled.storage_geo_redundant_enabled import ( + storage_geo_redundant_enabled, + ) + + check = storage_geo_redundant_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_ID} has Geo-redundant storage {replication_setting} enabled." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == storage_account_name + assert result[0].resource_id == storage_account_id + assert result[0].location == "westeurope" + + def test_storage_account_standard_gzrs_enabled(self): + storage_account_id = str(uuid4()) + storage_account_name = "Test Storage Account GZRS" + storage_client = mock.MagicMock() + replication_setting = "Standard_GZRS" + storage_client.storage_accounts = { + AZURE_SUBSCRIPTION_ID: [ + Account( + id=storage_account_id, + name=storage_account_name, + resouce_group_name="rg", + enable_https_traffic_only=False, + infrastructure_encryption=False, + allow_blob_public_access=False, + network_rule_set=NetworkRuleSet( + bypass="AzureServices", default_action="Allow" + ), + encryption_type="None", + minimum_tls_version="TLS1_2", + private_endpoint_connections=[], + key_expiration_period_in_days=None, + location="westeurope", + replication_settings=replication_setting, + ) + ] + } + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.storage.storage_geo_redundant_enabled.storage_geo_redundant_enabled.storage_client", + new=storage_client, + ), + ): + from prowler.providers.azure.services.storage.storage_geo_redundant_enabled.storage_geo_redundant_enabled import ( + storage_geo_redundant_enabled, + ) + + check = storage_geo_redundant_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_ID} has Geo-redundant storage {replication_setting} enabled." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == storage_account_name + assert result[0].resource_id == storage_account_id + assert result[0].location == "westeurope" + + def test_storage_account_standard_ragzrs_enabled(self): + storage_account_id = str(uuid4()) + storage_account_name = "Test Storage Account RAGZRS" + storage_client = mock.MagicMock() + replication_setting = "Standard_RAGZRS" + storage_client.storage_accounts = { + AZURE_SUBSCRIPTION_ID: [ + Account( + id=storage_account_id, + name=storage_account_name, + resouce_group_name="rg", + enable_https_traffic_only=False, + infrastructure_encryption=False, + allow_blob_public_access=False, + network_rule_set=NetworkRuleSet( + bypass="AzureServices", default_action="Allow" + ), + encryption_type="None", + minimum_tls_version="TLS1_2", + private_endpoint_connections=[], + key_expiration_period_in_days=None, + location="westeurope", + replication_settings=replication_setting, + ) + ] + } + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.storage.storage_geo_redundant_enabled.storage_geo_redundant_enabled.storage_client", + new=storage_client, + ), + ): + from prowler.providers.azure.services.storage.storage_geo_redundant_enabled.storage_geo_redundant_enabled import ( + storage_geo_redundant_enabled, + ) + + check = storage_geo_redundant_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_ID} has Geo-redundant storage {replication_setting} enabled." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == storage_account_name + assert result[0].resource_id == storage_account_id + assert result[0].location == "westeurope" + + def test_storage_account_standard_lrs_disabled(self): + storage_account_id = str(uuid4()) + storage_account_name = "Test Storage Account LRS" + storage_client = mock.MagicMock() + replication_setting = "Standard_LRS" + storage_client.storage_accounts = { + AZURE_SUBSCRIPTION_ID: [ + Account( + id=storage_account_id, + name=storage_account_name, + resouce_group_name="rg", + enable_https_traffic_only=False, + infrastructure_encryption=False, + allow_blob_public_access=False, + network_rule_set=NetworkRuleSet( + bypass="AzureServices", default_action="Allow" + ), + encryption_type="None", + minimum_tls_version="TLS1_2", + private_endpoint_connections=[], + key_expiration_period_in_days=None, + location="westeurope", + replication_settings=replication_setting, ) ] } @@ -134,7 +297,169 @@ class Test_storage_geo_redundant_enabled: assert result[0].status == "FAIL" assert ( result[0].status_extended - == f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_ID} does not have Geo-redundant storage (GRS) enabled." + == f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_ID} does not have Geo-redundant storage enabled, it has {replication_setting} instead." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == storage_account_name + assert result[0].resource_id == storage_account_id + assert result[0].location == "westeurope" + + def test_storage_account_standard_zrs_disabled(self): + storage_account_id = str(uuid4()) + storage_account_name = "Test Storage Account ZRS" + storage_client = mock.MagicMock() + replication_setting = "Standard_ZRS" + storage_client.storage_accounts = { + AZURE_SUBSCRIPTION_ID: [ + Account( + id=storage_account_id, + name=storage_account_name, + resouce_group_name="rg", + enable_https_traffic_only=False, + infrastructure_encryption=False, + allow_blob_public_access=False, + network_rule_set=NetworkRuleSet( + bypass="AzureServices", default_action="Allow" + ), + encryption_type="None", + minimum_tls_version="TLS1_2", + private_endpoint_connections=[], + key_expiration_period_in_days=None, + location="westeurope", + replication_settings=replication_setting, + ) + ] + } + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.storage.storage_geo_redundant_enabled.storage_geo_redundant_enabled.storage_client", + new=storage_client, + ), + ): + from prowler.providers.azure.services.storage.storage_geo_redundant_enabled.storage_geo_redundant_enabled import ( + storage_geo_redundant_enabled, + ) + + check = storage_geo_redundant_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_ID} does not have Geo-redundant storage enabled, it has {replication_setting} instead." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == storage_account_name + assert result[0].resource_id == storage_account_id + assert result[0].location == "westeurope" + + def test_storage_account_premium_lrs_disabled(self): + storage_account_id = str(uuid4()) + storage_account_name = "Test Storage Account Premium LRS" + storage_client = mock.MagicMock() + replication_setting = "Premium_LRS" + storage_client.storage_accounts = { + AZURE_SUBSCRIPTION_ID: [ + Account( + id=storage_account_id, + name=storage_account_name, + resouce_group_name="rg", + enable_https_traffic_only=False, + infrastructure_encryption=False, + allow_blob_public_access=False, + network_rule_set=NetworkRuleSet( + bypass="AzureServices", default_action="Allow" + ), + encryption_type="None", + minimum_tls_version="TLS1_2", + private_endpoint_connections=[], + key_expiration_period_in_days=None, + location="westeurope", + replication_settings=replication_setting, + ) + ] + } + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.storage.storage_geo_redundant_enabled.storage_geo_redundant_enabled.storage_client", + new=storage_client, + ), + ): + from prowler.providers.azure.services.storage.storage_geo_redundant_enabled.storage_geo_redundant_enabled import ( + storage_geo_redundant_enabled, + ) + + check = storage_geo_redundant_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_ID} does not have Geo-redundant storage enabled, it has {replication_setting} instead." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == storage_account_name + assert result[0].resource_id == storage_account_id + assert result[0].location == "westeurope" + + def test_storage_account_premium_zrs_disabled(self): + storage_account_id = str(uuid4()) + storage_account_name = "Test Storage Account Premium ZRS" + storage_client = mock.MagicMock() + replication_setting = "Premium_ZRS" + storage_client.storage_accounts = { + AZURE_SUBSCRIPTION_ID: [ + Account( + id=storage_account_id, + name=storage_account_name, + resouce_group_name="rg", + enable_https_traffic_only=False, + infrastructure_encryption=False, + allow_blob_public_access=False, + network_rule_set=NetworkRuleSet( + bypass="AzureServices", default_action="Allow" + ), + encryption_type="None", + minimum_tls_version="TLS1_2", + private_endpoint_connections=[], + key_expiration_period_in_days=None, + location="westeurope", + replication_settings=replication_setting, + ) + ] + } + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.storage.storage_geo_redundant_enabled.storage_geo_redundant_enabled.storage_client", + new=storage_client, + ), + ): + from prowler.providers.azure.services.storage.storage_geo_redundant_enabled.storage_geo_redundant_enabled import ( + storage_geo_redundant_enabled, + ) + + check = storage_geo_redundant_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_ID} does not have Geo-redundant storage enabled, it has {replication_setting} instead." ) assert result[0].subscription == AZURE_SUBSCRIPTION_ID assert result[0].resource_name == storage_account_name diff --git a/tests/providers/azure/services/storage/storage_service_test.py b/tests/providers/azure/services/storage/storage_service_test.py index 6eb21052a2..912403a0d3 100644 --- a/tests/providers/azure/services/storage/storage_service_test.py +++ b/tests/providers/azure/services/storage/storage_service_test.py @@ -6,7 +6,6 @@ from prowler.providers.azure.services.storage.storage_service import ( DeleteRetentionPolicy, FileServiceProperties, NetworkRuleSet, - ReplicationSettings, SMBProtocolSettings, Storage, ) @@ -53,7 +52,7 @@ def mock_storage_get_storage_accounts(_): location="westeurope", blob_properties=blob_properties, default_to_entra_authorization=True, - replication_settings=ReplicationSettings.STANDARD_LRS, + replication_settings="Standard_LRS", allow_cross_tenant_replication=True, allow_shared_key_access=True, file_service_properties=file_service_properties, @@ -150,7 +149,7 @@ class Test_Storage_Service: ].default_to_entra_authorization assert ( storage.storage_accounts[AZURE_SUBSCRIPTION_ID][0].replication_settings - == ReplicationSettings.STANDARD_LRS + == "Standard_LRS" ) assert ( storage.storage_accounts[AZURE_SUBSCRIPTION_ID][