mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-08-19 09:30:21 +00:00
feat(m365): add CIS M365 v7.0.0 defender preset policy checks (#12148)
Co-authored-by: Daniel Barranquero <danielbo2001@gmail.com>
This commit is contained in:
co-authored by
Daniel Barranquero
parent
162c6560d9
commit
0f39665ece
@@ -0,0 +1 @@
|
||||
`defender_priority_account_protection_enabled` and `defender_strict_preset_security_policy_enabled` checks for M365 provider, covering CIS Microsoft 365 Foundations Benchmark v7.0.0 controls 2.4.1 and 2.4.2
|
||||
@@ -770,7 +770,9 @@
|
||||
{
|
||||
"Id": "2.4.1",
|
||||
"Description": "Identify priority accounts to utilize Microsoft 365's advanced custom security features. This is an essential tool to bolster protection for users who are frequently targeted due to their critical positions, such as executives, leaders, managers, or others who have access to sensitive, confidential, financial, or high-priority information. Once these accounts are identified, several services and features can be enabled, including threat policies, enhanced sign-in protection through conditional access policies, and alert policies, enabling faster response times for incident response teams.",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"defender_priority_account_protection_enabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2 Microsoft Defender",
|
||||
@@ -791,7 +793,9 @@
|
||||
{
|
||||
"Id": "2.4.2",
|
||||
"Description": "Preset security policies have been established by Microsoft, utilizing observations and experiences within datacenters to strike a balance between the exclusion of malicious content from users and limiting unwarranted disruptions. These policies can apply to all, or select users and encompass recommendations for addressing spam, malware, and phishing threats. The policy parameters are pre-determined and non-adjustable. Strict protection has the most aggressive protection of the 3 presets. - EOP: Anti-spam, Anti-malware and Anti-phishing - Defender: Spoof protection, Impersonation protection and Advanced phishing - Defender: Safe Links and Safe Attachments NOTE: The preset security polices cannot target Priority account TAGS currently, groups should be used instead.",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"defender_strict_preset_security_policy_enabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2 Microsoft Defender",
|
||||
|
||||
@@ -402,6 +402,39 @@ class M365PowerShell(PowerShellSession):
|
||||
"Get-MalwareFilterPolicy | ConvertTo-Json -Depth 10", json_parse=True
|
||||
)
|
||||
|
||||
def get_eop_protection_policy_rule(self) -> dict:
|
||||
"""
|
||||
Get Exchange Online Protection (EOP) preset security policy rules.
|
||||
|
||||
Returns:
|
||||
dict: EOP protection policy rules in JSON format.
|
||||
"""
|
||||
return self.execute(
|
||||
"Get-EOPProtectionPolicyRule | ConvertTo-Json -Depth 10", json_parse=True
|
||||
)
|
||||
|
||||
def get_atp_protection_policy_rule(self) -> dict:
|
||||
"""
|
||||
Get Defender for Office 365 (ATP) preset security policy rules.
|
||||
|
||||
Returns:
|
||||
dict: ATP protection policy rules in JSON format.
|
||||
"""
|
||||
return self.execute(
|
||||
"Get-ATPProtectionPolicyRule | ConvertTo-Json -Depth 10", json_parse=True
|
||||
)
|
||||
|
||||
def get_email_tenant_settings(self) -> dict:
|
||||
"""
|
||||
Get Defender email tenant settings.
|
||||
|
||||
Returns:
|
||||
dict: Email tenant settings (e.g. EnablePriorityAccountProtection).
|
||||
"""
|
||||
return self.execute(
|
||||
"Get-EmailTenantSettings | ConvertTo-Json -Depth 10", json_parse=True
|
||||
)
|
||||
|
||||
def get_malware_filter_rule(self) -> dict:
|
||||
"""
|
||||
Get Defender Malware Filter Rule.
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"Provider": "m365",
|
||||
"CheckID": "defender_priority_account_protection_enabled",
|
||||
"CheckTitle": "Priority account protection is enabled",
|
||||
"CheckType": [],
|
||||
"ServiceName": "defender",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "NotDefined",
|
||||
"ResourceGroup": "security",
|
||||
"Description": "Priority account protection applies enhanced monitoring and protection to high-value accounts. The tenant-level flag **EnablePriorityAccountProtection** (from Get-EmailTenantSettings) should be enabled. This check evaluates the tenant-level enablement flag; tagging priority accounts and configuring alert policies must be verified separately.",
|
||||
"Risk": "Without **priority account** protection, high-value targets such as executives receive the same protection as standard users, despite being far more likely to be targeted by **phishing** and **business email compromise**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://learn.microsoft.com/en-us/defender-office-365/priority-accounts-security-recommendations"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Navigate to Microsoft Defender at https://security.microsoft.com/\n2. Go to **System** > **Settings** > **Email & collaboration** > **Priority account protection**\n3. Set **Priority account protection** to **On**\n4. Tag priority accounts and configure the associated alert policies",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable priority account protection, tag your high-value accounts as priority accounts, and configure the associated alert policies for enhanced monitoring.",
|
||||
"Url": "https://hub.prowler.com/check/defender_priority_account_protection_enabled"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"email-security",
|
||||
"e5"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "Covers the tenant-level EnablePriorityAccountProtection flag only; alert-policy verification (Get-ProtectionAlert) requires a Security & Compliance PowerShell session that Prowler does not currently establish."
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
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_priority_account_protection_enabled(Check):
|
||||
"""Check if priority account protection is enabled.
|
||||
|
||||
Priority account protection applies enhanced monitoring and protection to
|
||||
high-value accounts. Its tenant-level flag ``EnablePriorityAccountProtection``
|
||||
(from Get-EmailTenantSettings) should be enabled.
|
||||
|
||||
Note: This check covers the tenant-level enablement flag only. The full control
|
||||
also requires priority accounts to be tagged and alert policies to be configured,
|
||||
which must be verified manually.
|
||||
|
||||
- PASS: Priority account protection is enabled at the tenant level.
|
||||
- FAIL: Priority account protection is disabled at the tenant level.
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportM365]:
|
||||
"""Execute the priority account protection check.
|
||||
|
||||
Evaluates the tenant-level ``EnablePriorityAccountProtection`` flag from
|
||||
the Defender email tenant settings, producing PASS when enabled and FAIL
|
||||
when disabled. Returns no findings when settings are unavailable.
|
||||
|
||||
Returns:
|
||||
List[CheckReportM365]: A list with the check report, or empty when no
|
||||
email tenant settings are available.
|
||||
"""
|
||||
findings = []
|
||||
settings = defender_client.email_tenant_settings
|
||||
if not settings:
|
||||
return findings
|
||||
|
||||
report = CheckReportM365(
|
||||
metadata=self.metadata(),
|
||||
resource=settings,
|
||||
resource_name="Email Tenant Settings",
|
||||
resource_id="emailTenantSettings",
|
||||
)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"Priority account protection is not enabled at the tenant level."
|
||||
)
|
||||
|
||||
if settings.priority_account_protection_enabled:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
"Priority account protection is enabled at the tenant level."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -59,6 +59,9 @@ class Defender(M365Service):
|
||||
self.safe_links_policies = {}
|
||||
self.safe_links_rules = {}
|
||||
self.teams_protection_policy = None
|
||||
self.eop_protection_policy_rules = None
|
||||
self.atp_protection_policy_rules = None
|
||||
self.email_tenant_settings = None
|
||||
if self.powershell:
|
||||
if self.powershell.connect_exchange_online():
|
||||
self.malware_policies = self._get_malware_filter_policy()
|
||||
@@ -80,8 +83,102 @@ class Defender(M365Service):
|
||||
self.safe_links_policies = self._get_safe_links_policy()
|
||||
self.safe_links_rules = self._get_safe_links_rule()
|
||||
self.teams_protection_policy = self._get_teams_protection_policy()
|
||||
self.eop_protection_policy_rules = (
|
||||
self._get_eop_protection_policy_rules()
|
||||
)
|
||||
self.atp_protection_policy_rules = (
|
||||
self._get_atp_protection_policy_rules()
|
||||
)
|
||||
self.email_tenant_settings = self._get_email_tenant_settings()
|
||||
self.powershell.close()
|
||||
|
||||
def _parse_protection_policy_rules(self, rules_data):
|
||||
"""Parse preset security policy rules into PresetSecurityPolicyRule models."""
|
||||
rules = []
|
||||
if not rules_data:
|
||||
return rules
|
||||
if isinstance(rules_data, dict):
|
||||
rules_data = [rules_data]
|
||||
for rule in rules_data:
|
||||
if rule:
|
||||
rules.append(
|
||||
PresetSecurityPolicyRule(
|
||||
name=rule.get("Name", rule.get("Identity", "")),
|
||||
state=rule.get("State", ""),
|
||||
sent_to=self._normalize_list(rule.get("SentTo")),
|
||||
sent_to_member_of=self._normalize_list(
|
||||
rule.get("SentToMemberOf")
|
||||
),
|
||||
recipient_domain_is=self._normalize_list(
|
||||
rule.get("RecipientDomainIs")
|
||||
),
|
||||
)
|
||||
)
|
||||
return rules
|
||||
|
||||
@staticmethod
|
||||
def _normalize_list(value):
|
||||
"""Normalize a PowerShell scalar/list/None value into a list."""
|
||||
if value is None:
|
||||
return []
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
return [value]
|
||||
|
||||
def _get_eop_protection_policy_rules(self):
|
||||
"""Retrieve the EOP preset security policy rules.
|
||||
|
||||
Returns:
|
||||
Optional[List[PresetSecurityPolicyRule]]: The parsed rules (empty when
|
||||
the tenant has none), or None on error so checks can skip instead of
|
||||
reporting on missing data.
|
||||
"""
|
||||
logger.info("M365 - Getting Defender EOP protection policy rules...")
|
||||
try:
|
||||
return self._parse_protection_policy_rules(
|
||||
self.powershell.get_eop_protection_policy_rule()
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return None
|
||||
|
||||
def _get_atp_protection_policy_rules(self):
|
||||
"""Retrieve the Defender for Office 365 (ATP) preset security policy rules.
|
||||
|
||||
Returns:
|
||||
Optional[List[PresetSecurityPolicyRule]]: The parsed rules (empty when
|
||||
the tenant has none), or None on error so checks can skip instead of
|
||||
reporting on missing data.
|
||||
"""
|
||||
logger.info("M365 - Getting Defender ATP protection policy rules...")
|
||||
try:
|
||||
return self._parse_protection_policy_rules(
|
||||
self.powershell.get_atp_protection_policy_rule()
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return None
|
||||
|
||||
def _get_email_tenant_settings(self):
|
||||
logger.info("M365 - Getting Defender email tenant settings...")
|
||||
try:
|
||||
data = self.powershell.get_email_tenant_settings()
|
||||
if data:
|
||||
return EmailTenantSettings(
|
||||
priority_account_protection_enabled=data.get(
|
||||
"EnablePriorityAccountProtection", False
|
||||
),
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return None
|
||||
|
||||
def _get_malware_filter_policy(self):
|
||||
logger.info("M365 - Getting Defender malware filter policy...")
|
||||
malware_policies = []
|
||||
@@ -826,3 +923,22 @@ class TeamsProtectionPolicy(BaseModel):
|
||||
|
||||
identity: str
|
||||
zap_enabled: bool
|
||||
|
||||
|
||||
class PresetSecurityPolicyRule(BaseModel):
|
||||
"""Model for a preset security policy rule (EOP or ATP).
|
||||
|
||||
Empty recipient conditions mean the rule applies to all recipients.
|
||||
"""
|
||||
|
||||
name: str = ""
|
||||
state: str = ""
|
||||
sent_to: list = []
|
||||
sent_to_member_of: list = []
|
||||
recipient_domain_is: list = []
|
||||
|
||||
|
||||
class EmailTenantSettings(BaseModel):
|
||||
"""Model for Defender email tenant settings."""
|
||||
|
||||
priority_account_protection_enabled: bool = False
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"Provider": "m365",
|
||||
"CheckID": "defender_strict_preset_security_policy_enabled",
|
||||
"CheckTitle": "Strict Preset Security Policy is enabled",
|
||||
"CheckType": [],
|
||||
"ServiceName": "defender",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "NotDefined",
|
||||
"ResourceGroup": "security",
|
||||
"Description": "The **Strict Preset Security Policy** applies Microsoft's recommended strict protection settings and should be enabled for both **Exchange Online Protection** (anti-phishing, anti-spam, anti-malware) and **Defender for Office 365** (Safe Attachments, Safe Links). The rules are exposed via Get-EOPProtectionPolicyRule and Get-ATPProtectionPolicyRule.",
|
||||
"Risk": "Without the **Strict Preset Security Policy** enabled, mailboxes rely on weaker default or custom protection settings, increasing exposure to **phishing**, malware, and malicious links and attachments.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://learn.microsoft.com/en-us/defender-office-365/preset-security-policies"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Navigate to Microsoft Defender at https://security.microsoft.com/\n2. Go to **Email & collaboration** > **Policies & rules** > **Threat policies** > **Preset security policies**\n3. Turn on the **Strict protection** preset and assign it to the appropriate users, groups, or domains (including priority accounts)",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable the Strict Preset Security Policy for both Exchange Online Protection and Defender for Office 365 and assign it to your users and priority accounts.",
|
||||
"Url": "https://hub.prowler.com/check/defender_strict_preset_security_policy_enabled"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"email-security",
|
||||
"e5"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportM365
|
||||
from prowler.providers.m365.services.defender.defender_client import defender_client
|
||||
|
||||
STRICT_PRESET_NAME = "Strict Preset Security Policy"
|
||||
|
||||
|
||||
class defender_strict_preset_security_policy_enabled(Check):
|
||||
"""Check if the Strict Preset Security Policy is enabled for EOP and Defender.
|
||||
|
||||
The Strict Preset Security Policy applies Microsoft's recommended strict
|
||||
protection settings. It should be enabled for both Exchange Online Protection
|
||||
(anti-phishing, anti-spam, anti-malware) and Defender for Office 365 (Safe
|
||||
Attachments, Safe Links). A rule with no recipient conditions applies to all
|
||||
recipients.
|
||||
|
||||
- PASS: The Strict Preset Security Policy is enabled for both EOP and Defender.
|
||||
- FAIL: The Strict Preset Security Policy is not enabled for EOP and/or Defender.
|
||||
"""
|
||||
|
||||
def _has_enabled_strict_preset(self, rules) -> bool:
|
||||
"""Check whether any rule enables the Strict Preset Security Policy.
|
||||
|
||||
A rule qualifies when it is named the Strict Preset Security Policy and is
|
||||
in the ``Enabled`` state. Recipient conditions are not evaluated because
|
||||
empty conditions mean the rule applies to all recipients.
|
||||
|
||||
Args:
|
||||
rules: Iterable of preset security policy rules (EOP or ATP).
|
||||
|
||||
Returns:
|
||||
bool: True if at least one rule enables the Strict Preset Security
|
||||
Policy, False otherwise.
|
||||
"""
|
||||
return any(
|
||||
rule.name == STRICT_PRESET_NAME and rule.state == "Enabled"
|
||||
for rule in rules
|
||||
)
|
||||
|
||||
def execute(self) -> List[CheckReportM365]:
|
||||
"""Execute the Strict Preset Security Policy check.
|
||||
|
||||
Evaluates whether the Strict Preset Security Policy is enabled for both
|
||||
Exchange Online Protection (EOP) and Defender for Office 365 (ATP),
|
||||
producing PASS only when both are enabled. Returns no findings when the
|
||||
policy rules could not be collected.
|
||||
|
||||
Returns:
|
||||
List[CheckReportM365]: A list with the check report, or empty when the
|
||||
preset policy rules are unavailable.
|
||||
"""
|
||||
findings = []
|
||||
eop_rules = defender_client.eop_protection_policy_rules
|
||||
atp_rules = defender_client.atp_protection_policy_rules
|
||||
if eop_rules is None or atp_rules is None:
|
||||
return findings
|
||||
|
||||
report = CheckReportM365(
|
||||
metadata=self.metadata(),
|
||||
resource={
|
||||
"eop": [rule.dict() for rule in eop_rules],
|
||||
"atp": [rule.dict() for rule in atp_rules],
|
||||
},
|
||||
resource_name="Strict Preset Security Policy",
|
||||
resource_id="strictPresetSecurityPolicy",
|
||||
)
|
||||
|
||||
eop_enabled = self._has_enabled_strict_preset(eop_rules)
|
||||
atp_enabled = self._has_enabled_strict_preset(atp_rules)
|
||||
|
||||
if eop_enabled and atp_enabled:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
"The Strict Preset Security Policy is enabled for both Exchange "
|
||||
"Online Protection and Defender for Office 365."
|
||||
)
|
||||
else:
|
||||
missing = []
|
||||
if not eop_enabled:
|
||||
missing.append("Exchange Online Protection")
|
||||
if not atp_enabled:
|
||||
missing.append("Defender for Office 365")
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"The Strict Preset Security Policy is not enabled for "
|
||||
f"{' or '.join(missing)}."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
from unittest import mock
|
||||
|
||||
from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
|
||||
|
||||
|
||||
class Test_defender_priority_account_protection_enabled:
|
||||
def test_defender_no_email_tenant_settings(self):
|
||||
defender_client = mock.MagicMock()
|
||||
defender_client.audited_tenant = "audited_tenant"
|
||||
defender_client.audited_domain = DOMAIN
|
||||
defender_client.email_tenant_settings = None
|
||||
|
||||
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_priority_account_protection_enabled.defender_priority_account_protection_enabled.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.defender.defender_priority_account_protection_enabled.defender_priority_account_protection_enabled import (
|
||||
defender_priority_account_protection_enabled,
|
||||
)
|
||||
|
||||
check = defender_priority_account_protection_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_defender_priority_account_protection_enabled(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_priority_account_protection_enabled.defender_priority_account_protection_enabled.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.defender.defender_priority_account_protection_enabled.defender_priority_account_protection_enabled import (
|
||||
defender_priority_account_protection_enabled,
|
||||
)
|
||||
from prowler.providers.m365.services.defender.defender_service import (
|
||||
EmailTenantSettings,
|
||||
)
|
||||
|
||||
defender_client.email_tenant_settings = EmailTenantSettings(
|
||||
priority_account_protection_enabled=True,
|
||||
)
|
||||
|
||||
check = defender_priority_account_protection_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Priority account protection is enabled at the tenant level."
|
||||
)
|
||||
assert result[0].resource == defender_client.email_tenant_settings.dict()
|
||||
assert result[0].resource_name == "Email Tenant Settings"
|
||||
assert result[0].resource_id == "emailTenantSettings"
|
||||
assert result[0].location == "global"
|
||||
|
||||
def test_defender_priority_account_protection_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_priority_account_protection_enabled.defender_priority_account_protection_enabled.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.defender.defender_priority_account_protection_enabled.defender_priority_account_protection_enabled import (
|
||||
defender_priority_account_protection_enabled,
|
||||
)
|
||||
from prowler.providers.m365.services.defender.defender_service import (
|
||||
EmailTenantSettings,
|
||||
)
|
||||
|
||||
defender_client.email_tenant_settings = EmailTenantSettings(
|
||||
priority_account_protection_enabled=False,
|
||||
)
|
||||
|
||||
check = defender_priority_account_protection_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Priority account protection is not enabled at the tenant level."
|
||||
)
|
||||
assert result[0].resource == defender_client.email_tenant_settings.dict()
|
||||
assert result[0].resource_name == "Email Tenant Settings"
|
||||
assert result[0].resource_id == "emailTenantSettings"
|
||||
assert result[0].location == "global"
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
from unittest import mock
|
||||
|
||||
from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
|
||||
|
||||
|
||||
class Test_defender_strict_preset_security_policy_enabled:
|
||||
def test_defender_no_preset_policy_rules_data(self):
|
||||
defender_client = mock.MagicMock()
|
||||
defender_client.audited_tenant = "audited_tenant"
|
||||
defender_client.audited_domain = DOMAIN
|
||||
defender_client.eop_protection_policy_rules = None
|
||||
defender_client.atp_protection_policy_rules = None
|
||||
|
||||
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_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.defender.defender_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled import (
|
||||
defender_strict_preset_security_policy_enabled,
|
||||
)
|
||||
|
||||
check = defender_strict_preset_security_policy_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_defender_eop_rules_unavailable(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_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.defender.defender_service import (
|
||||
PresetSecurityPolicyRule,
|
||||
)
|
||||
from prowler.providers.m365.services.defender.defender_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled import (
|
||||
defender_strict_preset_security_policy_enabled,
|
||||
)
|
||||
|
||||
defender_client.eop_protection_policy_rules = None
|
||||
defender_client.atp_protection_policy_rules = [
|
||||
PresetSecurityPolicyRule(
|
||||
name="Strict Preset Security Policy",
|
||||
state="Enabled",
|
||||
)
|
||||
]
|
||||
|
||||
check = defender_strict_preset_security_policy_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_defender_strict_preset_enabled_for_both(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_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.defender.defender_service import (
|
||||
PresetSecurityPolicyRule,
|
||||
)
|
||||
from prowler.providers.m365.services.defender.defender_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled import (
|
||||
defender_strict_preset_security_policy_enabled,
|
||||
)
|
||||
|
||||
defender_client.eop_protection_policy_rules = [
|
||||
PresetSecurityPolicyRule(
|
||||
name="Strict Preset Security Policy",
|
||||
state="Enabled",
|
||||
recipient_domain_is=["contoso.com"],
|
||||
)
|
||||
]
|
||||
defender_client.atp_protection_policy_rules = [
|
||||
PresetSecurityPolicyRule(
|
||||
name="Strict Preset Security Policy",
|
||||
state="Enabled",
|
||||
recipient_domain_is=["contoso.com"],
|
||||
)
|
||||
]
|
||||
|
||||
check = defender_strict_preset_security_policy_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "The Strict Preset Security Policy is enabled for both Exchange Online Protection and Defender for Office 365."
|
||||
)
|
||||
assert result[0].resource == {
|
||||
"eop": [
|
||||
rule.dict() for rule in defender_client.eop_protection_policy_rules
|
||||
],
|
||||
"atp": [
|
||||
rule.dict() for rule in defender_client.atp_protection_policy_rules
|
||||
],
|
||||
}
|
||||
assert result[0].resource_name == "Strict Preset Security Policy"
|
||||
assert result[0].resource_id == "strictPresetSecurityPolicy"
|
||||
assert result[0].location == "global"
|
||||
|
||||
def test_defender_strict_preset_enabled_all_recipients(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_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.defender.defender_service import (
|
||||
PresetSecurityPolicyRule,
|
||||
)
|
||||
from prowler.providers.m365.services.defender.defender_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled import (
|
||||
defender_strict_preset_security_policy_enabled,
|
||||
)
|
||||
|
||||
# Empty recipient conditions mean the rule applies to all recipients.
|
||||
defender_client.eop_protection_policy_rules = [
|
||||
PresetSecurityPolicyRule(
|
||||
name="Strict Preset Security Policy",
|
||||
state="Enabled",
|
||||
)
|
||||
]
|
||||
defender_client.atp_protection_policy_rules = [
|
||||
PresetSecurityPolicyRule(
|
||||
name="Strict Preset Security Policy",
|
||||
state="Enabled",
|
||||
)
|
||||
]
|
||||
|
||||
check = defender_strict_preset_security_policy_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "The Strict Preset Security Policy is enabled for both Exchange Online Protection and Defender for Office 365."
|
||||
)
|
||||
|
||||
def test_defender_strict_preset_only_eop(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_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.defender.defender_service import (
|
||||
PresetSecurityPolicyRule,
|
||||
)
|
||||
from prowler.providers.m365.services.defender.defender_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled import (
|
||||
defender_strict_preset_security_policy_enabled,
|
||||
)
|
||||
|
||||
defender_client.eop_protection_policy_rules = [
|
||||
PresetSecurityPolicyRule(
|
||||
name="Strict Preset Security Policy",
|
||||
state="Enabled",
|
||||
)
|
||||
]
|
||||
defender_client.atp_protection_policy_rules = []
|
||||
|
||||
check = defender_strict_preset_security_policy_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "The Strict Preset Security Policy is not enabled for Defender for Office 365."
|
||||
)
|
||||
|
||||
def test_defender_strict_preset_only_atp(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_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.defender.defender_service import (
|
||||
PresetSecurityPolicyRule,
|
||||
)
|
||||
from prowler.providers.m365.services.defender.defender_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled import (
|
||||
defender_strict_preset_security_policy_enabled,
|
||||
)
|
||||
|
||||
defender_client.eop_protection_policy_rules = []
|
||||
defender_client.atp_protection_policy_rules = [
|
||||
PresetSecurityPolicyRule(
|
||||
name="Strict Preset Security Policy",
|
||||
state="Enabled",
|
||||
)
|
||||
]
|
||||
|
||||
check = defender_strict_preset_security_policy_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "The Strict Preset Security Policy is not enabled for Exchange Online Protection."
|
||||
)
|
||||
|
||||
def test_defender_strict_preset_disabled_state(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_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.defender.defender_service import (
|
||||
PresetSecurityPolicyRule,
|
||||
)
|
||||
from prowler.providers.m365.services.defender.defender_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled import (
|
||||
defender_strict_preset_security_policy_enabled,
|
||||
)
|
||||
|
||||
defender_client.eop_protection_policy_rules = [
|
||||
PresetSecurityPolicyRule(
|
||||
name="Strict Preset Security Policy",
|
||||
state="Disabled",
|
||||
)
|
||||
]
|
||||
defender_client.atp_protection_policy_rules = [
|
||||
PresetSecurityPolicyRule(
|
||||
name="Strict Preset Security Policy",
|
||||
state="Disabled",
|
||||
)
|
||||
]
|
||||
|
||||
check = defender_strict_preset_security_policy_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "The Strict Preset Security Policy is not enabled for Exchange Online Protection or Defender for Office 365."
|
||||
)
|
||||
|
||||
def test_defender_standard_preset_only(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_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.defender.defender_service import (
|
||||
PresetSecurityPolicyRule,
|
||||
)
|
||||
from prowler.providers.m365.services.defender.defender_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled import (
|
||||
defender_strict_preset_security_policy_enabled,
|
||||
)
|
||||
|
||||
defender_client.eop_protection_policy_rules = [
|
||||
PresetSecurityPolicyRule(
|
||||
name="Standard Preset Security Policy",
|
||||
state="Enabled",
|
||||
)
|
||||
]
|
||||
defender_client.atp_protection_policy_rules = [
|
||||
PresetSecurityPolicyRule(
|
||||
name="Standard Preset Security Policy",
|
||||
state="Enabled",
|
||||
)
|
||||
]
|
||||
|
||||
check = defender_strict_preset_security_policy_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "The Strict Preset Security Policy is not enabled for Exchange Online Protection or Defender for Office 365."
|
||||
)
|
||||
|
||||
def test_defender_no_preset_rules(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_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.defender.defender_strict_preset_security_policy_enabled.defender_strict_preset_security_policy_enabled import (
|
||||
defender_strict_preset_security_policy_enabled,
|
||||
)
|
||||
|
||||
# Rules were collected successfully but the presets were never enabled.
|
||||
defender_client.eop_protection_policy_rules = []
|
||||
defender_client.atp_protection_policy_rules = []
|
||||
|
||||
check = defender_strict_preset_security_policy_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "The Strict Preset Security Policy is not enabled for Exchange Online Protection or Defender for Office 365."
|
||||
)
|
||||
assert result[0].resource == {"eop": [], "atp": []}
|
||||
assert result[0].resource_name == "Strict Preset Security Policy"
|
||||
assert result[0].resource_id == "strictPresetSecurityPolicy"
|
||||
assert result[0].location == "global"
|
||||
@@ -554,3 +554,100 @@ class Test_Defender_Service:
|
||||
assert report_submission_policy.report_not_junk_addresses == []
|
||||
assert report_submission_policy.report_phish_addresses == []
|
||||
assert report_submission_policy.report_chat_message_enabled is True
|
||||
|
||||
@patch(
|
||||
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.get_eop_protection_policy_rule",
|
||||
return_value=[
|
||||
{
|
||||
"Name": "Standard Preset Security Policy",
|
||||
"State": "Disabled",
|
||||
"SentTo": None,
|
||||
"SentToMemberOf": None,
|
||||
"RecipientDomainIs": "contoso.com",
|
||||
},
|
||||
{
|
||||
"Name": "Strict Preset Security Policy",
|
||||
"State": "Enabled",
|
||||
"SentTo": ["user@contoso.com"],
|
||||
"SentToMemberOf": None,
|
||||
"RecipientDomainIs": None,
|
||||
},
|
||||
],
|
||||
)
|
||||
def test__get_eop_protection_policy_rules(self, _mock_get_eop_rules):
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online",
|
||||
return_value=True,
|
||||
),
|
||||
):
|
||||
defender_client = Defender(
|
||||
set_mocked_m365_provider(
|
||||
identity=M365IdentityInfo(tenant_domain=DOMAIN)
|
||||
)
|
||||
)
|
||||
eop_rules = defender_client.eop_protection_policy_rules
|
||||
assert len(eop_rules) == 2
|
||||
assert eop_rules[0].name == "Standard Preset Security Policy"
|
||||
assert eop_rules[0].state == "Disabled"
|
||||
assert eop_rules[0].sent_to == []
|
||||
assert eop_rules[0].recipient_domain_is == ["contoso.com"]
|
||||
assert eop_rules[1].name == "Strict Preset Security Policy"
|
||||
assert eop_rules[1].state == "Enabled"
|
||||
assert eop_rules[1].sent_to == ["user@contoso.com"]
|
||||
assert eop_rules[1].recipient_domain_is == []
|
||||
defender_client.powershell.close()
|
||||
|
||||
@patch(
|
||||
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.get_atp_protection_policy_rule",
|
||||
return_value={
|
||||
"Name": "Strict Preset Security Policy",
|
||||
"State": "Enabled",
|
||||
"SentTo": None,
|
||||
"SentToMemberOf": None,
|
||||
"RecipientDomainIs": None,
|
||||
},
|
||||
)
|
||||
def test__get_atp_protection_policy_rules_single_dict(self, _mock_get_atp_rules):
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online",
|
||||
return_value=True,
|
||||
),
|
||||
):
|
||||
defender_client = Defender(
|
||||
set_mocked_m365_provider(
|
||||
identity=M365IdentityInfo(tenant_domain=DOMAIN)
|
||||
)
|
||||
)
|
||||
atp_rules = defender_client.atp_protection_policy_rules
|
||||
assert len(atp_rules) == 1
|
||||
assert atp_rules[0].name == "Strict Preset Security Policy"
|
||||
assert atp_rules[0].state == "Enabled"
|
||||
assert atp_rules[0].sent_to == []
|
||||
assert atp_rules[0].sent_to_member_of == []
|
||||
assert atp_rules[0].recipient_domain_is == []
|
||||
defender_client.powershell.close()
|
||||
|
||||
@patch(
|
||||
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.get_email_tenant_settings",
|
||||
return_value={
|
||||
"Identity": "Default",
|
||||
"EnablePriorityAccountProtection": True,
|
||||
},
|
||||
)
|
||||
def test__get_email_tenant_settings(self, _mock_get_email_tenant_settings):
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online",
|
||||
return_value=True,
|
||||
),
|
||||
):
|
||||
defender_client = Defender(
|
||||
set_mocked_m365_provider(
|
||||
identity=M365IdentityInfo(tenant_domain=DOMAIN)
|
||||
)
|
||||
)
|
||||
email_tenant_settings = defender_client.email_tenant_settings
|
||||
assert email_tenant_settings.priority_account_protection_enabled is True
|
||||
defender_client.powershell.close()
|
||||
|
||||
Reference in New Issue
Block a user