Merge branch 'master' into PRWLR-6707-create-a-way-of-reporting-for-prowler-threatscore

This commit is contained in:
pedrooot
2025-07-04 10:27:17 +02:00
9 changed files with 351 additions and 9 deletions
+1
View File
@@ -5,6 +5,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
## [v5.9.0] (Prowler UNRELEASED)
### Added
- `storage_smb_channel_encryption_with_secure_algorithm` check for Azure provider [(#8123)](https://github.com/prowler-cloud/prowler/pull/8123)
### Changed
@@ -158,6 +158,21 @@ class Storage(AzureService):
"share_delete_retention_policy",
None,
)
smb_channel_encryption_raw = getattr(
getattr(
getattr(
file_service_properties,
"protocol_settings",
None,
),
"smb",
None,
),
"channel_encryption",
None,
)
account.file_service_properties = FileServiceProperties(
id=file_service_properties.id,
name=file_service_properties.name,
@@ -174,6 +189,13 @@ class Storage(AzureService):
0,
),
),
smb_protocol_settings=SMBProtocolSettings(
channel_encryption=(
smb_channel_encryption_raw.rstrip(";").split(";")
if smb_channel_encryption_raw
else []
)
),
)
except Exception as error:
logger.error(
@@ -221,11 +243,16 @@ class ReplicationSettings(Enum):
STANDARD_RAGZRS = "Standard_RAGZRS"
class SMBProtocolSettings(BaseModel):
channel_encryption: list[str]
class FileServiceProperties(BaseModel):
id: str
name: str
type: str
share_delete_retention_policy: DeleteRetentionPolicy
smb_protocol_settings: SMBProtocolSettings
@dataclass
@@ -0,0 +1,30 @@
{
"Provider": "azure",
"CheckID": "storage_smb_channel_encryption_with_secure_algorithm",
"CheckTitle": "Ensure SMB channel encryption uses a secure algorithm for SMB file shares",
"CheckType": [],
"ServiceName": "storage",
"SubServiceName": "",
"ResourceIdTemplate": "/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.Storage/storageAccounts/{storageAccountName}/fileServices/default",
"Severity": "medium",
"ResourceType": "AzureStorageAccount",
"Description": "Implement SMB channel encryption with a secure algorithm for SMB file shares to ensure data confidentiality and integrity in transit.",
"Risk": "Not using the recommended SMB channel encryption may expose data transmitted over SMB channels to unauthorized interception and tampering.",
"RelatedUrl": "https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-files#recommendations-for-smb-file-shares",
"Remediation": {
"Code": {
"CLI": "az storage account file-service-properties update --resource-group <resource-group> --account-name <storage-account> --channel-encryption AES-256-GCM",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Use the portal, CLI or PowerShell to set the SMB channel encryption to a secure algorithm.",
"Url": "https://learn.microsoft.com/en-us/azure/storage/files/files-smb-protocol?tabs=azure-portal#smb-security-settings"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": "This check passes if SMB channel encryption is set to a secure algorithm."
}
@@ -0,0 +1,51 @@
from prowler.lib.check.models import Check, Check_Report_Azure
from prowler.providers.azure.services.storage.storage_client import storage_client
SECURE_ENCRYPTION_ALGORITHMS = ["AES-256-GCM"]
class storage_smb_channel_encryption_with_secure_algorithm(Check):
"""
Ensure SMB channel encryption for file shares is set to the recommended algorithm (AES-256-GCM or higher).
This check evaluates whether SMB file shares are configured to use only the recommended SMB channel encryption algorithms.
- PASS: Storage account has the recommended SMB channel encryption (AES-256-GCM or higher) enabled for file shares.
- FAIL: Storage account does not have the recommended SMB channel encryption enabled for file shares or uses an unsupported algorithm.
"""
def execute(self) -> list[Check_Report_Azure]:
findings = []
for subscription, storage_accounts in storage_client.storage_accounts.items():
for account in storage_accounts:
if account.file_service_properties:
pretty_current_algorithms = (
", ".join(
account.file_service_properties.smb_protocol_settings.channel_encryption
)
if account.file_service_properties.smb_protocol_settings.channel_encryption
else "none"
)
report = Check_Report_Azure(
metadata=self.metadata(),
resource=account.file_service_properties,
)
report.subscription = subscription
report.resource_name = account.name
if (
not account.file_service_properties.smb_protocol_settings.channel_encryption
):
report.status = "FAIL"
report.status_extended = f"Storage account {account.name} from subscription {subscription} does not have SMB channel encryption enabled for file shares."
elif any(
algorithm in SECURE_ENCRYPTION_ALGORITHMS
for algorithm in account.file_service_properties.smb_protocol_settings.channel_encryption
):
report.status = "PASS"
report.status_extended = f"Storage account {account.name} from subscription {subscription} has a secure algorithm for SMB channel encryption ({', '.join(SECURE_ENCRYPTION_ALGORITHMS)}) enabled for file shares since it supports {pretty_current_algorithms}."
else:
report.status = "FAIL"
report.status_extended = f"Storage account {account.name} from subscription {subscription} does not have SMB channel encryption with a secure algorithm for file shares since it supports {pretty_current_algorithms}."
findings.append(report)
return findings
@@ -110,7 +110,6 @@ class TestAzureProvider:
return_value={},
),
):
with pytest.raises(AzureNoAuthenticationMethodError) as exception:
_ = AzureProvider(
az_cli_auth,
@@ -151,7 +150,6 @@ class TestAzureProvider:
return_value={},
),
):
with pytest.raises(AzureBrowserAuthNoTenantIDError) as exception:
_ = AzureProvider(
az_cli_auth,
@@ -193,7 +191,6 @@ class TestAzureProvider:
return_value={},
),
):
with pytest.raises(AzureTenantIDNoBrowserAuthError) as exception:
_ = AzureProvider(
az_cli_auth,
@@ -224,7 +221,6 @@ class TestAzureProvider:
"prowler.providers.azure.azure_provider.SubscriptionClient"
) as mock_resource_client,
):
# Mock the return value of DefaultAzureCredential
mock_credentials = MagicMock()
mock_credentials.get_token.return_value = AccessToken(
@@ -266,7 +262,6 @@ class TestAzureProvider:
"prowler.providers.azure.azure_provider.AzureProvider.validate_static_credentials"
) as mock_validate_static_credentials,
):
# Mock the return value of DefaultAzureCredential
mock_credentials = MagicMock()
mock_credentials.get_token.return_value = AccessToken(
@@ -317,7 +312,6 @@ class TestAzureProvider:
"prowler.providers.azure.azure_provider.AzureProvider.validate_static_credentials"
) as mock_validate_static_credentials,
):
# Mock the return value of DefaultAzureCredential
mock_default_credential.return_value = {
"client_id": str(uuid4()),
@@ -368,7 +362,6 @@ class TestAzureProvider:
"prowler.providers.azure.azure_provider.AzureProvider.validate_static_credentials"
) as mock_validate_static_credentials,
):
# Mock the return value of DefaultAzureCredential
mock_default_credential.return_value = {
"client_id": str(uuid4()),
@@ -442,7 +435,6 @@ class TestAzureProvider:
"prowler.providers.azure.azure_provider.AzureProvider.setup_session"
) as mock_setup_session,
):
mock_setup_session.side_effect = AzureHTTPResponseError(
file="test_file", original_exception="Simulated HttpResponseError"
)
@@ -463,7 +455,6 @@ class TestAzureProvider:
with patch(
"prowler.providers.azure.azure_provider.AzureProvider.setup_session"
) as mock_setup_session:
mock_setup_session.side_effect = Exception("Simulated Exception")
with pytest.raises(Exception) as exception:
@@ -5,6 +5,7 @@ from prowler.providers.azure.services.storage.storage_service import (
Account,
DeleteRetentionPolicy,
FileServiceProperties,
SMBProtocolSettings,
)
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_ID,
@@ -87,6 +88,7 @@ class Test_storage_ensure_file_shares_soft_delete_is_enabled:
name="default",
type="Microsoft.Storage/storageAccounts/fileServices",
share_delete_retention_policy=retention_policy,
smb_protocol_settings=SMBProtocolSettings(channel_encryption=[]),
)
storage_client.storage_accounts = {
AZURE_SUBSCRIPTION_ID: [
@@ -145,6 +147,7 @@ class Test_storage_ensure_file_shares_soft_delete_is_enabled:
name="default",
type="Microsoft.Storage/storageAccounts/fileServices",
share_delete_retention_policy=retention_policy,
smb_protocol_settings=SMBProtocolSettings(channel_encryption=[]),
)
storage_client.storage_accounts = {
AZURE_SUBSCRIPTION_ID: [
@@ -6,6 +6,7 @@ from prowler.providers.azure.services.storage.storage_service import (
DeleteRetentionPolicy,
FileServiceProperties,
ReplicationSettings,
SMBProtocolSettings,
Storage,
)
from tests.providers.azure.azure_fixtures import (
@@ -28,6 +29,7 @@ def mock_storage_get_storage_accounts(_):
name="name",
type="type",
share_delete_retention_policy=retention_policy,
smb_protocol_settings=SMBProtocolSettings(channel_encryption=[]),
)
return {
AZURE_SUBSCRIPTION_ID: [
@@ -0,0 +1,237 @@
from unittest import mock
from uuid import uuid4
from prowler.providers.azure.services.storage.storage_service import (
Account,
DeleteRetentionPolicy,
FileServiceProperties,
SMBProtocolSettings,
)
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_ID,
set_mocked_azure_provider,
)
class Test_storage_smb_channel_encryption_with_secure_algorithm:
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_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm import (
storage_smb_channel_encryption_with_secure_algorithm,
)
check = storage_smb_channel_encryption_with_secure_algorithm()
result = check.execute()
assert len(result) == 0
def test_no_file_service_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_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm import (
storage_smb_channel_encryption_with_secure_algorithm,
)
check = storage_smb_channel_encryption_with_secure_algorithm()
result = check.execute()
assert len(result) == 0
def test_no_smb_protocol_settings(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
file_service_properties = FileServiceProperties(
id="id1",
name="fs1",
type="type1",
share_delete_retention_policy=DeleteRetentionPolicy(enabled=True, days=7),
smb_protocol_settings=SMBProtocolSettings(channel_encryption=[]),
)
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=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_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm import (
storage_smb_channel_encryption_with_secure_algorithm,
)
check = storage_smb_channel_encryption_with_secure_algorithm()
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 SMB channel encryption enabled for file shares."
)
def test_not_recommended_encryption(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
file_service_properties = FileServiceProperties(
id="id1",
name="fs1",
type="type1",
share_delete_retention_policy=DeleteRetentionPolicy(enabled=True, days=7),
smb_protocol_settings=SMBProtocolSettings(
channel_encryption=["AES-128-GCM"]
),
)
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=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_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm import (
storage_smb_channel_encryption_with_secure_algorithm,
)
check = storage_smb_channel_encryption_with_secure_algorithm()
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 SMB channel encryption with a secure algorithm for file shares since it supports AES-128-GCM."
)
def test_recommended_encryption(self):
storage_account_id = str(uuid4())
storage_account_name = "Test Storage Account"
file_service_properties = FileServiceProperties(
id="id1",
name="fs1",
type="type1",
share_delete_retention_policy=DeleteRetentionPolicy(enabled=True, days=7),
smb_protocol_settings=SMBProtocolSettings(
channel_encryption=["AES-256-GCM"]
),
)
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=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_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm.storage_client",
new=storage_client,
),
):
from prowler.providers.azure.services.storage.storage_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm import (
storage_smb_channel_encryption_with_secure_algorithm,
)
check = storage_smb_channel_encryption_with_secure_algorithm()
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 a secure algorithm for SMB channel encryption (AES-256-GCM) enabled for file shares since it supports AES-256-GCM."
)