From 1a4cfd81c574fcc63d868e0648cf154a00d95b59 Mon Sep 17 00:00:00 2001 From: Prowler Bot Date: Tue, 19 May 2026 17:05:35 +0200 Subject: [PATCH] fix(azure): skip system 'master' DB in sqlserver_tde_encrypted_with_cmk (#11235) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Pedro Martín --- .../sqlserver_tde_encrypted_with_cmk.py | 10 +- .../sqlserver_tde_encrypted_with_cmk_test.py | 197 ++++++++++++++++++ 2 files changed, 204 insertions(+), 3 deletions(-) diff --git a/prowler/providers/azure/services/sqlserver/sqlserver_tde_encrypted_with_cmk/sqlserver_tde_encrypted_with_cmk.py b/prowler/providers/azure/services/sqlserver/sqlserver_tde_encrypted_with_cmk/sqlserver_tde_encrypted_with_cmk.py index 8ba04fb50f..5f44e2663c 100644 --- a/prowler/providers/azure/services/sqlserver/sqlserver_tde_encrypted_with_cmk/sqlserver_tde_encrypted_with_cmk.py +++ b/prowler/providers/azure/services/sqlserver/sqlserver_tde_encrypted_with_cmk/sqlserver_tde_encrypted_with_cmk.py @@ -10,9 +10,13 @@ class sqlserver_tde_encrypted_with_cmk(Check): subscription, subscription ) for sql_server in sql_servers: - databases = ( - sql_server.databases if sql_server.databases is not None else [] - ) + databases = [ + database + for database in ( + sql_server.databases if sql_server.databases is not None else [] + ) + if database.name.lower() != "master" + ] if len(databases) > 0: report = Check_Report_Azure( metadata=self.metadata(), resource=sql_server diff --git a/tests/providers/azure/services/sqlserver/sqlserver_tde_encrypted_with_cmk/sqlserver_tde_encrypted_with_cmk_test.py b/tests/providers/azure/services/sqlserver/sqlserver_tde_encrypted_with_cmk/sqlserver_tde_encrypted_with_cmk_test.py index 6add11d9c3..4dcdffc0e7 100644 --- a/tests/providers/azure/services/sqlserver/sqlserver_tde_encrypted_with_cmk/sqlserver_tde_encrypted_with_cmk_test.py +++ b/tests/providers/azure/services/sqlserver/sqlserver_tde_encrypted_with_cmk/sqlserver_tde_encrypted_with_cmk_test.py @@ -264,3 +264,200 @@ class Test_sqlserver_tde_encrypted_with_cmk: assert result[0].resource_name == sql_server_name assert result[0].resource_id == sql_server_id assert result[0].location == "location" + + def test_sql_servers_master_database_disabled_user_database_enabled(self): + # System "master" database always reports TDE Disabled in Azure SQL + # and is not customer-controllable. It must not fail a server whose + # user databases are correctly encrypted with CMK (PROWLER-1760). + sqlserver_client = mock.MagicMock + sqlserver_client.subscriptions = { + AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME + } + sql_server_name = "SQL Server Name" + sql_server_id = str(uuid4()) + master_database = Database( + id="master_id", + name="master", + type="type", + location="location", + managed_by="managed_by", + tde_encryption=TransparentDataEncryption(status="Disabled"), + ) + user_database = Database( + id="user_id", + name="DynamicBudgets_Intacct", + type="type", + location="location", + managed_by="managed_by", + tde_encryption=TransparentDataEncryption(status="Enabled"), + ) + sqlserver_client.sql_servers = { + AZURE_SUBSCRIPTION_ID: [ + Server( + id=sql_server_id, + name=sql_server_name, + location="location", + public_network_access="", + minimal_tls_version="", + administrators=None, + auditing_policies=None, + firewall_rules=None, + databases=[master_database, user_database], + encryption_protector=EncryptionProtector( + server_key_type="AzureKeyVault" + ), + ) + ] + } + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.sqlserver.sqlserver_tde_encrypted_with_cmk.sqlserver_tde_encrypted_with_cmk.sqlserver_client", + new=sqlserver_client, + ), + ): + from prowler.providers.azure.services.sqlserver.sqlserver_tde_encrypted_with_cmk.sqlserver_tde_encrypted_with_cmk import ( + sqlserver_tde_encrypted_with_cmk, + ) + + check = sqlserver_tde_encrypted_with_cmk() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"SQL Server {sql_server_name} from subscription {AZURE_SUBSCRIPTION_DISPLAY} has TDE enabled with CMK." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == sql_server_name + assert result[0].resource_id == sql_server_id + assert result[0].location == "location" + + def test_sql_servers_only_master_database(self): + # A server whose only database is the system "master" has no user + # databases to evaluate, so it must not produce a finding. + sqlserver_client = mock.MagicMock + sqlserver_client.subscriptions = { + AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME + } + sql_server_name = "SQL Server Name" + sql_server_id = str(uuid4()) + master_database = Database( + id="master_id", + name="MASTER", + type="type", + location="location", + managed_by="managed_by", + tde_encryption=TransparentDataEncryption(status="Disabled"), + ) + sqlserver_client.sql_servers = { + AZURE_SUBSCRIPTION_ID: [ + Server( + id=sql_server_id, + name=sql_server_name, + location="location", + public_network_access="", + minimal_tls_version="", + administrators=None, + auditing_policies=None, + firewall_rules=None, + databases=[master_database], + encryption_protector=EncryptionProtector( + server_key_type="AzureKeyVault" + ), + ) + ] + } + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.sqlserver.sqlserver_tde_encrypted_with_cmk.sqlserver_tde_encrypted_with_cmk.sqlserver_client", + new=sqlserver_client, + ), + ): + from prowler.providers.azure.services.sqlserver.sqlserver_tde_encrypted_with_cmk.sqlserver_tde_encrypted_with_cmk import ( + sqlserver_tde_encrypted_with_cmk, + ) + + check = sqlserver_tde_encrypted_with_cmk() + result = check.execute() + assert len(result) == 0 + + def test_sql_servers_master_disabled_user_database_disabled(self): + # Filtering out "master" must not mask a genuinely failing user + # database: a disabled user DB still fails even with CMK. + sqlserver_client = mock.MagicMock + sqlserver_client.subscriptions = { + AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME + } + sql_server_name = "SQL Server Name" + sql_server_id = str(uuid4()) + master_database = Database( + id="master_id", + name="master", + type="type", + location="location", + managed_by="managed_by", + tde_encryption=TransparentDataEncryption(status="Disabled"), + ) + user_database = Database( + id="user_id", + name="DynamicBudgets_Intacct", + type="type", + location="location", + managed_by="managed_by", + tde_encryption=TransparentDataEncryption(status="Disabled"), + ) + sqlserver_client.sql_servers = { + AZURE_SUBSCRIPTION_ID: [ + Server( + id=sql_server_id, + name=sql_server_name, + location="location", + public_network_access="", + minimal_tls_version="", + administrators=None, + auditing_policies=None, + firewall_rules=None, + databases=[master_database, user_database], + encryption_protector=EncryptionProtector( + server_key_type="AzureKeyVault" + ), + ) + ] + } + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.sqlserver.sqlserver_tde_encrypted_with_cmk.sqlserver_tde_encrypted_with_cmk.sqlserver_client", + new=sqlserver_client, + ), + ): + from prowler.providers.azure.services.sqlserver.sqlserver_tde_encrypted_with_cmk.sqlserver_tde_encrypted_with_cmk import ( + sqlserver_tde_encrypted_with_cmk, + ) + + check = sqlserver_tde_encrypted_with_cmk() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"SQL Server {sql_server_name} from subscription {AZURE_SUBSCRIPTION_DISPLAY} has TDE disabled with CMK." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == sql_server_name + assert result[0].resource_id == sql_server_id + assert result[0].location == "location"