feat(storage): add new check storage_cross_tenant_replication_disabled (#7977)

Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
This commit is contained in:
Hugo Pereira Brito
2025-06-18 09:54:13 +02:00
committed by GitHub
parent cc8f6131e6
commit 1b73ab2fe4
7 changed files with 211 additions and 0 deletions
+2
View File
@@ -5,6 +5,8 @@ All notable changes to the **Prowler SDK** are documented in this file.
## [v5.8.0] (Prowler UNRELEASED)
### Added
- Add `storage_cross_tenant_replication_disabled` check for Azure provider. [(#7977)](https://github.com/prowler-cloud/prowler/pull/7977)
- CIS 1.11 compliance framework for Kubernetes [(#7790)](https://github.com/prowler-cloud/prowler/pull/7790)
- Support `HTTPS_PROXY` and `K8S_SKIP_TLS_VERIFY` in Kubernetes [(#7720)](https://github.com/prowler-cloud/prowler/pull/7720)
- Weight for Prowler ThreatScore scoring [(#7795)](https://github.com/prowler-cloud/prowler/pull/7795)
@@ -0,0 +1,30 @@
{
"Provider": "azure",
"CheckID": "storage_cross_tenant_replication_disabled",
"CheckTitle": "Ensure cross-tenant replication is disabled",
"CheckType": [],
"ServiceName": "storage",
"SubServiceName": "account",
"ResourceIdTemplate": "",
"Severity": "high",
"ResourceType": "AzureStorageAccount",
"Description": "Ensure that cross-tenant replication is not enabled on Azure Storage Accounts to prevent unintended replication of data across tenant boundaries.",
"Risk": "If cross-tenant replication is enabled, sensitive data could be inadvertently replicated across tenants, increasing the risk of data leakage, unauthorized access, or non-compliance with data governance and privacy policies.",
"RelatedUrl": "https://learn.microsoft.com/en-us/azure/storage/blobs/object-replication-prevent-cross-tenant-policies?tabs=portal",
"Remediation": {
"Code": {
"CLI": "az storage account update --name <storage-account-name> --resource-group <resource-group> --default-to-oauth-authentication true --allow-cross-tenant-replication false",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity-staging/knowledge-base/azure/StorageAccounts/disable-cross-tenant-replication.html",
"Terraform": ""
},
"Recommendation": {
"Text": "Disable Cross Tenant Replication on storage accounts to ensure that data remains within tenant boundaries unless explicitly shared, reducing the risk of data leakage and unauthorized access.",
"Url": "https://learn.microsoft.com/en-us/azure/storage/blobs/object-replication-prevent-cross-tenant-policies?tabs=portal"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,34 @@
from prowler.lib.check.models import Check, Check_Report_Azure
from prowler.providers.azure.services.storage.storage_client import storage_client
class storage_cross_tenant_replication_disabled(Check):
"""Check if cross-tenant replication is disabled.
Attributes:
metadata: Metadata associated with the check (inherited from Check).
"""
def execute(self) -> Check_Report_Azure:
"""Execute the check for cross-tenant replication.
This method checks if cross-tenant replication is disabled. If it is, the check passes; otherwise, it fails.
Returns:
Check_Report_Azure: A report containing the result of the check.
"""
findings = []
for subscription, storage_accounts in storage_client.storage_accounts.items():
for storage_account in storage_accounts:
report = Check_Report_Azure(
metadata=self.metadata(), resource=storage_account
)
report.subscription = subscription
if not storage_account.allow_cross_tenant_replication:
report.status = "PASS"
report.status_extended = f"Storage account {storage_account.name} from subscription {subscription} has cross-tenant replication disabled."
else:
report.status = "FAIL"
report.status_extended = f"Storage account {storage_account.name} from subscription {subscription} has cross-tenant replication enabled."
findings.append(report)
return findings
@@ -68,6 +68,9 @@ class Storage(AzureService):
],
key_expiration_period_in_days=key_expiration_period_in_days,
location=storage_account.location,
allow_cross_tenant_replication=getattr(
storage_account, "allow_cross_tenant_replication", True
),
allow_shared_key_access=getattr(
storage_account, "allow_shared_key_access", True
),
@@ -222,6 +225,7 @@ class Account:
private_endpoint_connections: List[PrivateEndpointConnection]
key_expiration_period_in_days: str
location: str
allow_cross_tenant_replication: bool = True
allow_shared_key_access: bool = True
blob_properties: Optional[BlobProperties] = None
file_shares: list = None
@@ -0,0 +1,134 @@
from unittest import mock
from uuid import uuid4
from prowler.providers.azure.services.storage.storage_service import Account
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_ID,
set_mocked_azure_provider,
)
class Test_storage_cross_tenant_replication_disabled:
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_cross_tenant_replication_disabled.storage_cross_tenant_replication_disabled.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_cross_tenant_replication_disabled.storage_cross_tenant_replication_disabled import (
storage_cross_tenant_replication_disabled,
)
check = storage_cross_tenant_replication_disabled()
result = check.execute()
assert len(result) == 0
def test_storage_account_cross_tenant_replication_enabled(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,
allow_cross_tenant_replication=True,
)
]
}
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_cross_tenant_replication_disabled.storage_cross_tenant_replication_disabled.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_cross_tenant_replication_disabled.storage_cross_tenant_replication_disabled import (
storage_cross_tenant_replication_disabled,
)
check = storage_cross_tenant_replication_disabled()
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} has cross-tenant replication 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_cross_tenant_replication_disabled(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,
allow_cross_tenant_replication=False,
)
]
}
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_cross_tenant_replication_disabled.storage_cross_tenant_replication_disabled.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_cross_tenant_replication_disabled.storage_cross_tenant_replication_disabled import (
storage_cross_tenant_replication_disabled,
)
check = storage_cross_tenant_replication_disabled()
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 cross-tenant replication disabled."
)
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"
@@ -40,6 +40,7 @@ def mock_storage_get_storage_accounts(_):
private_endpoint_connections=None,
location="westeurope",
blob_properties=blob_properties,
allow_cross_tenant_replication=True,
allow_shared_key_access=True,
file_shares=file_shares,
)
@@ -117,6 +118,12 @@ class Test_Storage_Service:
default_service_version=None,
container_delete_retention_policy=None,
)
assert (
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][
0
].allow_cross_tenant_replication
is True
)
assert (
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][0].allow_shared_key_access
is True