fix(azure): consolidate file share properties to the storage account level (#8087)

(cherry picked from commit e0465f2aa2)

# Conflicts:
#	prowler/CHANGELOG.md
#	prowler/providers/azure/services/storage/storage_ensure_file_shares_soft_delete_is_enabled/storage_ensure_file_shares_soft_delete_is_enabled.py
#	prowler/providers/azure/services/storage/storage_service.py
#	tests/providers/azure/services/storage/storage_ensure_file_shares_soft_delete_is_enabled/storage_ensure_file_shares_soft_delete_is_enabled_test.py
#	tests/providers/azure/services/storage/storage_service_test.py
This commit is contained in:
Rubén De la Torre Vico committed 2025-06-24 09:37:56 +00:00
1 parent 1e2ab0e617
commit 7dd2038cc6
5 files changed
+347

No files matched your search

+18
View File
@@ -27,7 +27,25 @@ All notable changes to the **Prowler SDK** are documented in this file.
- `storage_ensure_file_shares_soft_delete_is_enabled` check for Azure provider [(#7966)](https://github.com/prowler-cloud/prowler/pull/7966)
- Make `validate_mutelist` method static inside `Mutelist` class [(#7811)](https://github.com/prowler-cloud/prowler/pull/7811)
- Avoid bypassing IAM check using wildcards [(#7708)](https://github.com/prowler-cloud/prowler/pull/7708)
<<<<<<< HEAD
- Add new method to authenticate in AppInsights in check `app_function_application_insights_enabled` [(#7763)](https://github.com/prowler-cloud/prowler/pull/7763)
=======
- `storage_blob_versioning_is_enabled` new check for Azure provider [(#7927)](https://github.com/prowler-cloud/prowler/pull/7927)
- New method to authenticate in AppInsights in check `app_function_application_insights_enabled` [(#7763)](https://github.com/prowler-cloud/prowler/pull/7763)
- ISO 27001 2022 for M365 provider. [(#7985)](https://github.com/prowler-cloud/prowler/pull/7985)
- `codebuild_project_uses_allowed_github_organizations` check for AWS provider [(#7595)](https://github.com/prowler-cloud/prowler/pull/7595)
- IaC provider [(#7852)](https://github.com/prowler-cloud/prowler/pull/7852)
- Azure Databricks service integration for Azure provider, including the `databricks_workspace_vnet_injection_enabled` check [(#8008)](https://github.com/prowler-cloud/prowler/pull/8008)
- Azure Databricks check `databricks_workspace_cmk_encryption_enabled` to ensure workspaces use customer-managed keys (CMK) for encryption at rest [(#8017)](https://github.com/prowler-cloud/prowler/pull/8017)
- Add `storage_account_default_to_entra_authorization_enabled` check for Azure provider. [(#7981)](https://github.com/prowler-cloud/prowler/pull/7981)
- Replace `Domain.Read.All` with `Directory.Read.All` in Azure and M365 docs [(#8075)](https://github.com/prowler-cloud/prowler/pull/8075)
### Fixed
- Consolidate Azure Storage file service properties to the account level, improving the accuracy of the `storage_ensure_file_shares_soft_delete_is_enabled` check [(#8087)](https://github.com/prowler-cloud/prowler/pull/8087)
### Removed
- OCSF version number references to point always to the latest [(#8064)](https://github.com/prowler-cloud/prowler/pull/8064)
>>>>>>> e0465f2aa (fix(azure): consolidate file share properties to the storage account level (#8087))
---
## [v5.7.5] (Prowler UNRELEASED)
@@ -0,0 +1,30 @@
from prowler.lib.check.models import Check, Check_Report_Azure
from prowler.providers.azure.services.storage.storage_client import storage_client
class storage_ensure_file_shares_soft_delete_is_enabled(Check):
def execute(self) -> list:
findings = []
for subscription, storage_accounts in storage_client.storage_accounts.items():
for storage_account in storage_accounts:
if getattr(storage_account, "file_service_properties", None):
report = Check_Report_Azure(
metadata=self.metadata(),
resource=storage_account.file_service_properties,
)
report.subscription = subscription
report.resource_name = storage_account.name
report.location = storage_account.location
if (
storage_account.file_service_properties.share_delete_retention_policy.enabled
):
report.status = "PASS"
report.status_extended = f"File share soft delete is enabled for storage account {storage_account.name} with a retention period of {storage_account.file_service_properties.share_delete_retention_policy.days} days."
else:
report.status = "FAIL"
report.status_extended = f"File share soft delete is not enabled for storage account {storage_account.name}."
findings.append(report)
return findings
@@ -2,6 +2,7 @@ from dataclasses import dataclass
from typing import List, Optional
from azure.mgmt.storage import StorageManagementClient
from pydantic import BaseModel
from prowler.lib.logger import logger
from prowler.providers.azure.azure_provider import AzureProvider
@@ -122,6 +123,47 @@ class Storage(AzureService):
f"Subscription name: {subscription} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
<<<<<<< HEAD
=======
def _get_file_share_properties(self):
logger.info("Storage - Getting file share properties...")
for subscription, accounts in self.storage_accounts.items():
client = self.clients[subscription]
for account in accounts:
try:
file_service_properties = (
client.file_services.get_service_properties(
account.resouce_group_name, account.name
)
)
share_delete_retention_policy = getattr(
file_service_properties,
"share_delete_retention_policy",
None,
)
account.file_service_properties = FileServiceProperties(
id=file_service_properties.id,
name=file_service_properties.name,
type=file_service_properties.type,
share_delete_retention_policy=DeleteRetentionPolicy(
enabled=getattr(
share_delete_retention_policy,
"enabled",
False,
),
days=getattr(
share_delete_retention_policy,
"days",
0,
),
),
)
except Exception as error:
logger.error(
f"Subscription name: {subscription} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
>>>>>>> e0465f2aa (fix(azure): consolidate file share properties to the storage account level (#8087))
@dataclass
class DeleteRetentionPolicy:
@@ -151,6 +193,27 @@ class PrivateEndpointConnection:
type: str
<<<<<<< HEAD
=======
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 FileServiceProperties(BaseModel):
id: str
name: str
type: str
share_delete_retention_policy: DeleteRetentionPolicy
>>>>>>> e0465f2aa (fix(azure): consolidate file share properties to the storage account level (#8087))
@dataclass
class Account:
id: str
@@ -166,3 +229,8 @@ class Account:
key_expiration_period_in_days: str
location: str
blob_properties: Optional[BlobProperties] = None
<<<<<<< HEAD
=======
default_to_entra_authorization: bool = False
file_service_properties: Optional[FileServiceProperties] = None
>>>>>>> e0465f2aa (fix(azure): consolidate file share properties to the storage account level (#8087))
@@ -0,0 +1,194 @@
from unittest import mock
from uuid import uuid4
from prowler.providers.azure.services.storage.storage_service import (
Account,
DeleteRetentionPolicy,
FileServiceProperties,
)
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_ID,
set_mocked_azure_provider,
)
class Test_storage_ensure_file_shares_soft_delete_is_enabled:
def test_no_storage_accounts(self):
storage_client = mock.MagicMock
storage_client.storage_accounts = {}
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_ensure_file_shares_soft_delete_is_enabled.storage_ensure_file_shares_soft_delete_is_enabled.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_ensure_file_shares_soft_delete_is_enabled.storage_ensure_file_shares_soft_delete_is_enabled import (
storage_ensure_file_shares_soft_delete_is_enabled,
)
check = storage_ensure_file_shares_soft_delete_is_enabled()
result = check.execute()
assert len(result) == 0
def test_storage_account_no_file_properties(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
storage_client = mock.MagicMock
storage_client.storage_accounts = {
AZURE_SUBSCRIPTION_ID: [
Account(
id=storage_account_id,
name=storage_account_name,
resouce_group_name=None,
enable_https_traffic_only=False,
infrastructure_encryption=False,
allow_blob_public_access=None,
network_rule_set=None,
encryption_type="None",
minimum_tls_version=None,
key_expiration_period_in_days=None,
location="westeurope",
private_endpoint_connections=None,
file_service_properties=None,
)
]
}
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_ensure_file_shares_soft_delete_is_enabled.storage_ensure_file_shares_soft_delete_is_enabled.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_ensure_file_shares_soft_delete_is_enabled.storage_ensure_file_shares_soft_delete_is_enabled import (
storage_ensure_file_shares_soft_delete_is_enabled,
)
check = storage_ensure_file_shares_soft_delete_is_enabled()
result = check.execute()
assert len(result) == 0
def test_file_share_soft_delete_disabled(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
storage_client = mock.MagicMock
retention_policy = DeleteRetentionPolicy(enabled=False, days=0)
file_service_properties = FileServiceProperties(
id=f"/subscriptions/{AZURE_SUBSCRIPTION_ID}/resourceGroups/prowler-resource-group/providers/Microsoft.Storage/storageAccounts/{storage_account_name}/fileServices/default",
name="default",
type="Microsoft.Storage/storageAccounts/fileServices",
share_delete_retention_policy=retention_policy,
)
storage_client.storage_accounts = {
AZURE_SUBSCRIPTION_ID: [
Account(
id=storage_account_id,
name=storage_account_name,
resouce_group_name=None,
enable_https_traffic_only=False,
infrastructure_encryption=False,
allow_blob_public_access=None,
network_rule_set=None,
encryption_type="None",
minimum_tls_version=None,
key_expiration_period_in_days=None,
location="westeurope",
private_endpoint_connections=None,
file_service_properties=file_service_properties,
)
]
}
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_ensure_file_shares_soft_delete_is_enabled.storage_ensure_file_shares_soft_delete_is_enabled.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_ensure_file_shares_soft_delete_is_enabled.storage_ensure_file_shares_soft_delete_is_enabled import (
storage_ensure_file_shares_soft_delete_is_enabled,
)
check = storage_ensure_file_shares_soft_delete_is_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"File share soft delete is not enabled for storage account {storage_account_name}."
)
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == storage_account_name
assert result[0].resource_id == file_service_properties.id
assert result[0].location == "westeurope"
def test_file_share_soft_delete_enabled(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
storage_client = mock.MagicMock
retention_policy = DeleteRetentionPolicy(enabled=True, days=7)
file_service_properties = FileServiceProperties(
id=f"/subscriptions/{AZURE_SUBSCRIPTION_ID}/resourceGroups/prowler-resource-group/providers/Microsoft.Storage/storageAccounts/{storage_account_name}/fileServices/default",
name="default",
type="Microsoft.Storage/storageAccounts/fileServices",
share_delete_retention_policy=retention_policy,
)
storage_client.storage_accounts = {
AZURE_SUBSCRIPTION_ID: [
Account(
id=storage_account_id,
name=storage_account_name,
resouce_group_name=None,
enable_https_traffic_only=False,
infrastructure_encryption=False,
allow_blob_public_access=None,
network_rule_set=None,
encryption_type="None",
minimum_tls_version=None,
key_expiration_period_in_days=None,
location="westeurope",
private_endpoint_connections=None,
file_service_properties=file_service_properties,
)
]
}
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_ensure_file_shares_soft_delete_is_enabled.storage_ensure_file_shares_soft_delete_is_enabled.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_ensure_file_shares_soft_delete_is_enabled.storage_ensure_file_shares_soft_delete_is_enabled import (
storage_ensure_file_shares_soft_delete_is_enabled,
)
check = storage_ensure_file_shares_soft_delete_is_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"File share soft delete is enabled for storage account {storage_account_name} with a retention period of {retention_policy.days} days."
)
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == storage_account_name
assert result[0].resource_id == file_service_properties.id
assert result[0].location == "westeurope"
@@ -3,6 +3,12 @@ from unittest.mock import patch
from prowler.providers.azure.services.storage.storage_service import (
Account,
BlobProperties,
<<<<<<< HEAD
=======
DeleteRetentionPolicy,
FileServiceProperties,
ReplicationSettings,
>>>>>>> e0465f2aa (fix(azure): consolidate file share properties to the storage account level (#8087))
Storage,
)
from tests.providers.azure.azure_fixtures import (
@@ -19,6 +25,16 @@ def mock_storage_get_storage_accounts(_):
default_service_version=None,
container_delete_retention_policy=None,
)
<<<<<<< HEAD
=======
retention_policy = DeleteRetentionPolicy(enabled=True, days=7)
file_service_properties = FileServiceProperties(
id="id",
name="name",
type="type",
share_delete_retention_policy=retention_policy,
)
>>>>>>> e0465f2aa (fix(azure): consolidate file share properties to the storage account level (#8087))
return {
AZURE_SUBSCRIPTION_ID: [
Account(
@@ -35,6 +51,14 @@ def mock_storage_get_storage_accounts(_):
private_endpoint_connections=None,
location="westeurope",
blob_properties=blob_properties,
<<<<<<< HEAD
=======
default_to_entra_authorization=True,
replication_settings=ReplicationSettings.STANDARD_LRS,
allow_cross_tenant_replication=True,
allow_shared_key_access=True,
file_service_properties=file_service_properties,
>>>>>>> e0465f2aa (fix(azure): consolidate file share properties to the storage account level (#8087))
)
]
}
@@ -143,3 +167,16 @@ class Test_Storage_Service:
].blob_properties.container_delete_retention_policy
is None
)
<<<<<<< HEAD
=======
def test_get_file_service_properties(self):
storage = Storage(set_mocked_azure_provider())
account = storage.storage_accounts[AZURE_SUBSCRIPTION_ID][0]
assert hasattr(account, "file_service_properties")
assert (
account.file_service_properties.share_delete_retention_policy.enabled
is True
)
assert account.file_service_properties.share_delete_retention_policy.days == 7
>>>>>>> e0465f2aa (fix(azure): consolidate file share properties to the storage account level (#8087))