feat(azure): add new check storage_blob_versioning_is_enabled (#7927)

Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
This commit is contained in:
Rubén De la Torre Vico
2025-06-18 09:46:38 +02:00
committed by GitHub
parent dfd5c9aee7
commit cc8f6131e6
6 changed files with 268 additions and 0 deletions
+2
View File
@@ -27,12 +27,14 @@ 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)
- `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)
---
## [v5.7.5] (Prowler UNRELEASED)
### Fixed
@@ -0,0 +1,30 @@
{
"Provider": "azure",
"CheckID": "storage_blob_versioning_is_enabled",
"CheckTitle": "Ensure Blob Versioning is Enabled on Azure Blob Storage Accounts",
"CheckType": [],
"ServiceName": "storage",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "medium",
"ResourceType": "AzureStorageAccount",
"Description": "Ensure that blob versioning is enabled on Azure Blob Storage accounts to automatically retain previous versions of objects.",
"Risk": "Without blob versioning, accidental or malicious changes to blobs cannot be easily recovered, leading to potential data loss.",
"RelatedUrl": "https://learn.microsoft.com/en-us/azure/storage/blobs/versioning-enable",
"Remediation": {
"Code": {
"CLI": "az storage account blob-service-properties update --resource-group <resource_group> --account-name <storage-account> --enable-versioning true",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/StorageAccounts/enable-versioning-for-blobs.html",
"Terraform": "resource \"azurerm_storage_account\" \"example\" {\n name = \"examplestorageacct\"\n resource_group_name = azurerm_resource_group.example.name\n location = azurerm_resource_group.example.location\n account_tier = \"Standard\"\n account_replication_type = \"LRS\"\n\n blob_properties {\n versioning_enabled = true\n }\n}\n"
},
"Recommendation": {
"Text": "Enable blob versioning for all Azure Storage accounts that store critical or sensitive data.",
"Url": "https://learn.microsoft.com/en-us/azure/storage/blobs/versioning-enable"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,24 @@
from prowler.lib.check.models import Check, Check_Report_Azure
from prowler.providers.azure.services.storage.storage_client import storage_client
class storage_blob_versioning_is_enabled(Check):
def execute(self) -> Check_Report_Azure:
findings = []
for subscription, storage_accounts in storage_client.storage_accounts.items():
for storage_account in storage_accounts:
if storage_account.blob_properties:
report = Check_Report_Azure(
metadata=self.metadata(), resource=storage_account
)
report.subscription = subscription
if getattr(
storage_account.blob_properties, "versioning_enabled", False
):
report.status = "PASS"
report.status_extended = f"Storage account {storage_account.name} from subscription {subscription} has blob versioning enabled."
else:
report.status = "FAIL"
report.status_extended = f"Storage account {storage_account.name} from subscription {subscription} does not have blob versioning enabled."
findings.append(report)
return findings
@@ -92,6 +92,9 @@ class Storage(AzureService):
container_delete_retention_policy = getattr(
properties, "container_delete_retention_policy", None
)
versioning_enabled = getattr(
properties, "is_versioning_enabled", False
)
account.blob_properties = BlobProperties(
id=properties.id,
name=properties.name,
@@ -107,6 +110,7 @@ class Storage(AzureService):
container_delete_retention_policy, "days", 0
),
),
versioning_enabled=versioning_enabled,
)
except Exception as error:
if (
@@ -188,6 +192,7 @@ class BlobProperties:
type: str
default_service_version: str
container_delete_retention_policy: DeleteRetentionPolicy
versioning_enabled: bool = False
@dataclass
@@ -0,0 +1,207 @@
from unittest import mock
from uuid import uuid4
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_ID,
set_mocked_azure_provider,
)
class Test_storage_blob_versioning_is_enabled:
def test_storage_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_blob_versioning_is_enabled.storage_blob_versioning_is_enabled.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_blob_versioning_is_enabled.storage_blob_versioning_is_enabled import (
storage_blob_versioning_is_enabled,
)
check = storage_blob_versioning_is_enabled()
result = check.execute()
assert len(result) == 0
def test_storage_no_blob_properties(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
storage_client = mock.MagicMock
storage_account_blob_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_blob_versioning_is_enabled.storage_blob_versioning_is_enabled.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_service import Account
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,
blob_properties=storage_account_blob_properties,
)
]
}
from prowler.providers.azure.services.storage.storage_blob_versioning_is_enabled.storage_blob_versioning_is_enabled import (
storage_blob_versioning_is_enabled,
)
check = storage_blob_versioning_is_enabled()
result = check.execute()
assert len(result) == 0
def test_storage_blob_versioning_is_enabled(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
storage_client = mock.MagicMock
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_blob_versioning_is_enabled.storage_blob_versioning_is_enabled.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_service import (
Account,
BlobProperties,
DeleteRetentionPolicy,
)
storage_account_blob_properties = BlobProperties(
id=None,
name=None,
type=None,
default_service_version=None,
container_delete_retention_policy=DeleteRetentionPolicy(
enabled=False, days=0
),
versioning_enabled=True,
)
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,
blob_properties=storage_account_blob_properties,
)
]
}
from prowler.providers.azure.services.storage.storage_blob_versioning_is_enabled.storage_blob_versioning_is_enabled import (
storage_blob_versioning_is_enabled,
)
check = storage_blob_versioning_is_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 blob versioning 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_blob_versioning_is_disabled(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
storage_client = mock.MagicMock
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_blob_versioning_is_enabled.storage_blob_versioning_is_enabled.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_service import (
Account,
BlobProperties,
DeleteRetentionPolicy,
)
storage_account_blob_properties = BlobProperties(
id=None,
name=None,
type=None,
default_service_version=None,
container_delete_retention_policy=DeleteRetentionPolicy(
enabled=False, days=0
),
versioning_enabled=False,
)
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,
blob_properties=storage_account_blob_properties,
)
]
}
from prowler.providers.azure.services.storage.storage_blob_versioning_is_enabled.storage_blob_versioning_is_enabled import (
storage_blob_versioning_is_enabled,
)
check = storage_blob_versioning_is_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 blob versioning 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"