diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index a2257479ad..bd5107984e 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -45,6 +45,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - Add new check for Auditing Mailbox on E3 users is enabled for Exchange in M365 [(#7642)](https://github.com/prowler-cloud/prowler/pull/7642) - Add new check for SMTP Auth disabled for Exchange in M365 [(#7640)](https://github.com/prowler-cloud/prowler/pull/7640) - Add new check for MailTips full enabled for Exchange in M365 [(#7637)](https://github.com/prowler-cloud/prowler/pull/7637) +- Add new check for Comprehensive Attachments Filter Applied for Defender in M365 [(#7661)](https://github.com/prowler-cloud/prowler/pull/7661) - Modified check `exchange_mailbox_properties_auditing_enabled` to make it configurable [(#7662)](https://github.com/prowler-cloud/prowler/pull/7662) ### Fixed diff --git a/prowler/config/config.yaml b/prowler/config/config.yaml index 033b2f2d3e..c4bcd40b8e 100644 --- a/prowler/config/config.yaml +++ b/prowler/config/config.yaml @@ -500,6 +500,18 @@ m365: # Exchange # m365.exchange_organization_mailtips_enabled recommended_mailtips_large_audience_threshold: 25 # maximum number of recipients + # Defender Malware Policy Settings + # m365.defender_malware_policy_comprehensive_attachments_filter_applied + # The recommended list of file extensions to be blocked, this can be changed depending on the organization needs + default_recommended_extensions: + [ + "ace", "ani", "apk", "app", "appx", "arj", "bat", "cab", "cmd", "com", + "deb", "dex", "dll", "docm", "elf", "exe", "hta", "img", "iso", "jar", + "jnlp", "kext", "lha", "lib", "library", "lnk", "lzh", "macho", "msc", + "msi", "msix", "msp", "mst", "pif", "ppa", "ppam", "reg", "rev", "scf", + "scr", "sct", "sys", "uif", "vb", "vbe", "vbs", "vxd", "wsc", "wsf", + "wsh", "xll", "xz", "z" + ] # m365.exchange_mailbox_properties_auditing_enabled # Maximum number of days to keep audit logs audit_log_age: 90 diff --git a/prowler/providers/m365/lib/powershell/m365_powershell.py b/prowler/providers/m365/lib/powershell/m365_powershell.py index 9bf39060c3..0fb24f6069 100644 --- a/prowler/providers/m365/lib/powershell/m365_powershell.py +++ b/prowler/providers/m365/lib/powershell/m365_powershell.py @@ -249,6 +249,24 @@ class M365PowerShell(PowerShellSession): """ return self.execute("Get-MalwareFilterPolicy | ConvertTo-Json", json_parse=True) + def get_malware_filter_rule(self) -> dict: + """ + Get Defender Malware Filter Rule. + + Retrieves the current Defender anti-malware filter rule settings. + + Returns: + dict: Malware filter rule settings in JSON format. + + Example: + >>> get_malware_filter_rule() + { + "Name": "Default", + "State": "Enabled" + } + """ + return self.execute("Get-MalwareFilterRule | ConvertTo-Json", json_parse=True) + def get_outbound_spam_filter_policy(self) -> dict: """ Get Defender Outbound Spam Filter Policy. diff --git a/prowler/providers/m365/services/defender/defender_malware_policy_comprehensive_attachments_filter_applied/__init__.py b/prowler/providers/m365/services/defender/defender_malware_policy_comprehensive_attachments_filter_applied/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/m365/services/defender/defender_malware_policy_comprehensive_attachments_filter_applied/defender_malware_policy_comprehensive_attachments_filter_applied.metadata.json b/prowler/providers/m365/services/defender/defender_malware_policy_comprehensive_attachments_filter_applied/defender_malware_policy_comprehensive_attachments_filter_applied.metadata.json new file mode 100644 index 0000000000..189076af60 --- /dev/null +++ b/prowler/providers/m365/services/defender/defender_malware_policy_comprehensive_attachments_filter_applied/defender_malware_policy_comprehensive_attachments_filter_applied.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "m365", + "CheckID": "defender_malware_policy_comprehensive_attachments_filter_applied", + "CheckTitle": "Ensure the Common Attachment Types Filter is enabled and applied in a comprehensive way", + "CheckType": [], + "ServiceName": "defender", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "Defender Malware Policy", + "Description": "Ensure that the Common Attachment Types Filter is enabled in all enabled anti-malware policies in a Comprehensive way to block known and custom malicious file types from being attached to emails. This means that the file types that the filter blocks are checked by the organization, by default all the default file types from M365 defender should be blocked but you can change that with the config file.", + "Risk": "If this setting or the policy is not enabled, users may receive emails with malicious attachments that could contain malware, increasing the risk of endpoint infection or data compromise.", + "RelatedUrl": "https://learn.microsoft.com/en-us/defender-office-365/anti-malware-protection-about?view=o365-worldwide#common-attachments-filter-in-anti-malware-policies", + "Remediation": { + "Code": { + "CLI": "$Policy = @{Name = 'CIS L2 Attachment Policy'; EnableFileFilter = $true; }; $L2Extensions = @('ace','ani','apk','app','appx','arj','bat','cab','cmd','com','deb','dex','dll','docm','elf','exe','hta','img','iso','jar','jnlp','kext','lha','lib','library','lnk','lzh','macho','msc','msi','msix','msp','mst','pif','ppa','ppam','reg','rev','scf','scr','sct','sys','uif','vb','vbe','vbs','vxd','wsc','wsf','wsh','xll','xz','z'); New-MalwareFilterPolicy @Policy -FileTypes $L2Extensions; $Rule = @{Name = $Policy.Name; Enabled = $false; MalwareFilterPolicy = $Policy.Name; Priority = 0}; New-MalwareFilterRule @Rule", + "NativeIaC": "", + "Other": "1. Navigate to Microsoft 365 Defender https://security.microsoft.com. 2. Click to expand Email & collaboration and select Policies & rules. 3. On the Policies & rules page select Threat policies. 4. Under Policies, select Anti-malware and click on the Default (Default) policy. 5. On the policy page, scroll to the bottom and click Edit protection settings. 6. Check the option Enable the common attachments filter. 7. Click on select file types and select the file types you want to block. 8. Click Save. 9. Ensure the status of the policy is On", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable the common attachment types filter in your default or custom anti-malware policy to prevent the delivery of emails with potentially dangerous attachments.", + "Url": "https://learn.microsoft.com/en-us/powershell/module/exchange/set-malwarefilterpolicy?view=exchange-ps" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/m365/services/defender/defender_malware_policy_comprehensive_attachments_filter_applied/defender_malware_policy_comprehensive_attachments_filter_applied.py b/prowler/providers/m365/services/defender/defender_malware_policy_comprehensive_attachments_filter_applied/defender_malware_policy_comprehensive_attachments_filter_applied.py new file mode 100644 index 0000000000..cd2682d538 --- /dev/null +++ b/prowler/providers/m365/services/defender/defender_malware_policy_comprehensive_attachments_filter_applied/defender_malware_policy_comprehensive_attachments_filter_applied.py @@ -0,0 +1,143 @@ +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_malware_policy_comprehensive_attachments_filter_applied(Check): + """ + Verifies if the Common Attachment Types Filter is enabled in the Defender anti-malware policy in a comprehensive way. + + Attributes: + metadata: Metadata associated with the check (inherited from Check). + """ + + def execute(self) -> List[CheckReportM365]: + """ + Executes the check to determine if the Common Attachment Types Filter is enabled. + + This method evaluates the Defender anti-malware policy to ensure it is enabled and the + Common Attachment Types Filter is active and applied to the recommended file types. + + Returns: + List[CheckReportM365]: A list of reports with the results of the check. + """ + findings = [] + + # Default to Microsoft-recommended common file types + default_recommended_extensions = [ + "ace", + "ani", + "apk", + "app", + "appx", + "arj", + "bat", + "cab", + "cmd", + "com", + "deb", + "dex", + "dll", + "docm", + "elf", + "exe", + "hta", + "img", + "iso", + "jar", + "jnlp", + "kext", + "lha", + "lib", + "library", + "lnk", + "lzh", + "macho", + "msc", + "msi", + "msix", + "msp", + "mst", + "pif", + "ppa", + "ppam", + "reg", + "rev", + "scf", + "scr", + "sct", + "sys", + "uif", + "vb", + "vbe", + "vbs", + "vxd", + "wsc", + "wsf", + "wsh", + "xll", + "xz", + "z", + ] + + # Load extensions from audit_config (user config), fallback to default + recommended_extensions = defender_client.audit_config.get( + "recommended_blocked_file_types", default_recommended_extensions + ) + + if not defender_client.malware_policies: + report = CheckReportM365( + metadata=self.metadata(), + resource={}, + resource_name="Defender Malware Policy", + resource_id="defenderMalwarePolicy", + ) + report.status = "FAIL" + report.status_extended = "Common Attachment Types Filter is not enabled." + findings.append(report) + else: + for policy in defender_client.malware_policies: + report = CheckReportM365( + metadata=self.metadata(), + resource=policy, + resource_name="Defender Malware Policy", + resource_id="defenderMalwarePolicy", + ) + report.status = "FAIL" + report.status_extended = f"Common Attachment Types Filter is not properly configured in anti-malware policy {policy.identity}." + + if not policy.enable_file_filter: + report.status_extended = f"Common Attachment Types Filter is not enabled in anti-malware policy {policy.identity}." + break + + if ( + not policy.is_default + and policy.identity in defender_client.malware_rules + ) or policy.is_default: + if ( + not policy.is_default + and defender_client.malware_rules[policy.identity].state.lower() + == "enabled" + ) or policy.is_default: + blocked_extensions = [ext.lower() for ext in policy.file_types] + missing = [ + ext + for ext in recommended_extensions + if ext.lower() not in blocked_extensions + ] + + if missing: + report.status_extended = f"Common Attachment Types Filter is enabled in anti-malware policy {policy.identity}, but the following recommended file types are missing: {', '.join(missing)}." + break + + report.status = "PASS" + report.status_extended = f"Common Attachment Types Filter is enabled in anti-malware policy {policy.identity}, the policy is enabled and the filter is applied to the recommended file types." + + else: + report.status_extended = f"Common Attachment Types Filter is enabled in anti-malware policy {policy.identity}, but the policy is disabled." + break + + 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 875d78f622..b3a8db760c 100644 --- a/prowler/providers/m365/services/defender/defender_service.py +++ b/prowler/providers/m365/services/defender/defender_service.py @@ -22,6 +22,7 @@ class Defender(M365Service): if self.powershell: self.powershell.connect_exchange_online() self.malware_policies = self._get_malware_filter_policy() + self.malware_rules = self._get_malware_filter_rule() 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() @@ -50,6 +51,8 @@ class Defender(M365Service): internal_sender_admin_address=policy.get( "InternalSenderAdminAddress", "" ), + file_types=policy.get("FileTypes", []), + is_default=policy.get("IsDefault", False), ) ) except Exception as error: @@ -58,6 +61,24 @@ class Defender(M365Service): ) return malware_policies + def _get_malware_filter_rule(self): + logger.info("Microsoft365 - Getting Defender malware filter rule...") + malware_rules = {} + try: + malware_rule = self.powershell.get_malware_filter_rule() + if isinstance(malware_rule, dict): + malware_rule = [malware_rule] + for rule in malware_rule: + if rule: + malware_rules[rule.get("Name", "")] = MalwareRule( + state=rule.get("State", ""), + ) + except Exception as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return malware_rules + def _get_antiphising_policy(self): logger.info("Microsoft365 - Getting Defender antiphishing policy...") antiphishing_policies = {} @@ -222,6 +243,12 @@ class MalwarePolicy(BaseModel): identity: str enable_internal_sender_admin_notifications: bool internal_sender_admin_address: str + file_types: list[str] + is_default: bool + + +class MalwareRule(BaseModel): + state: str class AntiphishingPolicy(BaseModel): diff --git a/tests/config/fixtures/config.yaml b/tests/config/fixtures/config.yaml index 9d8d713f20..b93d38fda6 100644 --- a/tests/config/fixtures/config.yaml +++ b/tests/config/fixtures/config.yaml @@ -448,6 +448,18 @@ m365: # Exchange Organization Settings # m365.exchange_organization_mailtips_enabled recommended_mailtips_large_audience_threshold: 25 # maximum number of recipients + # Defender Malware Policy Settings + # m365.defender_malware_policy_comprehensive_attachments_filter_applied + # The recommended list of file extensions to be blocked, this can be changed depending on the organization needs + default_recommended_extensions: + [ + "ace", "ani", "apk", "app", "appx", "arj", "bat", "cab", "cmd", "com", + "deb", "dex", "dll", "docm", "elf", "exe", "hta", "img", "iso", "jar", + "jnlp", "kext", "lha", "lib", "library", "lnk", "lzh", "macho", "msc", + "msi", "msix", "msp", "mst", "pif", "ppa", "ppam", "reg", "rev", "scf", + "scr", "sct", "sys", "uif", "vb", "vbe", "vbs", "vxd", "wsc", "wsf", + "wsh", "xll", "xz", "z" + ] # Exchange Mailbox Settings # m365.exchange_mailbox_properties_auditing_enabled audit_log_age: 90 # maximum number of days to keep audit logs 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 f416559e35..d7cbb59d9e 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 @@ -35,6 +35,8 @@ class Test_defender_malware_policy_common_attachments_filter_enabled: identity="Policy1", enable_internal_sender_admin_notifications=False, internal_sender_admin_address="", + file_types=[], + is_default=True, ), ] @@ -83,6 +85,8 @@ class Test_defender_malware_policy_common_attachments_filter_enabled: identity="Policy1", enable_internal_sender_admin_notifications=False, internal_sender_admin_address="", + file_types=[], + is_default=True, ), ] diff --git a/tests/providers/m365/services/defender/defender_malware_policy_comprehensive_attachments_filter_applied/defender_malware_policy_comprehensive_attachments_filter_applied_test.py b/tests/providers/m365/services/defender/defender_malware_policy_comprehensive_attachments_filter_applied/defender_malware_policy_comprehensive_attachments_filter_applied_test.py new file mode 100644 index 0000000000..9977de9a54 --- /dev/null +++ b/tests/providers/m365/services/defender/defender_malware_policy_comprehensive_attachments_filter_applied/defender_malware_policy_comprehensive_attachments_filter_applied_test.py @@ -0,0 +1,259 @@ +from unittest import mock + +from prowler.providers.m365.services.defender.defender_service import ( + MalwarePolicy, + MalwareRule, +) +from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider + + +class Test_defender_malware_policy_comprehensive_attachments_filter_applied: + def test_no_policy(self): + defender_client = mock.MagicMock() + defender_client.audited_tenant = "audited_tenant" + defender_client.audited_domain = DOMAIN + defender_client.malware_policies = [] + defender_client.audit_config = {} + defender_client.malware_rules = {} + + 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_malware_policy_comprehensive_attachments_filter_applied.defender_malware_policy_comprehensive_attachments_filter_applied.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_malware_policy_comprehensive_attachments_filter_applied.defender_malware_policy_comprehensive_attachments_filter_applied import ( + defender_malware_policy_comprehensive_attachments_filter_applied, + ) + + check = defender_malware_policy_comprehensive_attachments_filter_applied() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Common Attachment Types Filter is not enabled." + ) + assert result[0].resource == {} + assert result[0].resource_name == "Defender Malware Policy" + assert result[0].resource_id == "defenderMalwarePolicy" + assert result[0].location == "global" + + def test_policy_enabled_all_extensions_blocked(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_malware_policy_comprehensive_attachments_filter_applied.defender_malware_policy_comprehensive_attachments_filter_applied.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_malware_policy_comprehensive_attachments_filter_applied.defender_malware_policy_comprehensive_attachments_filter_applied import ( + defender_malware_policy_comprehensive_attachments_filter_applied, + ) + + valid_extensions = ["exe", "bat", "js"] + defender_client.audit_config = { + "recommended_blocked_file_types": valid_extensions + } + defender_client.malware_rules = {"PolicyGood": MalwareRule(state="Enabled")} + defender_client.malware_policies = [ + MalwarePolicy( + enable_file_filter=True, + identity="PolicyGood", + enable_internal_sender_admin_notifications=True, + internal_sender_admin_address="admin@example.com", + file_types=valid_extensions, + is_default=True, + ) + ] + + check = defender_malware_policy_comprehensive_attachments_filter_applied() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Common Attachment Types Filter is enabled in anti-malware policy PolicyGood, the policy is enabled and the filter is applied to the recommended file types." + ) + assert result[0].resource == defender_client.malware_policies[0].dict() + assert result[0].resource_name == "Defender Malware Policy" + assert result[0].resource_id == "defenderMalwarePolicy" + assert result[0].location == "global" + + def test_policy_enabled_missing_extensions(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_malware_policy_comprehensive_attachments_filter_applied.defender_malware_policy_comprehensive_attachments_filter_applied.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_malware_policy_comprehensive_attachments_filter_applied.defender_malware_policy_comprehensive_attachments_filter_applied import ( + defender_malware_policy_comprehensive_attachments_filter_applied, + ) + + defender_client.audit_config = { + "recommended_blocked_file_types": ["exe", "bat", "js"] + } + defender_client.malware_rules = { + "PolicyPartial": MalwareRule(state="Enabled") + } + defender_client.malware_policies = [ + MalwarePolicy( + enable_file_filter=True, + identity="PolicyPartial", + enable_internal_sender_admin_notifications=True, + internal_sender_admin_address="admin@example.com", + file_types=["exe"], + is_default=True, + ) + ] + + check = defender_malware_policy_comprehensive_attachments_filter_applied() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Common Attachment Types Filter is enabled in anti-malware policy PolicyPartial, but the following recommended file types are missing: bat, js." + ) + assert result[0].resource == defender_client.malware_policies[0].dict() + assert result[0].resource_name == "Defender Malware Policy" + assert result[0].resource_id == "defenderMalwarePolicy" + assert result[0].location == "global" + + def test_policy_enabled_but_rule_disabled(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_malware_policy_comprehensive_attachments_filter_applied.defender_malware_policy_comprehensive_attachments_filter_applied.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_malware_policy_comprehensive_attachments_filter_applied.defender_malware_policy_comprehensive_attachments_filter_applied import ( + defender_malware_policy_comprehensive_attachments_filter_applied, + ) + + valid_extensions = ["exe"] + defender_client.audit_config = { + "recommended_blocked_file_types": valid_extensions + } + defender_client.malware_rules = { + "PolicyDisabled": MalwareRule(state="Disabled") + } + defender_client.malware_policies = [ + MalwarePolicy( + enable_file_filter=True, + identity="PolicyDisabled", + enable_internal_sender_admin_notifications=True, + internal_sender_admin_address="admin@example.com", + file_types=valid_extensions, + is_default=False, + ) + ] + + check = defender_malware_policy_comprehensive_attachments_filter_applied() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Common Attachment Types Filter is enabled in anti-malware policy PolicyDisabled, but the policy is disabled." + ) + assert result[0].resource == defender_client.malware_policies[0].dict() + assert result[0].resource_name == "Defender Malware Policy" + assert result[0].resource_id == "defenderMalwarePolicy" + assert result[0].location == "global" + + def test_policy_enabled_but_no_rule_and_not_default(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_malware_policy_comprehensive_attachments_filter_applied.defender_malware_policy_comprehensive_attachments_filter_applied.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_malware_policy_comprehensive_attachments_filter_applied.defender_malware_policy_comprehensive_attachments_filter_applied import ( + defender_malware_policy_comprehensive_attachments_filter_applied, + ) + + valid_extensions = ["exe"] + defender_client.audit_config = { + "recommended_blocked_file_types": valid_extensions + } + defender_client.malware_rules = {} + defender_client.malware_policies = [ + MalwarePolicy( + enable_file_filter=True, + identity="PolicyNoRule", + enable_internal_sender_admin_notifications=True, + internal_sender_admin_address="admin@example.com", + file_types=valid_extensions, + is_default=False, + ) + ] + + check = defender_malware_policy_comprehensive_attachments_filter_applied() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Common Attachment Types Filter is not properly configured in anti-malware policy PolicyNoRule." + ) + assert result[0].resource == defender_client.malware_policies[0].dict() + assert result[0].resource_name == "Defender Malware Policy" + assert result[0].resource_id == "defenderMalwarePolicy" + assert result[0].location == "global" 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 0b34cc044a..fdd26415d0 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 @@ -35,6 +35,8 @@ class Test_defender_malware_policy_notifications_internal_users_malware_enabled: identity="Default", enable_internal_sender_admin_notifications=False, internal_sender_admin_address="", + file_types=[], + is_default=True, ) ] @@ -85,6 +87,8 @@ class Test_defender_malware_policy_notifications_internal_users_malware_enabled: identity="Default", enable_internal_sender_admin_notifications=True, internal_sender_admin_address="", + file_types=[], + is_default=True, ) ] @@ -135,6 +139,8 @@ class Test_defender_malware_policy_notifications_internal_users_malware_enabled: identity="Default", enable_internal_sender_admin_notifications=True, internal_sender_admin_address="security@example.com", + file_types=[], + is_default=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 4c170d0da9..45035a2952 100644 --- a/tests/providers/m365/services/defender/m365_defender_service_test.py +++ b/tests/providers/m365/services/defender/m365_defender_service_test.py @@ -10,6 +10,7 @@ from prowler.providers.m365.services.defender.defender_service import ( DefenderInboundSpamPolicy, DkimConfig, MalwarePolicy, + MalwareRule, OutboundSpamPolicy, OutboundSpamRule, ) @@ -23,16 +24,27 @@ def mock_defender_get_malware_filter_policy(_): identity="Policy1", enable_internal_sender_admin_notifications=False, internal_sender_admin_address="", + file_types=[], + is_default=True, ), MalwarePolicy( enable_file_filter=True, identity="Policy2", enable_internal_sender_admin_notifications=True, internal_sender_admin_address="security@example.com", + file_types=["exe", "zip"], + is_default=False, ), ] +def mock_defender_get_malware_filter_rule(_): + return { + "Policy1": MalwareRule(state="Enabled"), + "Policy2": MalwareRule(state="Disabled"), + } + + def mock_defender_get_antiphising_policy(_): return { "Policy1": AntiphishingPolicy( @@ -167,6 +179,10 @@ class Test_Defender_Service: malware_policies[0].enable_internal_sender_admin_notifications is False ) assert malware_policies[0].internal_sender_admin_address == "" + assert malware_policies[0].enable_file_filter is False + assert malware_policies[0].identity == "Policy1" + assert malware_policies[0].file_types == [] + assert malware_policies[0].is_default is True assert malware_policies[1].enable_file_filter is True assert malware_policies[1].identity == "Policy2" assert ( @@ -176,6 +192,28 @@ class Test_Defender_Service: malware_policies[1].internal_sender_admin_address == "security@example.com" ) + assert malware_policies[1].file_types == ["exe", "zip"] + assert malware_policies[1].is_default is False + defender_client.powershell.close() + + @patch( + "prowler.providers.m365.services.defender.defender_service.Defender._get_malware_filter_rule", + new=mock_defender_get_malware_filter_rule, + ) + def test__get_malware_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) + ) + ) + malware_rules = defender_client.malware_rules + assert malware_rules["Policy1"].state == "Enabled" + assert malware_rules["Policy2"].state == "Disabled" defender_client.powershell.close() @patch(