From b07080245dd38c2e21c7122594456fe2823a9dd3 Mon Sep 17 00:00:00 2001 From: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com> Date: Tue, 22 Apr 2025 15:58:07 +0200 Subject: [PATCH] feat(defender): add new check `defender_antispam_outbound_policy_configured` (#7480) Co-authored-by: HugoPBrito Co-authored-by: MrCloudSec --- prowler/CHANGELOG.md | 1 + .../m365/lib/powershell/m365_powershell.py | 37 +++ .../__init__.py | 0 ...m_outbound_policy_configured.metadata.json | 30 +++ ...der_antispam_outbound_policy_configured.py | 54 ++++ .../services/defender/defender_service.py | 66 ++++- ...ntispam_outbound_policy_configured_test.py | 248 ++++++++++++++++++ ..._common_attachments_filter_enabled_test.py | 8 +- ...ons_internal_users_malware_enabled_test.py | 12 +- .../defender/m365_defender_service_test.py | 92 ++++++- 10 files changed, 533 insertions(+), 15 deletions(-) create mode 100644 prowler/providers/m365/services/defender/defender_antispam_outbound_policy_configured/__init__.py create mode 100644 prowler/providers/m365/services/defender/defender_antispam_outbound_policy_configured/defender_antispam_outbound_policy_configured.metadata.json create mode 100644 prowler/providers/m365/services/defender/defender_antispam_outbound_policy_configured/defender_antispam_outbound_policy_configured.py create mode 100644 tests/providers/m365/services/defender/defender_antispam_outbound_policy_configured/defender_antispam_outbound_policy_configured_test.py diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 22feb8bb6a..d42960d4d7 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - Add check for unused Service Accounts in GCP [(#7419)](https://github.com/prowler-cloud/prowler/pull/7419). - Add Powershell to Microsoft365 [(#7331)](https://github.com/prowler-cloud/prowler/pull/7331). - Add service Defender to Microsoft365 with one check for Common Attachments filter enabled in Malware Policies [(#7425)](https://github.com/prowler-cloud/prowler/pull/7425). +- Add check for Outbound Antispam Policy well configured in service Defender for M365 [(#7480)](https://github.com/prowler-cloud/prowler/pull/7480). - Add check for Antiphishing Policy well configured in service Defender in M365 [(#7453)](https://github.com/prowler-cloud/prowler/pull/7453). - Add check for Notifications for Internal users enabled in Malware Policies from service Defender in M365 [(#7435)](https://github.com/prowler-cloud/prowler/pull/7435). - Support CLOUDSDK_AUTH_ACCESS_TOKEN in GCP [(#7495)](https://github.com/prowler-cloud/prowler/pull/7495). diff --git a/prowler/providers/m365/lib/powershell/m365_powershell.py b/prowler/providers/m365/lib/powershell/m365_powershell.py index 06bb40b5b8..573cdd5adf 100644 --- a/prowler/providers/m365/lib/powershell/m365_powershell.py +++ b/prowler/providers/m365/lib/powershell/m365_powershell.py @@ -188,6 +188,43 @@ class M365PowerShell(PowerShellSession): """ return self.execute("Get-MalwareFilterPolicy | ConvertTo-Json") + def get_outbound_spam_filter_policy(self) -> dict: + """ + Get Defender Outbound Spam Filter Policy. + + Retrieves the current Defender outbound spam filter policy settings. + + Returns: + dict: Outbound spam filter policy settings in JSON format. + + Example: + >>> get_outbound_spam_filter_policy() + { + "NotifyOutboundSpam": true, + "BccSuspiciousOutboundMail": true, + "BccSuspiciousOutboundAdditionalRecipients": [], + "NotifyOutboundSpamRecipients": [] + } + """ + return self.execute("Get-HostedOutboundSpamFilterPolicy | ConvertTo-Json") + + def get_outbound_spam_filter_rule(self) -> dict: + """ + Get Defender Outbound Spam Filter Rule. + + Retrieves the current Defender outbound spam filter rule settings. + + Returns: + dict: Outbound spam filter rule settings in JSON format. + + Example: + >>> get_outbound_spam_filter_rule() + { + "State": "Enabled" + } + """ + return self.execute("Get-HostedOutboundSpamFilterRule | ConvertTo-Json") + def get_antiphishing_policy(self) -> dict: """ Get Defender Antiphishing Policy. diff --git a/prowler/providers/m365/services/defender/defender_antispam_outbound_policy_configured/__init__.py b/prowler/providers/m365/services/defender/defender_antispam_outbound_policy_configured/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/m365/services/defender/defender_antispam_outbound_policy_configured/defender_antispam_outbound_policy_configured.metadata.json b/prowler/providers/m365/services/defender/defender_antispam_outbound_policy_configured/defender_antispam_outbound_policy_configured.metadata.json new file mode 100644 index 0000000000..0d7b2ecc76 --- /dev/null +++ b/prowler/providers/m365/services/defender/defender_antispam_outbound_policy_configured/defender_antispam_outbound_policy_configured.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "m365", + "CheckID": "defender_antispam_outbound_policy_configured", + "CheckTitle": "Ensure Defender Outbound Spam Policies are set to notify administrators.", + "CheckType": [], + "ServiceName": "defender", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "low", + "ResourceType": "Defender Anti-Spam Policy", + "Description": "Ensure that outbound anti-spam policies are configured to notify administrators and copy suspicious outbound messages to designated recipients when a sender is blocked for sending spam emails.", + "Risk": "Without outbound spam notifications and message copies, compromised accounts may go undetected, increasing the risk of reputation damage or data leakage through unauthorized email activity.", + "RelatedUrl": "https://learn.microsoft.com/en-us/defender-office-365/outbound-spam-protection-about", + "Remediation": { + "Code": { + "CLI": "$BccEmailAddress = @(\"\")\n$NotifyEmailAddress = @(\"\")\nSet-HostedOutboundSpamFilterPolicy -Identity Default -BccSuspiciousOutboundAdditionalRecipients $BccEmailAddress -BccSuspiciousOutboundMail $true -NotifyOutboundSpam $true -NotifyOutboundSpamRecipients $NotifyEmailAddress", + "NativeIaC": "", + "Other": "1. Navigate to Microsoft 365 Defender https://security.microsoft.com. 2. Click to expand Email & collaboration and select Policies & rules > Threat policies. 3. Under Policies, select Anti-spam. 4. Click on the Anti-spam outbound policy (default). 5. Select Edit protection settings then under Notifications: 6. Check 'Send a copy of suspicious outbound messages or message that exceed these limits to these users and groups' and enter the email addresses. 7. Check 'Notify these users and groups if a sender is blocked due to sending outbound spam' and enter the desired email addresses. 8. Click Save.", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure Defender outbound spam filter policies to notify administrators and copy suspicious outbound messages when users are blocked for sending spam.", + "Url": "https://learn.microsoft.com/en-us/defender-office-365/outbound-spam-protection-about" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "Ensure settings are applied to the highest priority policy if custom policies exist. Default values do not notify or copy outbound spam messages by default." +} diff --git a/prowler/providers/m365/services/defender/defender_antispam_outbound_policy_configured/defender_antispam_outbound_policy_configured.py b/prowler/providers/m365/services/defender/defender_antispam_outbound_policy_configured/defender_antispam_outbound_policy_configured.py new file mode 100644 index 0000000000..b4a0f2e31b --- /dev/null +++ b/prowler/providers/m365/services/defender/defender_antispam_outbound_policy_configured/defender_antispam_outbound_policy_configured.py @@ -0,0 +1,54 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportM365 +from prowler.providers.m365.services.defender.defender_client import defender_client + + +class defender_antispam_outbound_policy_configured(Check): + """ + Check if the Exchange Online Spam Policies are configured to notify administrators + when a sender is blocked for sending spam emails. + + Attributes: + metadata: Metadata associated with the check (inherited from Check). + """ + + def execute(self) -> List[CheckReportM365]: + """ + Execute the check to verify if the Exchange Online Spam Policies notify administrators + when a sender is blocked for sending spam emails. + + Returns: + List[CheckReportM365]: A list of reports containing the result of the check. + """ + findings = [] + for policy_name, policy in defender_client.outbound_spam_policies.items(): + report = CheckReportM365( + metadata=self.metadata(), + resource=policy, + resource_name="Defender Outbound Spam Policy", + resource_id=policy_name, + ) + report.status = "FAIL" + report.status_extended = ( + f"Outbound Spam Policy {policy_name} is not properly configured." + ) + + if ( + not policy.default + and policy_name in defender_client.outbound_spam_rules + and defender_client.outbound_spam_rules[policy_name].state.lower() + == "enabled" + ) or policy.default: + if ( + policy.notify_limit_exceeded + and policy.notify_sender_blocked + and policy.notify_limit_exceeded_addresses + and policy.notify_sender_blocked_addresses + ): + report.status = "PASS" + report.status_extended = f"Outbound Spam Policy {policy_name} is properly configured and enabled." + + findings.append(report) + + return findings diff --git a/prowler/providers/m365/services/defender/defender_service.py b/prowler/providers/m365/services/defender/defender_service.py index 37948bfd6e..05e57d669d 100644 --- a/prowler/providers/m365/services/defender/defender_service.py +++ b/prowler/providers/m365/services/defender/defender_service.py @@ -1,3 +1,5 @@ +from typing import List + from pydantic import BaseModel from prowler.lib.logger import logger @@ -10,6 +12,8 @@ class Defender(M365Service): super().__init__(provider) self.powershell.connect_exchange_online() self.malware_policies = self._get_malware_filter_policy() + self.outbound_spam_policies = self._get_outbound_spam_filter_policy() + self.outbound_spam_rules = self._get_outbound_spam_filter_rule() self.antiphishing_policies = self._get_antiphising_policy() self.antiphising_rules = self._get_antiphising_rules() self.powershell.close() @@ -24,7 +28,7 @@ class Defender(M365Service): for policy in malware_policy: if policy: malware_policies.append( - DefenderMalwarePolicy( + MalwarePolicy( enable_file_filter=policy.get("EnableFileFilter", True), identity=policy.get("Identity", ""), enable_internal_sender_admin_notifications=policy.get( @@ -89,8 +93,54 @@ class Defender(M365Service): ) return antiphishing_rules + def _get_outbound_spam_filter_policy(self): + logger.info("Microsoft365 - Getting Defender outbound spam filter policy...") + outbound_spam_policies = {} + try: + outbound_spam_policy = self.powershell.get_outbound_spam_filter_policy() + if isinstance(outbound_spam_policy, dict): + outbound_spam_policy = [outbound_spam_policy] + for policy in outbound_spam_policy: + if policy: + outbound_spam_policies[policy.get("Name", "")] = OutboundSpamPolicy( + notify_sender_blocked=policy.get("NotifyOutboundSpam", True), + notify_limit_exceeded=policy.get( + "BccSuspiciousOutboundMail", True + ), + notify_limit_exceeded_addresses=policy.get( + "BccSuspiciousOutboundAdditionalRecipients", [] + ), + notify_sender_blocked_addresses=policy.get( + "NotifyOutboundSpamRecipients", [] + ), + default=policy.get("IsDefault", False), + ) + except Exception as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return outbound_spam_policies -class DefenderMalwarePolicy(BaseModel): + def _get_outbound_spam_filter_rule(self): + logger.info("Microsoft365 - Getting Defender outbound spam filter rule...") + outbound_spam_rules = {} + try: + outbound_spam_rule = self.powershell.get_outbound_spam_filter_rule() + if isinstance(outbound_spam_rule, dict): + outbound_spam_rule = [outbound_spam_rule] + for rule in outbound_spam_rule: + if rule: + outbound_spam_rules[rule.get("Name", "")] = OutboundSpamRule( + state=rule.get("State", "Disabled"), + ) + except Exception as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return outbound_spam_rules + + +class MalwarePolicy(BaseModel): enable_file_filter: bool identity: str enable_internal_sender_admin_notifications: bool @@ -111,3 +161,15 @@ class AntiphishingPolicy(BaseModel): class AntiphishingRule(BaseModel): state: str + + +class OutboundSpamPolicy(BaseModel): + notify_sender_blocked: bool + notify_limit_exceeded: bool + notify_limit_exceeded_addresses: List[str] + notify_sender_blocked_addresses: List[str] + default: bool + + +class OutboundSpamRule(BaseModel): + state: str diff --git a/tests/providers/m365/services/defender/defender_antispam_outbound_policy_configured/defender_antispam_outbound_policy_configured_test.py b/tests/providers/m365/services/defender/defender_antispam_outbound_policy_configured/defender_antispam_outbound_policy_configured_test.py new file mode 100644 index 0000000000..fe7c338838 --- /dev/null +++ b/tests/providers/m365/services/defender/defender_antispam_outbound_policy_configured/defender_antispam_outbound_policy_configured_test.py @@ -0,0 +1,248 @@ +from unittest import mock + +from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider + + +class Test_defender_antispam_outbound_policy_configured: + def test_properly_configured_custom_policy(self): + defender_client = mock.MagicMock() + defender_client.audited_tenant = "audited_tenant" + defender_client.audited_domain = DOMAIN + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_m365_provider(), + ), + mock.patch( + "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" + ), + mock.patch( + "prowler.providers.m365.services.defender.defender_antispam_outbound_policy_configured.defender_antispam_outbound_policy_configured.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_configured.defender_antispam_outbound_policy_configured import ( + defender_antispam_outbound_policy_configured, + ) + from prowler.providers.m365.services.defender.defender_service import ( + OutboundSpamPolicy, + OutboundSpamRule, + ) + + defender_client.outbound_spam_policies = { + "Policy1": OutboundSpamPolicy( + notify_sender_blocked=True, + notify_limit_exceeded=True, + notify_limit_exceeded_addresses=["test@correo.com"], + notify_sender_blocked_addresses=["test@correo.com"], + default=False, + ) + } + defender_client.outbound_spam_rules = { + "Policy1": OutboundSpamRule(state="Enabled") + } + + check = defender_antispam_outbound_policy_configured() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Outbound Spam Policy Policy1 is properly configured and enabled." + ) + assert ( + result[0].resource + == defender_client.outbound_spam_policies["Policy1"].dict() + ) + assert result[0].resource_name == "Defender Outbound Spam Policy" + assert result[0].resource_id == "Policy1" + assert result[0].location == "global" + + def test_not_properly_configured_policy(self): + defender_client = mock.MagicMock() + defender_client.audited_tenant = "audited_tenant" + defender_client.audited_domain = DOMAIN + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_m365_provider(), + ), + mock.patch( + "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" + ), + mock.patch( + "prowler.providers.m365.services.defender.defender_antispam_outbound_policy_configured.defender_antispam_outbound_policy_configured.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_configured.defender_antispam_outbound_policy_configured import ( + defender_antispam_outbound_policy_configured, + ) + from prowler.providers.m365.services.defender.defender_service import ( + OutboundSpamPolicy, + OutboundSpamRule, + ) + + defender_client.outbound_spam_policies = { + "Policy2": OutboundSpamPolicy( + notify_sender_blocked=False, + notify_limit_exceeded=False, + notify_limit_exceeded_addresses=[], + notify_sender_blocked_addresses=[], + default=False, + ) + } + defender_client.outbound_spam_rules = { + "Policy2": OutboundSpamRule(state="Enabled") + } + + check = defender_antispam_outbound_policy_configured() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Outbound Spam Policy Policy2 is not properly configured." + ) + assert ( + result[0].resource + == defender_client.outbound_spam_policies["Policy2"].dict() + ) + assert result[0].resource_name == "Defender Outbound Spam Policy" + assert result[0].resource_id == "Policy2" + assert result[0].location == "global" + + def test_properly_configured_default_policy(self): + defender_client = mock.MagicMock() + defender_client.audited_tenant = "audited_tenant" + defender_client.audited_domain = DOMAIN + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_m365_provider(), + ), + mock.patch( + "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" + ), + mock.patch( + "prowler.providers.m365.services.defender.defender_antispam_outbound_policy_configured.defender_antispam_outbound_policy_configured.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_configured.defender_antispam_outbound_policy_configured import ( + defender_antispam_outbound_policy_configured, + ) + from prowler.providers.m365.services.defender.defender_service import ( + OutboundSpamPolicy, + ) + + defender_client.outbound_spam_policies = { + "Default": OutboundSpamPolicy( + notify_sender_blocked=True, + notify_limit_exceeded=True, + notify_limit_exceeded_addresses=["test@correo.com"], + notify_sender_blocked_addresses=["test@correo.com"], + default=True, + ) + } + defender_client.outbound_spam_rules = {} + + check = defender_antispam_outbound_policy_configured() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Outbound Spam Policy Default is properly configured and enabled." + ) + assert ( + result[0].resource + == defender_client.outbound_spam_policies["Default"].dict() + ) + assert result[0].resource_name == "Defender Outbound Spam Policy" + assert result[0].resource_id == "Default" + assert result[0].location == "global" + + def test_policy_without_rule(self): + defender_client = mock.MagicMock() + defender_client.audited_tenant = "audited_tenant" + defender_client.audited_domain = DOMAIN + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_m365_provider(), + ), + mock.patch( + "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" + ), + mock.patch( + "prowler.providers.m365.services.defender.defender_antispam_outbound_policy_configured.defender_antispam_outbound_policy_configured.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_configured.defender_antispam_outbound_policy_configured import ( + defender_antispam_outbound_policy_configured, + ) + from prowler.providers.m365.services.defender.defender_service import ( + OutboundSpamPolicy, + ) + + defender_client.outbound_spam_policies = { + "PolicyX": OutboundSpamPolicy( + notify_sender_blocked=True, + notify_limit_exceeded=True, + notify_limit_exceeded_addresses=["admin@org.com"], + notify_sender_blocked_addresses=["admin@org.com"], + default=False, + ) + } + defender_client.outbound_spam_rules = {} + + check = defender_antispam_outbound_policy_configured() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Outbound Spam Policy PolicyX is not properly configured." + ) + assert ( + result[0].resource + == defender_client.outbound_spam_policies["PolicyX"].dict() + ) + assert result[0].resource_name == "Defender Outbound Spam Policy" + assert result[0].resource_id == "PolicyX" + assert result[0].location == "global" + + def test_no_outbound_spam_policies(self): + defender_client = mock.MagicMock() + defender_client.audited_tenant = "audited_tenant" + defender_client.audited_domain = DOMAIN + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_m365_provider(), + ), + mock.patch( + "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" + ), + mock.patch( + "prowler.providers.m365.services.defender.defender_antispam_outbound_policy_configured.defender_antispam_outbound_policy_configured.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_configured.defender_antispam_outbound_policy_configured import ( + defender_antispam_outbound_policy_configured, + ) + + defender_client.outbound_spam_policies = {} + defender_client.outbound_spam_rules = {} + + check = defender_antispam_outbound_policy_configured() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/m365/services/defender/defender_malware_policy_common_attachments_filter_enabled/defender_malware_policy_common_attachments_filter_enabled_test.py b/tests/providers/m365/services/defender/defender_malware_policy_common_attachments_filter_enabled/defender_malware_policy_common_attachments_filter_enabled_test.py index 1e06abb97b..f416559e35 100644 --- a/tests/providers/m365/services/defender/defender_malware_policy_common_attachments_filter_enabled/defender_malware_policy_common_attachments_filter_enabled_test.py +++ b/tests/providers/m365/services/defender/defender_malware_policy_common_attachments_filter_enabled/defender_malware_policy_common_attachments_filter_enabled_test.py @@ -26,11 +26,11 @@ class Test_defender_malware_policy_common_attachments_filter_enabled: defender_malware_policy_common_attachments_filter_enabled, ) from prowler.providers.m365.services.defender.defender_service import ( - DefenderMalwarePolicy, + MalwarePolicy, ) defender_client.malware_policies = [ - DefenderMalwarePolicy( + MalwarePolicy( enable_file_filter=False, identity="Policy1", enable_internal_sender_admin_notifications=False, @@ -74,11 +74,11 @@ class Test_defender_malware_policy_common_attachments_filter_enabled: defender_malware_policy_common_attachments_filter_enabled, ) from prowler.providers.m365.services.defender.defender_service import ( - DefenderMalwarePolicy, + MalwarePolicy, ) defender_client.malware_policies = [ - DefenderMalwarePolicy( + MalwarePolicy( enable_file_filter=True, identity="Policy1", enable_internal_sender_admin_notifications=False, diff --git a/tests/providers/m365/services/defender/defender_malware_policy_notifications_internal_users_malware_enabled/defender_malware_policy_notifications_internal_users_malware_enabled_test.py b/tests/providers/m365/services/defender/defender_malware_policy_notifications_internal_users_malware_enabled/defender_malware_policy_notifications_internal_users_malware_enabled_test.py index db84249c10..0b34cc044a 100644 --- a/tests/providers/m365/services/defender/defender_malware_policy_notifications_internal_users_malware_enabled/defender_malware_policy_notifications_internal_users_malware_enabled_test.py +++ b/tests/providers/m365/services/defender/defender_malware_policy_notifications_internal_users_malware_enabled/defender_malware_policy_notifications_internal_users_malware_enabled_test.py @@ -26,11 +26,11 @@ class Test_defender_malware_policy_notifications_internal_users_malware_enabled: defender_malware_policy_notifications_internal_users_malware_enabled, ) from prowler.providers.m365.services.defender.defender_service import ( - DefenderMalwarePolicy, + MalwarePolicy, ) defender_client.malware_policies = [ - DefenderMalwarePolicy( + MalwarePolicy( enable_file_filter=True, identity="Default", enable_internal_sender_admin_notifications=False, @@ -76,11 +76,11 @@ class Test_defender_malware_policy_notifications_internal_users_malware_enabled: defender_malware_policy_notifications_internal_users_malware_enabled, ) from prowler.providers.m365.services.defender.defender_service import ( - DefenderMalwarePolicy, + MalwarePolicy, ) defender_client.malware_policies = [ - DefenderMalwarePolicy( + MalwarePolicy( enable_file_filter=True, identity="Default", enable_internal_sender_admin_notifications=True, @@ -126,11 +126,11 @@ class Test_defender_malware_policy_notifications_internal_users_malware_enabled: defender_malware_policy_notifications_internal_users_malware_enabled, ) from prowler.providers.m365.services.defender.defender_service import ( - DefenderMalwarePolicy, + MalwarePolicy, ) defender_client.malware_policies = [ - DefenderMalwarePolicy( + MalwarePolicy( enable_file_filter=True, identity="Default", enable_internal_sender_admin_notifications=True, diff --git a/tests/providers/m365/services/defender/m365_defender_service_test.py b/tests/providers/m365/services/defender/m365_defender_service_test.py index 3e61bb0673..6a9b881d31 100644 --- a/tests/providers/m365/services/defender/m365_defender_service_test.py +++ b/tests/providers/m365/services/defender/m365_defender_service_test.py @@ -6,20 +6,22 @@ from prowler.providers.m365.services.defender.defender_service import ( AntiphishingPolicy, AntiphishingRule, Defender, - DefenderMalwarePolicy, + MalwarePolicy, + OutboundSpamPolicy, + OutboundSpamRule, ) from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider def mock_defender_get_malware_filter_policy(_): return [ - DefenderMalwarePolicy( + MalwarePolicy( enable_file_filter=False, identity="Policy1", enable_internal_sender_admin_notifications=False, internal_sender_admin_address="", ), - DefenderMalwarePolicy( + MalwarePolicy( enable_file_filter=True, identity="Policy2", enable_internal_sender_admin_notifications=True, @@ -66,6 +68,36 @@ def mock_defender_get_antiphising_rules(_): } +def mock_defender_get_outbound_spam_filter_policy(_): + return { + "Policy1": OutboundSpamPolicy( + notify_sender_blocked=True, + notify_limit_exceeded=True, + notify_limit_exceeded_addresses=["security@example.com"], + notify_sender_blocked_addresses=["security@example.com"], + default=False, + ), + "Policy2": OutboundSpamPolicy( + notify_sender_blocked=False, + notify_limit_exceeded=False, + notify_limit_exceeded_addresses=[], + notify_sender_blocked_addresses=[], + default=True, + ), + } + + +def mock_defender_get_outbound_spam_filter_rule(_): + return { + "Policy1": OutboundSpamRule( + state="Enabled", + ), + "Policy2": OutboundSpamRule( + state="Disabled", + ), + } + + class Test_Defender_Service: def test_get_client(self): with ( @@ -177,3 +209,57 @@ class Test_Defender_Service: assert antiphishing_rules["Policy1"].state == "Enabled" assert antiphishing_rules["Policy2"].state == "Disabled" defender_client.powershell.close() + + @patch( + "prowler.providers.m365.services.defender.defender_service.Defender._get_outbound_spam_filter_policy", + new=mock_defender_get_outbound_spam_filter_policy, + ) + def test_get_outbound_spam_filter_policy(self): + with ( + mock.patch( + "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" + ), + ): + defender_client = Defender( + set_mocked_m365_provider( + identity=M365IdentityInfo(tenant_domain=DOMAIN) + ) + ) + outbound_spam_policies = defender_client.outbound_spam_policies + assert outbound_spam_policies["Policy1"].notify_sender_blocked is True + assert outbound_spam_policies["Policy1"].notify_limit_exceeded is True + assert outbound_spam_policies[ + "Policy1" + ].notify_limit_exceeded_addresses == ["security@example.com"] + assert outbound_spam_policies[ + "Policy1" + ].notify_sender_blocked_addresses == ["security@example.com"] + assert outbound_spam_policies["Policy1"].default is False + assert outbound_spam_policies["Policy2"].notify_sender_blocked is False + assert outbound_spam_policies["Policy2"].notify_limit_exceeded is False + assert ( + outbound_spam_policies["Policy2"].notify_limit_exceeded_addresses == [] + ) + assert ( + outbound_spam_policies["Policy2"].notify_sender_blocked_addresses == [] + ) + assert outbound_spam_policies["Policy2"].default is True + + @patch( + "prowler.providers.m365.services.defender.defender_service.Defender._get_outbound_spam_filter_rule", + new=mock_defender_get_outbound_spam_filter_rule, + ) + def test_get_outbound_spam_filter_rule(self): + with ( + mock.patch( + "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" + ), + ): + defender_client = Defender( + set_mocked_m365_provider( + identity=M365IdentityInfo(tenant_domain=DOMAIN) + ) + ) + outbound_spam_rules = defender_client.outbound_spam_rules + assert outbound_spam_rules["Policy1"].state == "Enabled" + assert outbound_spam_rules["Policy2"].state == "Disabled"