fix(defender): enhance policies checks logic (#7666)

Co-authored-by: Daniel Barranquero <danielbo2001@gmail.com>
Co-authored-by: MrCloudSec <hello@mistercloudsec.com>
This commit is contained in:
Hugo Pereira Brito
2025-05-12 17:02:30 +02:00
committed by GitHub
parent 16f2209d3f
commit 1a8df3bf18
17 changed files with 3766 additions and 996 deletions
@@ -525,6 +525,26 @@ class M365PowerShell(PowerShellSession):
"Get-HostedContentFilterPolicy | ConvertTo-Json", json_parse=True
)
def get_inbound_spam_filter_rule(self) -> dict:
"""
Get Inbound Spam Filter Rule.
Retrieves the current inbound spam filter rule settings for Exchange Online.
Returns:
dict: Inbound spam filter rule settings in JSON format.
Example:
>>> get_inbound_spam_filter_rule()
{
"Name": "Rule1",
"State": "Enabled"
}
"""
return self.execute(
"Get-HostedContentFilterRule | ConvertTo-Json", json_parse=True
)
def get_report_submission_policy(self) -> dict:
"""
Get Exchange Online Report Submission Policy.
@@ -23,37 +23,155 @@ class defender_antiphishing_policy_configured(Check):
List[CheckReportM365]: A list of reports containing the result of the check.
"""
findings = []
for policy_name, policy in defender_client.antiphishing_policies.items():
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name="Defender Anti-Phishing Policy",
resource_id=policy_name,
)
report.status = "FAIL"
report.status_extended = (
f"Anti-phishing policy {policy_name} is not properly configured."
)
if (
not policy.default
and policy_name in defender_client.antiphising_rules
and defender_client.antiphising_rules[policy_name].state.lower()
== "enabled"
) or policy.default:
if (
policy.spoof_intelligence
and policy.spoof_intelligence_action.lower() == "quarantine"
and policy.dmarc_reject_action.lower() == "quarantine"
and policy.dmarc_quarantine_action.lower() == "quarantine"
and policy.safety_tips
and policy.unauthenticated_sender_action
and policy.show_tag
and policy.honor_dmarc_policy
):
if defender_client.antiphishing_policies:
# Only Default Defender Anti-Phishing Policy exists since there are only anti phishing rules when there are custom policies
if not defender_client.antiphishing_rules:
# Get the only policy in the dictionary since there is only the default policy
policy = next(iter(defender_client.antiphishing_policies.values()))
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.name,
resource_id=policy.name,
)
if self._is_policy_properly_configured(policy):
# Case 1: Default policy exists and is properly configured
report.status = "PASS"
report.status_extended = f"Anti-phishing policy {policy_name} is properly configured and enabled."
report.status_extended = f"{policy.name} is the only policy and it's properly configured in the default Defender Anti-Phishing Policy."
else:
# Case 5: Default policy exists but is not properly configured
report.status = "FAIL"
report.status_extended = f"{policy.name} is the only policy and it's not properly configured in the default Defender Anti-Phishing Policy."
findings.append(report)
findings.append(report)
# Multiple Defender Anti-Phishing Policies
else:
default_policy_well_configured = False
for (
policy_name,
policy,
) in defender_client.antiphishing_policies.items():
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy_name,
resource_id=policy_name,
)
if policy.default:
if not self._is_policy_properly_configured(policy):
# Case 4: Default policy is not properly configured and there are other policies
report.status = "FAIL"
report.status_extended = f"{policy_name} is not properly configured in the default Defender Anti-Phishing Policy, but could be overridden by another well-configured Custom Policy."
findings.append(report)
else:
# Case 2: Default policy is properly configured and there are other policies
report.status = "PASS"
report.status_extended = f"{policy_name} is properly configured in the default Defender Anti-Phishing Policy, but could be overridden by another bad-configured Custom Policy."
default_policy_well_configured = True
findings.append(report)
else:
if not self._is_policy_properly_configured(policy):
included_resources = []
if defender_client.antiphishing_rules[policy.name].users:
included_resources.append(
f"users: {', '.join(defender_client.antiphishing_rules[policy.name].users)}"
)
if defender_client.antiphishing_rules[policy.name].groups:
included_resources.append(
f"groups: {', '.join(defender_client.antiphishing_rules[policy.name].groups)}"
)
if defender_client.antiphishing_rules[policy.name].domains:
included_resources.append(
f"domains: {', '.join(defender_client.antiphishing_rules[policy.name].domains)}"
)
included_resources_str = "; ".join(included_resources)
# Case 3: Default policy is properly configured but other custom policies are not
if default_policy_well_configured:
report.status = "FAIL"
report.status_extended = (
f"Custom Anti-phishing policy {policy_name} is not properly configured and includes {included_resources_str}, "
f"with priority {defender_client.antiphishing_rules[policy.name].priority} (0 is the highest). "
"However, the default policy is properly configured, so entities not included by this custom policy could be correctly protected."
)
findings.append(report)
# Case 5: Default policy is not properly configured and other custom policies are not
else:
report.status = "FAIL"
report.status_extended = (
f"Custom Anti-phishing policy {policy_name} is not properly configured and includes {included_resources_str}, "
f"with priority {defender_client.antiphishing_rules[policy.name].priority} (0 is the highest). "
"Also, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
else:
included_resources = []
if defender_client.antiphishing_rules[policy.name].users:
included_resources.append(
f"users: {', '.join(defender_client.antiphishing_rules[policy.name].users)}"
)
if defender_client.antiphishing_rules[policy.name].groups:
included_resources.append(
f"groups: {', '.join(defender_client.antiphishing_rules[policy.name].groups)}"
)
if defender_client.antiphishing_rules[policy.name].domains:
included_resources.append(
f"domains: {', '.join(defender_client.antiphishing_rules[policy.name].domains)}"
)
included_resources_str = "; ".join(included_resources)
# Case 2: Default policy is properly configured and other custom policies are too
if default_policy_well_configured:
report.status = "PASS"
report.status_extended = (
f"Custom Anti-phishing policy {policy_name} is properly configured and includes {included_resources_str}, "
f"with priority {defender_client.antiphishing_rules[policy.name].priority} (0 is the highest). "
"Also, the default policy is properly configured, so entities not included by this custom policy could still be correctly protected."
)
findings.append(report)
# Case 6: Default policy is not properly configured but other custom policies are
else:
report.status = "PASS"
report.status_extended = (
f"Custom Anti-phishing policy {policy_name} is properly configured and includes {included_resources_str}, "
f"with priority {defender_client.antiphishing_rules[policy.name].priority} (0 is the highest). "
"However, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
return findings
def _is_policy_properly_configured(self, policy) -> bool:
"""
Check if a policy is properly configured according to best practices.
Args:
policy: The anti-phishing policy to check.
Returns:
bool: True if the policy is properly configured, False otherwise.
"""
return (
(
policy.default
or defender_client.antiphishing_rules[policy.name].state.lower()
== "enabled"
)
and policy.spoof_intelligence
and policy.spoof_intelligence_action.lower() == "quarantine"
and policy.dmarc_reject_action.lower() == "quarantine"
and policy.dmarc_quarantine_action.lower() == "quarantine"
and policy.safety_tips
and policy.unauthenticated_sender_action
and policy.show_tag
and policy.honor_dmarc_policy
)
@@ -6,8 +6,7 @@ from prowler.providers.m365.services.defender.defender_client import defender_cl
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.
Check if the outbound spam policy is established and properly configured in the Defender service.
Attributes:
metadata: Metadata associated with the check (inherited from Check).
@@ -15,40 +14,159 @@ class defender_antispam_outbound_policy_configured(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.
Execute the check to verify if an outbound spam policy is established and properly configured.
This method checks the Defender outbound spam policies to ensure they are configured
according to best practices.
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
):
if defender_client.outbound_spam_policies:
# Only Default Defender Outbound Spam Policy
if not defender_client.outbound_spam_rules:
# Get the only policy in the dictionary
policy = next(iter(defender_client.outbound_spam_policies.values()))
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.name,
resource_id=policy.name,
)
if self._is_policy_properly_configured(policy):
# Case 1: Default policy exists and is properly configured
report.status = "PASS"
report.status_extended = f"Outbound Spam Policy {policy_name} is properly configured and enabled."
report.status_extended = f"{policy.name} is the only policy and it's properly configured in the default Defender Outbound Spam Policy."
else:
# Case 5: Default policy exists but is not properly configured
report.status = "FAIL"
report.status_extended = f"{policy.name} is the only policy and it's not properly configured in the default Defender Outbound Spam Policy."
findings.append(report)
findings.append(report)
# Multiple Defender Outbound Spam Policies
else:
default_policy_well_configured = False
for (
policy_name,
policy,
) in defender_client.outbound_spam_policies.items():
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy_name,
resource_id=policy_name,
)
if policy.default:
if not self._is_policy_properly_configured(policy):
# Case 4: Default policy is not properly configured and there are other policies
report.status = "FAIL"
report.status_extended = f"{policy_name} is not properly configured in the default Defender Outbound Spam Policy, but could be overridden by another well-configured Custom Policy."
findings.append(report)
else:
# Case 2: Default policy is properly configured and there are other policies
report.status = "PASS"
report.status_extended = f"{policy_name} is properly configured in the default Defender Outbound Spam Policy, but could be overridden by another bad-configured Custom Policy."
default_policy_well_configured = True
findings.append(report)
else:
if not self._is_policy_properly_configured(policy):
included_resources = []
if defender_client.outbound_spam_rules[policy.name].users:
included_resources.append(
f"users: {', '.join(defender_client.outbound_spam_rules[policy.name].users)}"
)
if defender_client.outbound_spam_rules[policy.name].groups:
included_resources.append(
f"groups: {', '.join(defender_client.outbound_spam_rules[policy.name].groups)}"
)
if defender_client.outbound_spam_rules[policy.name].domains:
included_resources.append(
f"domains: {', '.join(defender_client.outbound_spam_rules[policy.name].domains)}"
)
included_resources_str = "; ".join(included_resources)
# Case 3: Default policy is properly configured but other custom policies are not
if default_policy_well_configured:
report.status = "FAIL"
report.status_extended = (
f"Custom Outbound Spam policy {policy_name} is not properly configured and includes {included_resources_str}, "
f"with priority {defender_client.outbound_spam_rules[policy.name].priority} (0 is the highest). "
"However, the default policy is properly configured, so entities not included by this custom policy could be correctly protected."
)
findings.append(report)
# Case 5: Default policy is not properly configured and other custom policies are not
else:
report.status = "FAIL"
report.status_extended = (
f"Custom Outbound Spam policy {policy_name} is not properly configured and includes {included_resources_str}, "
f"with priority {defender_client.outbound_spam_rules[policy.name].priority} (0 is the highest). "
"Also, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
else:
included_resources = []
if defender_client.outbound_spam_rules[policy.name].users:
included_resources.append(
f"users: {', '.join(defender_client.outbound_spam_rules[policy.name].users)}"
)
if defender_client.outbound_spam_rules[policy.name].groups:
included_resources.append(
f"groups: {', '.join(defender_client.outbound_spam_rules[policy.name].groups)}"
)
if defender_client.outbound_spam_rules[policy.name].domains:
included_resources.append(
f"domains: {', '.join(defender_client.outbound_spam_rules[policy.name].domains)}"
)
included_resources_str = "; ".join(included_resources)
# Case 2: Default policy is properly configured and other custom policies are too
if default_policy_well_configured:
report.status = "PASS"
report.status_extended = (
f"Custom Outbound Spam policy {policy_name} is properly configured and includes {included_resources_str}, "
f"with priority {defender_client.outbound_spam_rules[policy.name].priority} (0 is the highest). "
"Also, the default policy is properly configured, so entities not included by this custom policy could still be correctly protected."
)
findings.append(report)
# Case 6: Default policy is not properly configured but other custom policies are
else:
report.status = "PASS"
report.status_extended = (
f"Custom Outbound Spam policy {policy_name} is properly configured and includes {included_resources_str}, "
f"with priority {defender_client.outbound_spam_rules[policy.name].priority} (0 is the highest). "
"However, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
return findings
def _is_policy_properly_configured(self, policy) -> bool:
"""
Check if a policy is properly configured according to best practices.
Args:
policy: The outbound spam policy to check.
Returns:
bool: True if the policy is properly configured, False otherwise.
"""
return (
(
policy.default
or defender_client.outbound_spam_rules[policy.name].state.lower()
== "enabled"
)
and policy.notify_limit_exceeded
and policy.notify_sender_blocked
and policy.notify_limit_exceeded_addresses
and policy.notify_sender_blocked_addresses
)
@@ -20,28 +20,150 @@ class defender_antispam_outbound_policy_forwarding_disabled(Check):
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} does allow mail forwarding."
)
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 not policy.auto_forwarding_mode:
if defender_client.outbound_spam_policies:
# Only Default Defender Outbound Spam Policy exists
if not defender_client.outbound_spam_rules:
policy = next(iter(defender_client.outbound_spam_policies.values()))
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.name,
resource_id=policy.name,
)
if self._is_forwarding_disabled(policy):
# Case 1: Default policy exists and has forwarding disabled
report.status = "PASS"
report.status_extended = f"Outbound Spam Policy {policy_name} does not allow mail forwarding."
report.status_extended = f"{policy.name} is the only policy and mail forwarding is disabled."
else:
# Case 5: Default policy exists but allows forwarding
report.status = "FAIL"
report.status_extended = f"{policy.name} is the only policy and mail forwarding is allowed."
findings.append(report)
findings.append(report)
# Multiple Defender Outbound Spam Policies exist
else:
default_policy_well_configured = False
for (
policy_name,
policy,
) in defender_client.outbound_spam_policies.items():
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy_name,
resource_id=policy_name,
)
if policy.default:
if not self._is_forwarding_disabled(policy):
# Case 4: Default policy allows forwarding and there are other policies
report.status = "FAIL"
report.status_extended = (
f"{policy_name} is the default policy and mail forwarding is allowed, "
"but it could be overridden by another well-configured Custom Policy."
)
findings.append(report)
else:
# Case 2: Default policy disables forwarding and there are other policies
report.status = "PASS"
report.status_extended = (
f"{policy_name} is the default policy and mail forwarding is disabled, "
"but it could be overridden by another misconfigured Custom Policy."
)
default_policy_well_configured = True
findings.append(report)
else:
if not self._is_forwarding_disabled(policy):
included_resources = []
if defender_client.outbound_spam_rules[policy.name].users:
included_resources.append(
f"users: {', '.join(defender_client.outbound_spam_rules[policy.name].users)}"
)
if defender_client.outbound_spam_rules[policy.name].groups:
included_resources.append(
f"groups: {', '.join(defender_client.outbound_spam_rules[policy.name].groups)}"
)
if defender_client.outbound_spam_rules[policy.name].domains:
included_resources.append(
f"domains: {', '.join(defender_client.outbound_spam_rules[policy.name].domains)}"
)
included_resources_str = "; ".join(included_resources)
if default_policy_well_configured:
# Case 3: Default policy disables forwarding but custom one doesn't
report.status = "FAIL"
report.status_extended = (
f"Custom Outbound Spam policy {policy_name} allows mail forwarding and includes {included_resources_str}, "
f"with priority {defender_client.outbound_spam_rules[policy.name].priority} (0 is the highest). "
"However, the default policy disables mail forwarding, so entities not included by this custom policy could be correctly protected."
)
findings.append(report)
else:
# Case 5: Neither default nor custom policies disable forwarding
report.status = "FAIL"
report.status_extended = (
f"Custom Outbound Spam policy {policy_name} allows mail forwarding and includes {included_resources_str}, "
f"with priority {defender_client.outbound_spam_rules[policy.name].priority} (0 is the highest). "
"Also, the default policy allows mail forwarding, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
else:
included_resources = []
if defender_client.outbound_spam_rules[policy.name].users:
included_resources.append(
f"users: {', '.join(defender_client.outbound_spam_rules[policy.name].users)}"
)
if defender_client.outbound_spam_rules[policy.name].groups:
included_resources.append(
f"groups: {', '.join(defender_client.outbound_spam_rules[policy.name].groups)}"
)
if defender_client.outbound_spam_rules[policy.name].domains:
included_resources.append(
f"domains: {', '.join(defender_client.outbound_spam_rules[policy.name].domains)}"
)
included_resources_str = "; ".join(included_resources)
if default_policy_well_configured:
# Case 2: Both default and custom policies disable forwarding
report.status = "PASS"
report.status_extended = (
f"Custom Outbound Spam policy {policy_name} disables mail forwarding and includes {included_resources_str}, "
f"with priority {defender_client.outbound_spam_rules[policy.name].priority} (0 is the highest). "
"Also, the default policy disables mail forwarding, so entities not included by this custom policy could still be correctly protected."
)
findings.append(report)
else:
# Case 6: Default policy allows forwarding, custom policy disables it
report.status = "PASS"
report.status_extended = (
f"Custom Outbound Spam policy {policy_name} disables mail forwarding and includes {included_resources_str}, "
f"with priority {defender_client.outbound_spam_rules[policy.name].priority} (0 is the highest). "
"However, the default policy allows mail forwarding, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
return findings
def _is_forwarding_disabled(self, policy) -> bool:
"""
Check if mail forwarding is disabled in the policy.
Args:
policy: The outbound spam policy to check.
Returns:
bool: True if mail forwarding is disabled, False otherwise.
"""
return (
policy.default
or defender_client.outbound_spam_rules[policy.name].state.lower()
== "enabled"
) and not policy.auto_forwarding_mode
@@ -23,20 +23,163 @@ class defender_antispam_policy_inbound_no_allowed_domains(Check):
List[CheckReportM365]: A list of reports containing the result of the check.
"""
findings = []
for policy in defender_client.inbound_spam_policies:
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name="Defender Inbound Spam Policy",
resource_id=policy.identity,
)
report.status = "PASS"
report.status_extended = f"Inbound anti-spam policy {policy.identity} does not contain allowed domains."
if policy.allowed_sender_domains:
report.status = "FAIL"
report.status_extended = f"Inbound anti-spam policy {policy.identity} contains allowed domains: {policy.allowed_sender_domains}."
if defender_client.inbound_spam_policies:
# Only Default Defender Inbound Spam Policy exists
if not defender_client.inbound_spam_rules:
policy = defender_client.inbound_spam_policies[0]
findings.append(report)
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.identity,
resource_id=policy.identity,
)
if self._has_no_allowed_domains(policy):
# Case 1: Default policy exists and has no allowed domains
report.status = "PASS"
report.status_extended = f"{policy.identity} is the only policy and it does not contain allowed domains."
else:
# Case 5: Default policy exists but contains allowed domains
report.status = "FAIL"
report.status_extended = f"{policy.identity} is the only policy and it contains allowed domains: {', '.join(policy.allowed_sender_domains)}."
findings.append(report)
# Multiple Defender Inbound Spam Policies exist
else:
default_policy_well_configured = False
for policy in defender_client.inbound_spam_policies:
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.identity,
resource_id=policy.identity,
)
if policy.default:
if not self._has_no_allowed_domains(policy):
# Case 4: Default policy contains allowed domains
report.status = "FAIL"
report.status_extended = (
f"{policy.identity} is the default policy and it contains allowed domains: {', '.join(policy.allowed_sender_domains)}, "
"but it could be overridden by another well-configured Custom Policy."
)
findings.append(report)
else:
# Case 2: Default policy has no allowed domains and there are other policies
report.status = "PASS"
report.status_extended = (
f"{policy.identity} is the default policy and it does not contain allowed domains, "
"but it could be overridden by another misconfigured Custom Policy."
)
default_policy_well_configured = True
findings.append(report)
else:
if not self._has_no_allowed_domains(policy):
included_resources = []
if defender_client.inbound_spam_rules[
policy.identity
].users:
included_resources.append(
f"users: {', '.join(defender_client.inbound_spam_rules[policy.identity].users)}"
)
if defender_client.inbound_spam_rules[
policy.identity
].groups:
included_resources.append(
f"groups: {', '.join(defender_client.inbound_spam_rules[policy.identity].groups)}"
)
if defender_client.inbound_spam_rules[
policy.identity
].domains:
included_resources.append(
f"domains: {', '.join(defender_client.inbound_spam_rules[policy.identity].domains)}"
)
included_resources_str = "; ".join(included_resources)
priority = defender_client.inbound_spam_rules[
policy.identity
].priority
if default_policy_well_configured:
# Case 3: Default policy has no allowed domains but custom one does
report.status = "FAIL"
report.status_extended = (
f"Custom Inbound Spam policy {policy.identity} contains allowed domains and includes {included_resources_str}, "
f"with priority {priority} (0 is the highest). However, the default policy does not contain allowed domains, "
"so entities not included by this custom policy could be correctly protected."
)
else:
# Case 5: Neither default nor custom policies are correctly configured
report.status = "FAIL"
report.status_extended = (
f"Custom Inbound Spam policy {policy.identity} contains allowed domains and includes {included_resources_str}, "
f"with priority {priority} (0 is the highest). Also, the default policy contains allowed domains, "
"so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
else:
included_resources = []
if defender_client.inbound_spam_rules[
policy.identity
].users:
included_resources.append(
f"users: {', '.join(defender_client.inbound_spam_rules[policy.identity].users)}"
)
if defender_client.inbound_spam_rules[
policy.identity
].groups:
included_resources.append(
f"groups: {', '.join(defender_client.inbound_spam_rules[policy.identity].groups)}"
)
if defender_client.inbound_spam_rules[
policy.identity
].domains:
included_resources.append(
f"domains: {', '.join(defender_client.inbound_spam_rules[policy.identity].domains)}"
)
included_resources_str = "; ".join(included_resources)
priority = defender_client.inbound_spam_rules[
policy.identity
].priority
if default_policy_well_configured:
# Case 2: Both default and custom policies do not contain allowed domains
report.status = "PASS"
report.status_extended = (
f"Custom Inbound Spam policy {policy.identity} does not contain allowed domains and includes {included_resources_str}, "
f"with priority {priority} (0 is the highest). Also, the default policy does not contain allowed domains, "
"so entities not included by this custom policy could still be correctly protected."
)
else:
# Case 6: Default policy contains allowed domains, custom policy does not
report.status = "PASS"
report.status_extended = (
f"Custom Inbound Spam policy {policy.identity} does not contain allowed domains and includes {included_resources_str}, "
f"with priority {priority} (0 is the highest). However, the default policy contains allowed domains, "
"so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
return findings
def _has_no_allowed_domains(self, policy) -> bool:
"""
Check if the policy has no allowed domains.
Args:
policy: The inbound spam policy to check.
Returns:
bool: True if the policy has no allowed domains, False otherwise.
"""
return (
policy.default
or defender_client.inbound_spam_rules[policy.identity].state.lower()
== "enabled"
) and not policy.allowed_sender_domains
@@ -16,39 +16,138 @@ class defender_malware_policy_common_attachments_filter_enabled(Check):
"""
Execute the check to verify if the Common Attachment Types Filter is enabled.
This method checks the Defender anti-malware policy to determine if the
This method checks the Defender anti-malware policies to determine if the
Common Attachment Types Filter is enabled.
Returns:
List[CheckReportM365]: A list of reports containing the result of the check.
"""
findings = []
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:
if defender_client.malware_policies:
# Only Default Defender Malware Policy exists
if not defender_client.malware_rules:
policy = defender_client.malware_policies[0]
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name="Defender Malware Policy",
resource_id="defenderMalwarePolicy",
resource_name=policy.identity,
resource_id=policy.identity,
)
report.status = "FAIL"
report.status_extended = f"Common Attachment Types Filter is not enabled in anti-malware policy {policy.identity}."
if policy.enable_file_filter:
# Case 1: Default policy exists and has the setting enabled
report.status = "PASS"
report.status_extended = f"Common Attachment Types Filter is enabled in anti-malware policy {policy.identity}."
break
report.status_extended = f"{policy.identity} is the only policy and Common Attachment Types Filter is enabled."
else:
# Case 5: Default policy exists but doesn't have the setting enabled
report.status = "FAIL"
report.status_extended = f"{policy.identity} is the only policy and Common Attachment Types Filter is not enabled."
findings.append(report)
findings.append(report)
# Multiple Defender Malware Policies exist
else:
default_policy_well_configured = False
for policy in defender_client.malware_policies:
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.identity,
resource_id=policy.identity,
)
if policy.is_default:
if not policy.enable_file_filter:
# Case 4: Default policy doesn't have the setting enabled and there are other policies
report.status = "FAIL"
report.status_extended = (
f"{policy.identity} is the default policy and Common Attachment Types Filter is not enabled, "
"but it could be overridden by another well-configured Custom Policy."
)
findings.append(report)
else:
# Case 2: Default policy has the setting enabled and there are other policies
report.status = "PASS"
report.status_extended = (
f"{policy.identity} is the default policy and Common Attachment Types Filter is enabled, "
"but it could be overridden by another misconfigured Custom Policy."
)
default_policy_well_configured = True
findings.append(report)
else:
if not policy.enable_file_filter:
included_resources = []
if defender_client.malware_rules[policy.identity].users:
included_resources.append(
f"users: {', '.join(defender_client.malware_rules[policy.identity].users)}"
)
if defender_client.malware_rules[policy.identity].groups:
included_resources.append(
f"groups: {', '.join(defender_client.malware_rules[policy.identity].groups)}"
)
if defender_client.malware_rules[policy.identity].domains:
included_resources.append(
f"domains: {', '.join(defender_client.malware_rules[policy.identity].domains)}"
)
included_resources_str = "; ".join(included_resources)
if default_policy_well_configured:
# Case 3: Default policy enables the setting but custom one doesn't
report.status = "FAIL"
report.status_extended = (
f"Custom Malware policy {policy.identity} does not enable Common Attachment Types Filter and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"However, the default policy enables the filter, so entities not included by this custom policy could be correctly protected."
)
findings.append(report)
else:
# Case 5: Neither default nor custom policies enable the setting
report.status = "FAIL"
report.status_extended = (
f"Custom Malware policy {policy.identity} does not enable Common Attachment Types Filter and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"Also, the default policy does not enable the filter, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
else:
included_resources = []
if defender_client.malware_rules[policy.identity].users:
included_resources.append(
f"users: {', '.join(defender_client.malware_rules[policy.identity].users)}"
)
if defender_client.malware_rules[policy.identity].groups:
included_resources.append(
f"groups: {', '.join(defender_client.malware_rules[policy.identity].groups)}"
)
if defender_client.malware_rules[policy.identity].domains:
included_resources.append(
f"domains: {', '.join(defender_client.malware_rules[policy.identity].domains)}"
)
included_resources_str = "; ".join(included_resources)
if default_policy_well_configured:
# Case 2: Both default and custom policies enable the setting
report.status = "PASS"
report.status_extended = (
f"Custom Malware policy {policy.identity} enables Common Attachment Types Filter and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"Also, the default policy enables the filter, so entities not included by this custom policy could still be correctly protected."
)
findings.append(report)
else:
# Case 6: Default policy doesn't enable the setting, but custom policy does
report.status = "PASS"
report.status_extended = (
f"Custom Malware policy {policy.identity} enables Common Attachment Types Filter and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"However, the default policy does not enable the filter, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
return findings
@@ -14,17 +14,16 @@ class defender_malware_policy_comprehensive_attachments_filter_applied(Check):
def execute(self) -> List[CheckReportM365]:
"""
Executes the check to determine if the Common Attachment Types Filter is enabled.
Executes the check to determine if the Common Attachment Types Filter is properly configured.
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.
This method evaluates the Defender anti-malware policies to ensure the filter is enabled and applied
to all 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",
@@ -81,63 +80,175 @@ class defender_malware_policy_comprehensive_attachments_filter_applied(Check):
"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:
if defender_client.malware_policies:
# Only Default Defender Malware Policy exists
if not defender_client.malware_rules:
policy = defender_client.malware_policies[0]
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name="Defender Malware Policy",
resource_id="defenderMalwarePolicy",
resource_name=policy.identity,
resource_id=policy.identity,
)
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 self._is_filter_properly_configured(policy, recommended_extensions):
# Case 1: Default policy exists and has filter properly configured
report.status = "PASS"
report.status_extended = f"{policy.identity} is the only policy and Common Attachment Types Filter is properly configured."
else:
# Case 5: Default policy exists but doesn't have filter properly configured
missing = self._get_missing_extensions(
policy, recommended_extensions
)
report.status = "FAIL"
report.status_extended = f"{policy.identity} is the only policy and Common Attachment Types Filter is not properly configured. Missing recommended file types: {', '.join(missing)}."
findings.append(report)
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
]
# Multiple Defender Malware Policies exist
else:
default_policy_well_configured = False
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."
for policy in defender_client.malware_policies:
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.identity,
resource_id=policy.identity,
)
if policy.is_default:
if not self._is_filter_properly_configured(
policy, recommended_extensions
):
# Case 4: Default policy is not properly configured
missing = self._get_missing_extensions(
policy, recommended_extensions
)
report.status = "FAIL"
report.status_extended = (
f"{policy.identity} is the default policy and Common Attachment Types Filter is not properly configured, "
f"but it could be overridden by another well-configured Custom Policy. Missing recommended file types: {', '.join(missing)}."
)
findings.append(report)
else:
# Case 2: Default policy is properly configured
report.status = "PASS"
report.status_extended = (
f"{policy.identity} is the default policy and Common Attachment Types Filter is properly configured, "
"but it could be overridden by another misconfigured Custom Policy."
)
default_policy_well_configured = True
findings.append(report)
else:
report.status_extended = f"Common Attachment Types Filter is enabled in anti-malware policy {policy.identity}, but the policy is disabled."
break
if not self._is_filter_properly_configured(
policy, recommended_extensions
):
included_resources = []
findings.append(report)
if defender_client.malware_rules[policy.identity].users:
included_resources.append(
f"users: {', '.join(defender_client.malware_rules[policy.identity].users)}"
)
if defender_client.malware_rules[policy.identity].groups:
included_resources.append(
f"groups: {', '.join(defender_client.malware_rules[policy.identity].groups)}"
)
if defender_client.malware_rules[policy.identity].domains:
included_resources.append(
f"domains: {', '.join(defender_client.malware_rules[policy.identity].domains)}"
)
included_resources_str = "; ".join(included_resources)
missing = self._get_missing_extensions(
policy, recommended_extensions
)
if default_policy_well_configured:
# Case 3: Default policy is configured, custom one isn't
report.status = "FAIL"
report.status_extended = (
f"Custom Malware policy {policy.identity} is not properly configured and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
f"Missing recommended file types: {', '.join(missing)}. "
"However, the default policy is properly configured, so entities not included by this custom policy could be correctly protected."
)
findings.append(report)
else:
# Case 5: Neither default nor custom policy is properly configured
report.status = "FAIL"
report.status_extended = (
f"Custom Malware policy {policy.identity} is not properly configured and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
f"Missing recommended file types: {', '.join(missing)}. "
"Also, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
else:
included_resources = []
if defender_client.malware_rules[policy.identity].users:
included_resources.append(
f"users: {', '.join(defender_client.malware_rules[policy.identity].users)}"
)
if defender_client.malware_rules[policy.identity].groups:
included_resources.append(
f"groups: {', '.join(defender_client.malware_rules[policy.identity].groups)}"
)
if defender_client.malware_rules[policy.identity].domains:
included_resources.append(
f"domains: {', '.join(defender_client.malware_rules[policy.identity].domains)}"
)
included_resources_str = "; ".join(included_resources)
if default_policy_well_configured:
# Case 2: Both default and custom policies are properly configured
report.status = "PASS"
report.status_extended = (
f"Custom Malware policy {policy.identity} is properly configured and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"Also, the default policy is properly configured, so entities not included by this custom policy could still be correctly protected."
)
findings.append(report)
else:
# Case 6: Default policy not configured, custom policy is
report.status = "PASS"
report.status_extended = (
f"Custom Malware policy {policy.identity} is properly configured and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"However, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
return findings
def _is_filter_properly_configured(self, policy, recommended_extensions) -> bool:
if not policy.enable_file_filter:
return False
if (
not policy.is_default
and policy.identity in defender_client.malware_rules
and defender_client.malware_rules[policy.identity].state.lower()
!= "enabled"
):
return False
blocked_extensions = [ext.lower() for ext in policy.file_types]
return all(ext.lower() in blocked_extensions for ext in recommended_extensions)
def _get_missing_extensions(self, policy, recommended_extensions) -> List[str]:
if not policy.enable_file_filter:
return recommended_extensions
blocked_extensions = [ext.lower() for ext in policy.file_types]
return [
ext
for ext in recommended_extensions
if ext.lower() not in blocked_extensions
]
@@ -16,47 +16,152 @@ class defender_malware_policy_notifications_internal_users_malware_enabled(Check
"""
Execute the check to verify if notifications for internal users sending malware are enabled.
This method checks the Defender anti-malware policy to determine if notifications for internal users
sending malware are enabled.
This method evaluates the Defender anti-malware policies to ensure internal sender notifications
are properly configured.
Returns:
List[CheckReportM365]: A list of reports containing the result of the check.
List[CheckReportM365]: A list of reports with the results of the check.
"""
findings = []
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 = (
"Notifications for internal users sending malware are not enabled."
)
findings.append(report)
else:
for policy in defender_client.malware_policies:
if defender_client.malware_policies:
# Only Default Defender Malware Policy exists
if not defender_client.malware_rules:
policy = defender_client.malware_policies[0]
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name="Defender Malware Policy",
resource_id="defenderMalwarePolicy",
)
report.status = "FAIL"
report.status_extended = (
"Notifications for internal users sending malware are not enabled."
resource_name=policy.identity,
resource_id=policy.identity,
)
if policy.enable_internal_sender_admin_notifications:
if policy.internal_sender_admin_address:
report.status = "PASS"
report.status_extended = "Notifications for internal users sending malware are enabled."
break
if self._are_notifications_enabled(policy):
# Case 1: Default policy exists and has notifications enabled
report.status = "PASS"
report.status_extended = f"{policy.identity} is the only policy and notifications for internal users sending malware are enabled."
else:
# Case 5: Default policy exists but doesn't have notifications enabled
report.status = "FAIL"
report.status_extended = f"{policy.identity} is the only policy and notifications for internal users sending malware are not enabled."
findings.append(report)
# Multiple Defender Malware Policies exist
else:
default_policy_well_configured = False
for policy in defender_client.malware_policies:
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.identity,
resource_id=policy.identity,
)
if policy.is_default:
if not self._are_notifications_enabled(policy):
# Case 4: Default policy not configured
report.status = "FAIL"
report.status_extended = (
f"{policy.identity} is the default policy and notifications for internal users sending malware are not enabled, "
"but it could be overridden by another well-configured Custom Policy."
)
findings.append(report)
else:
# Case 2: Default policy is properly configured
report.status = "PASS"
report.status_extended = (
f"{policy.identity} is the default policy and notifications for internal users sending malware are enabled, "
"but it could be overridden by another misconfigured Custom Policy."
)
default_policy_well_configured = True
findings.append(report)
else:
report.status = "FAIL"
report.status_extended = "Notifications for internal users sending malware are enabled, but no email addresses are configured."
if not self._are_notifications_enabled(policy):
included_resources = []
findings.append(report)
if defender_client.malware_rules[policy.identity].users:
included_resources.append(
f"users: {', '.join(defender_client.malware_rules[policy.identity].users)}"
)
if defender_client.malware_rules[policy.identity].groups:
included_resources.append(
f"groups: {', '.join(defender_client.malware_rules[policy.identity].groups)}"
)
if defender_client.malware_rules[policy.identity].domains:
included_resources.append(
f"domains: {', '.join(defender_client.malware_rules[policy.identity].domains)}"
)
included_resources_str = "; ".join(included_resources)
if default_policy_well_configured:
# Case 3: Default policy is configured, custom one isn't
report.status = "FAIL"
report.status_extended = (
f"Custom Malware policy {policy.identity} is not properly configured and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"However, the default policy is properly configured, so entities not included by this custom policy could be correctly protected."
)
findings.append(report)
else:
# Case 5: Neither default nor custom policy is properly configured
report.status = "FAIL"
report.status_extended = (
f"Custom Malware policy {policy.identity} is not properly configured and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"Also, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
else:
included_resources = []
if defender_client.malware_rules[policy.identity].users:
included_resources.append(
f"users: {', '.join(defender_client.malware_rules[policy.identity].users)}"
)
if defender_client.malware_rules[policy.identity].groups:
included_resources.append(
f"groups: {', '.join(defender_client.malware_rules[policy.identity].groups)}"
)
if defender_client.malware_rules[policy.identity].domains:
included_resources.append(
f"domains: {', '.join(defender_client.malware_rules[policy.identity].domains)}"
)
included_resources_str = "; ".join(included_resources)
if default_policy_well_configured:
# Case 2: Both default and custom policies are properly configured
report.status = "PASS"
report.status_extended = (
f"Custom Malware policy {policy.identity} is properly configured and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"Also, the default policy is properly configured, so entities not included by this custom policy could still be correctly protected."
)
findings.append(report)
else:
# Case 6: Default policy not configured, custom policy is
report.status = "PASS"
report.status_extended = (
f"Custom Malware policy {policy.identity} is properly configured and includes {included_resources_str}, "
f"with priority {defender_client.malware_rules[policy.identity].priority} (0 is the highest). "
"However, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
return findings
def _are_notifications_enabled(self, policy) -> bool:
if (
not policy.is_default
and policy.identity in defender_client.malware_rules
and defender_client.malware_rules[policy.identity].state.lower()
!= "enabled"
):
return False
return (
policy.enable_internal_sender_admin_notifications
and policy.internal_sender_admin_address
)
@@ -1,4 +1,4 @@
from typing import List
from typing import List, Optional
from pydantic import BaseModel
@@ -14,10 +14,11 @@ class Defender(M365Service):
self.outbound_spam_policies = {}
self.outbound_spam_rules = {}
self.antiphishing_policies = {}
self.antiphising_rules = {}
self.antiphishing_rules = {}
self.connection_filter_policy = None
self.dkim_configurations = []
self.inbound_spam_policies = []
self.inbound_spam_rules = {}
self.report_submission_policy = None
if self.powershell:
self.powershell.connect_exchange_online()
@@ -25,11 +26,12 @@ class Defender(M365Service):
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()
self.antiphising_rules = self._get_antiphising_rules()
self.antiphishing_policies = self._get_antiphishing_policy()
self.antiphishing_rules = self._get_antiphishing_rules()
self.connection_filter_policy = self._get_connection_filter_policy()
self.dkim_configurations = self._get_dkim_config()
self.inbound_spam_policies = self._get_inbound_spam_filter_policy()
self.inbound_spam_rules = self._get_inbound_spam_filter_rule()
self.report_submission_policy = self._get_report_submission_policy()
self.powershell.close()
@@ -44,7 +46,7 @@ class Defender(M365Service):
if policy:
malware_policies.append(
MalwarePolicy(
enable_file_filter=policy.get("EnableFileFilter", True),
enable_file_filter=policy.get("EnableFileFilter", False),
identity=policy.get("Identity", ""),
enable_internal_sender_admin_notifications=policy.get(
"EnableInternalSenderAdminNotifications", False
@@ -56,6 +58,7 @@ class Defender(M365Service):
is_default=policy.get("IsDefault", False),
)
)
malware_policies.sort(key=lambda x: x.is_default, reverse=True)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -73,6 +76,10 @@ class Defender(M365Service):
if rule:
malware_rules[rule.get("Name", "")] = MalwareRule(
state=rule.get("State", ""),
priority=rule.get("Priority", 0),
users=rule.get("SentTo", None),
groups=rule.get("SentToMemberOf", None),
domains=rule.get("RecipientDomainIs", None),
)
except Exception as error:
logger.error(
@@ -80,7 +87,7 @@ class Defender(M365Service):
)
return malware_rules
def _get_antiphising_policy(self):
def _get_antiphishing_policy(self):
logger.info("Microsoft365 - Getting Defender antiphishing policy...")
antiphishing_policies = {}
try:
@@ -90,6 +97,7 @@ class Defender(M365Service):
for policy in antiphishing_policy:
if policy:
antiphishing_policies[policy.get("Name", "")] = AntiphishingPolicy(
name=policy.get("Name", ""),
spoof_intelligence=policy.get("EnableSpoofIntelligence", True),
spoof_intelligence_action=policy.get(
"AuthenticationFailAction", ""
@@ -104,13 +112,21 @@ class Defender(M365Service):
honor_dmarc_policy=policy.get("HonorDmarcPolicy", True),
default=policy.get("IsDefault", False),
)
antiphishing_policies = dict(
sorted(
antiphishing_policies.items(),
key=lambda item: item[1].default,
reverse=True,
)
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return antiphishing_policies
def _get_antiphising_rules(self):
def _get_antiphishing_rules(self):
logger.info("Microsoft365 - Getting Defender antiphishing rules...")
antiphishing_rules = {}
try:
@@ -121,6 +137,10 @@ class Defender(M365Service):
if rule:
antiphishing_rules[rule.get("Name", "")] = AntiphishingRule(
state=rule.get("State", ""),
priority=rule.get("Priority", 0),
users=rule.get("SentTo", None),
groups=rule.get("SentToMemberOf", None),
domains=rule.get("RecipientDomainIs", None),
)
except Exception as error:
logger.error(
@@ -176,6 +196,7 @@ class Defender(M365Service):
for policy in outbound_spam_policy:
if policy:
outbound_spam_policies[policy.get("Name", "")] = OutboundSpamPolicy(
name=policy.get("Name", ""),
notify_sender_blocked=policy.get("NotifyOutboundSpam", True),
notify_limit_exceeded=policy.get(
"BccSuspiciousOutboundMail", True
@@ -189,6 +210,14 @@ class Defender(M365Service):
auto_forwarding_mode=policy.get("AutoForwardingMode", True),
default=policy.get("IsDefault", False),
)
outbound_spam_policies = dict(
sorted(
outbound_spam_policies.items(),
key=lambda item: item[1].default,
reverse=True,
)
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -206,6 +235,10 @@ class Defender(M365Service):
if rule:
outbound_spam_rules[rule.get("Name", "")] = OutboundSpamRule(
state=rule.get("State", "Disabled"),
priority=rule.get("Priority", 0),
users=rule.get("From", None),
groups=rule.get("FromMemberOf", None),
domains=rule.get("SenderDomainIs", None),
)
except Exception as error:
logger.error(
@@ -230,14 +263,38 @@ class Defender(M365Service):
allowed_sender_domains=policy.get(
"AllowedSenderDomains", []
),
default=policy.get("IsDefault", False),
)
)
inbound_spam_policies.sort(key=lambda x: x.default, reverse=True)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return inbound_spam_policies
def _get_inbound_spam_filter_rule(self):
logger.info("Microsoft365 - Getting Defender inbound spam filter rule...")
inbound_spam_rules = {}
try:
inbound_spam_rule = self.powershell.get_inbound_spam_filter_rule()
if isinstance(inbound_spam_rule, dict):
inbound_spam_rule = [inbound_spam_rule]
for rule in inbound_spam_rule:
if rule:
inbound_spam_rules[rule.get("Name", "")] = InboundSpamRule(
state=rule.get("State", "Disabled"),
priority=rule.get("Priority", 0),
users=rule.get("SentTo", None),
groups=rule.get("SentToMemberOf", None),
domains=rule.get("RecipientDomainIs", None),
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return inbound_spam_rules
def _get_report_submission_policy(self):
logger.info("Microsoft365 - Getting Defender report submission policy...")
report_submission_policy = None
@@ -288,9 +345,14 @@ class MalwarePolicy(BaseModel):
class MalwareRule(BaseModel):
state: str
priority: int
users: Optional[list[str]]
groups: Optional[list[str]]
domains: Optional[list[str]]
class AntiphishingPolicy(BaseModel):
name: str
spoof_intelligence: bool
spoof_intelligence_action: str
dmarc_reject_action: str
@@ -304,6 +366,10 @@ class AntiphishingPolicy(BaseModel):
class AntiphishingRule(BaseModel):
state: str
priority: int
users: Optional[list[str]]
groups: Optional[list[str]]
domains: Optional[list[str]]
class ConnectionFilterPolicy(BaseModel):
@@ -318,6 +384,7 @@ class DkimConfig(BaseModel):
class OutboundSpamPolicy(BaseModel):
name: str
notify_sender_blocked: bool
notify_limit_exceeded: bool
notify_limit_exceeded_addresses: List[str]
@@ -328,11 +395,24 @@ class OutboundSpamPolicy(BaseModel):
class OutboundSpamRule(BaseModel):
state: str
priority: int
users: Optional[list[str]]
groups: Optional[list[str]]
domains: Optional[list[str]]
class DefenderInboundSpamPolicy(BaseModel):
identity: str
allowed_sender_domains: list[str] = []
default: bool
class InboundSpamRule(BaseModel):
state: str
priority: int
users: Optional[list[str]]
groups: Optional[list[str]]
domains: Optional[list[str]]
class ReportSubmissionPolicy(BaseModel):
@@ -4,7 +4,69 @@ from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
class Test_defender_antiphishing_policy_configured:
def test_properly_configured_custom_policy(self):
def test_case_1_default_policy_properly_configured(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_antiphishing_policy_configured.defender_antiphishing_policy_configured.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antiphishing_policy_configured.defender_antiphishing_policy_configured import (
defender_antiphishing_policy_configured,
)
from prowler.providers.m365.services.defender.defender_service import (
AntiphishingPolicy,
)
defender_client.antiphishing_policies = {
"Default": AntiphishingPolicy(
name="Default",
spoof_intelligence=True,
spoof_intelligence_action="Quarantine",
dmarc_reject_action="Quarantine",
dmarc_quarantine_action="Quarantine",
safety_tips=True,
unauthenticated_sender_action=True,
show_tag=True,
honor_dmarc_policy=True,
default=True,
)
}
defender_client.antiphishing_rules = {}
check = defender_antiphishing_policy_configured()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is the only policy and it's properly configured in the default Defender Anti-Phishing Policy."
)
assert (
result[0].resource_name
== defender_client.antiphishing_policies["Default"].name
)
assert (
result[0].resource_id
== defender_client.antiphishing_policies["Default"].name
)
assert (
result[0].resource
== defender_client.antiphishing_policies["Default"].dict()
)
def test_case_2_all_policies_properly_configured(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -31,7 +93,20 @@ class Test_defender_antiphishing_policy_configured:
)
defender_client.antiphishing_policies = {
"Default": AntiphishingPolicy(
name="Default",
spoof_intelligence=True,
spoof_intelligence_action="Quarantine",
dmarc_reject_action="Quarantine",
dmarc_quarantine_action="Quarantine",
safety_tips=True,
unauthenticated_sender_action=True,
show_tag=True,
honor_dmarc_policy=True,
default=True,
),
"Policy1": AntiphishingPolicy(
name="Policy1",
spoof_intelligence=True,
spoof_intelligence_action="Quarantine",
dmarc_reject_action="Quarantine",
@@ -41,29 +116,59 @@ class Test_defender_antiphishing_policy_configured:
show_tag=True,
honor_dmarc_policy=True,
default=False,
)
),
}
defender_client.antiphising_rules = {
"Policy1": AntiphishingRule(state="Enabled")
defender_client.antiphishing_rules = {
"Policy1": AntiphishingRule(
state="Enabled",
priority=1,
users=["test@example.com"],
groups=["example_group"],
domains=["example.com"],
)
}
check = defender_antiphishing_policy_configured()
result = check.execute()
assert len(result) == 1
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Anti-phishing policy Policy1 is properly configured and enabled."
== "Default is properly configured in the default Defender Anti-Phishing Policy, but could be overridden by another bad-configured Custom Policy."
)
assert (
result[0].resource_name
== defender_client.antiphishing_policies["Default"].name
)
assert (
result[0].resource_id
== defender_client.antiphishing_policies["Default"].name
)
assert (
result[0].resource
== defender_client.antiphishing_policies["Default"].dict()
)
assert result[1].status == "PASS"
assert (
result[1].status_extended
== f"Custom Anti-phishing policy {defender_client.antiphishing_policies['Policy1'].name} is properly configured and includes users: {', '.join(defender_client.antiphishing_rules['Policy1'].users)}; groups: {', '.join(defender_client.antiphishing_rules['Policy1'].groups)}; domains: {', '.join(defender_client.antiphishing_rules['Policy1'].domains)}, "
f"with priority {defender_client.antiphishing_rules['Policy1'].priority} (0 is the highest). "
"Also, the default policy is properly configured, so entities not included by this custom policy could still be correctly protected."
)
assert (
result[1].resource_name
== defender_client.antiphishing_policies["Policy1"].name
)
assert (
result[1].resource_id
== defender_client.antiphishing_policies["Policy1"].name
)
assert (
result[1].resource
== defender_client.antiphishing_policies["Policy1"].dict()
)
assert result[0].resource_name == "Defender Anti-Phishing Policy"
assert result[0].resource_id == "Policy1"
assert result[0].location == "global"
def test_not_properly_configured_policy(self):
def test_case_3_default_ok_others_not(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -89,66 +194,9 @@ class Test_defender_antiphishing_policy_configured:
AntiphishingRule,
)
defender_client.antiphishing_policies = {
"Policy2": AntiphishingPolicy(
spoof_intelligence=False,
spoof_intelligence_action="None",
dmarc_reject_action="None",
dmarc_quarantine_action="None",
safety_tips=False,
unauthenticated_sender_action=False,
show_tag=False,
honor_dmarc_policy=False,
default=False,
)
}
defender_client.antiphising_rules = {
"Policy2": AntiphishingRule(state="Enabled")
}
check = defender_antiphishing_policy_configured()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Anti-phishing policy Policy2 is not properly configured."
)
assert (
result[0].resource
== defender_client.antiphishing_policies["Policy2"].dict()
)
assert result[0].resource_name == "Defender Anti-Phishing 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_antiphishing_policy_configured.defender_antiphishing_policy_configured.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antiphishing_policy_configured.defender_antiphishing_policy_configured import (
defender_antiphishing_policy_configured,
)
from prowler.providers.m365.services.defender.defender_service import (
AntiphishingPolicy,
)
defender_client.antiphishing_policies = {
"Default": AntiphishingPolicy(
name="Default",
spoof_intelligence=True,
spoof_intelligence_action="Quarantine",
dmarc_reject_action="Quarantine",
@@ -158,27 +206,149 @@ class Test_defender_antiphishing_policy_configured:
show_tag=True,
honor_dmarc_policy=True,
default=True,
),
"Policy1": AntiphishingPolicy(
name="Policy1",
spoof_intelligence=False,
spoof_intelligence_action="None",
dmarc_reject_action="None",
dmarc_quarantine_action="None",
safety_tips=False,
unauthenticated_sender_action=False,
show_tag=False,
honor_dmarc_policy=False,
default=False,
),
}
defender_client.antiphishing_rules = {
"Policy1": AntiphishingRule(
state="Enabled",
priority=1,
users=["test@example.com"],
groups=["example_group"],
domains=["example.com"],
)
}
defender_client.antiphising_rules = {}
check = defender_antiphishing_policy_configured()
result = check.execute()
assert len(result) == 1
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Anti-phishing policy Default is properly configured and enabled."
== "Default is properly configured in the default Defender Anti-Phishing Policy, but could be overridden by another bad-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.antiphishing_policies["Default"].dict()
)
assert result[0].resource_name == "Defender Anti-Phishing Policy"
assert result[0].resource_id == "Default"
assert result[0].location == "global"
def test_default_policy_not_properly_configured(self):
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Anti-phishing policy Policy1 is not properly configured and includes users: test@example.com; groups: example_group; domains: example.com, "
"with priority 1 (0 is the highest). However, the default policy is properly configured, so entities not included by this custom policy could be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert (
result[1].resource
== defender_client.antiphishing_policies["Policy1"].dict()
)
def test_case_4_default_not_ok_potential_false_positive(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_antiphishing_policy_configured.defender_antiphishing_policy_configured.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antiphishing_policy_configured.defender_antiphishing_policy_configured import (
defender_antiphishing_policy_configured,
)
from prowler.providers.m365.services.defender.defender_service import (
AntiphishingPolicy,
AntiphishingRule,
)
defender_client.antiphishing_policies = {
"Default": AntiphishingPolicy(
name="Default",
spoof_intelligence=False,
spoof_intelligence_action="None",
dmarc_reject_action="None",
dmarc_quarantine_action="None",
safety_tips=False,
unauthenticated_sender_action=False,
show_tag=False,
honor_dmarc_policy=False,
default=True,
),
"Policy1": AntiphishingPolicy(
name="Policy1",
spoof_intelligence=True,
spoof_intelligence_action="Quarantine",
dmarc_reject_action="Quarantine",
dmarc_quarantine_action="Quarantine",
safety_tips=True,
unauthenticated_sender_action=True,
show_tag=True,
honor_dmarc_policy=True,
default=False,
),
}
defender_client.antiphishing_rules = {
"Policy1": AntiphishingRule(
state="Enabled",
priority=1,
users=["test@example.com"],
groups=["example_group"],
domains=["example.com"],
)
}
check = defender_antiphishing_policy_configured()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is not properly configured in the default Defender Anti-Phishing Policy, but could be overridden by another well-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.antiphishing_policies["Default"].dict()
)
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Custom Anti-phishing policy Policy1 is properly configured and includes users: test@example.com; groups: example_group; domains: example.com, "
f"with priority {defender_client.antiphishing_rules['Policy1'].priority} (0 is the highest). "
"However, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert (
result[1].resource
== defender_client.antiphishing_policies["Policy1"].dict()
)
def test_case_5_default_policy_not_properly_configured(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -205,6 +375,7 @@ class Test_defender_antiphishing_policy_configured:
defender_client.antiphishing_policies = {
"Default": AntiphishingPolicy(
name="Default",
spoof_intelligence=False,
spoof_intelligence_action="None",
dmarc_reject_action="None",
@@ -216,7 +387,7 @@ class Test_defender_antiphishing_policy_configured:
default=True,
)
}
defender_client.antiphising_rules = {}
defender_client.antiphishing_rules = {}
check = defender_antiphishing_policy_configured()
result = check.execute()
@@ -224,15 +395,103 @@ class Test_defender_antiphishing_policy_configured:
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Anti-phishing policy Default is not properly configured."
== "Default is the only policy and it's not properly configured in the default Defender Anti-Phishing Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.antiphishing_policies["Default"].dict()
)
assert result[0].resource_name == "Defender Anti-Phishing Policy"
def test_case_6_both_policies_not_properly_configured(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_antiphishing_policy_configured.defender_antiphishing_policy_configured.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antiphishing_policy_configured.defender_antiphishing_policy_configured import (
defender_antiphishing_policy_configured,
)
from prowler.providers.m365.services.defender.defender_service import (
AntiphishingPolicy,
AntiphishingRule,
)
defender_client.antiphishing_policies = {
"Default": AntiphishingPolicy(
name="Default",
spoof_intelligence=False,
spoof_intelligence_action="None",
dmarc_reject_action="None",
dmarc_quarantine_action="None",
safety_tips=False,
unauthenticated_sender_action=False,
show_tag=False,
honor_dmarc_policy=False,
default=True,
),
"Policy1": AntiphishingPolicy(
name="Policy1",
spoof_intelligence=False,
spoof_intelligence_action="None",
dmarc_reject_action="None",
dmarc_quarantine_action="None",
safety_tips=False,
unauthenticated_sender_action=False,
show_tag=False,
honor_dmarc_policy=False,
default=False,
),
}
defender_client.antiphishing_rules = {
"Policy1": AntiphishingRule(
state="Enabled",
priority=1,
users=[],
groups=[],
domains=["example.com"],
)
}
check = defender_antiphishing_policy_configured()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is not properly configured in the default Defender Anti-Phishing Policy, but could be overridden by another well-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].location == "global"
assert (
result[0].resource
== defender_client.antiphishing_policies["Default"].dict()
)
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Anti-phishing policy Policy1 is not properly configured and includes domains: example.com, "
"with priority 1 (0 is the highest). Also, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert (
result[1].resource
== defender_client.antiphishing_policies["Policy1"].dict()
)
def test_no_antiphishing_policies(self):
defender_client = mock.MagicMock()
@@ -257,64 +516,8 @@ class Test_defender_antiphishing_policy_configured:
)
defender_client.antiphishing_policies = {}
defender_client.antiphising_rules = {}
defender_client.antiphishing_rules = {}
check = defender_antiphishing_policy_configured()
result = check.execute()
assert result == []
def test_custom_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_antiphishing_policy_configured.defender_antiphishing_policy_configured.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antiphishing_policy_configured.defender_antiphishing_policy_configured import (
defender_antiphishing_policy_configured,
)
from prowler.providers.m365.services.defender.defender_service import (
AntiphishingPolicy,
)
defender_client.antiphishing_policies = {
"PolicyX": AntiphishingPolicy(
spoof_intelligence=True,
spoof_intelligence_action="Quarantine",
dmarc_reject_action="Quarantine",
dmarc_quarantine_action="Quarantine",
safety_tips=True,
unauthenticated_sender_action=True,
show_tag=True,
honor_dmarc_policy=True,
default=False,
)
}
defender_client.antiphising_rules = {}
check = defender_antiphishing_policy_configured()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Anti-phishing policy PolicyX is not properly configured."
)
assert (
result[0].resource
== defender_client.antiphishing_policies["PolicyX"].dict()
)
assert result[0].resource_name == "Defender Anti-Phishing Policy"
assert result[0].resource_id == "PolicyX"
assert result[0].location == "global"
assert len(result) == 0
@@ -4,119 +4,7 @@ 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,
auto_forwarding_mode=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,
auto_forwarding_mode=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):
def test_case_1_default_policy_properly_configured(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -143,12 +31,13 @@ class Test_defender_antispam_outbound_policy_configured:
defender_client.outbound_spam_policies = {
"Default": OutboundSpamPolicy(
notify_sender_blocked=True,
name="Default",
notify_limit_exceeded=True,
notify_limit_exceeded_addresses=["test@correo.com"],
notify_sender_blocked_addresses=["test@correo.com"],
default=True,
notify_sender_blocked=True,
notify_limit_exceeded_addresses=["admin@example.com"],
notify_sender_blocked_addresses=["admin@example.com"],
auto_forwarding_mode=False,
default=True,
)
}
defender_client.outbound_spam_rules = {}
@@ -159,17 +48,271 @@ class Test_defender_antispam_outbound_policy_configured:
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Outbound Spam Policy Default is properly configured and enabled."
== "Default is the only policy and it's properly configured in the default Defender Outbound Spam Policy."
)
assert (
result[0].resource_name
== defender_client.outbound_spam_policies["Default"].name
)
assert result[0].resource_id == "Default"
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):
def test_case_2_all_policies_properly_configured(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 = {
"Default": OutboundSpamPolicy(
name="Default",
notify_limit_exceeded=True,
notify_sender_blocked=True,
notify_limit_exceeded_addresses=["admin@example.com"],
notify_sender_blocked_addresses=["admin@example.com"],
auto_forwarding_mode=False,
default=True,
),
"Policy1": OutboundSpamPolicy(
name="Policy1",
notify_limit_exceeded=True,
notify_sender_blocked=True,
notify_limit_exceeded_addresses=["admin@example.com"],
notify_sender_blocked_addresses=["admin@example.com"],
auto_forwarding_mode=False,
default=False,
),
}
defender_client.outbound_spam_rules = {
"Policy1": OutboundSpamRule(
state="Enabled",
priority=1,
users=["test@example.com"],
groups=["group1"],
domains=["example.com"],
)
}
check = defender_antispam_outbound_policy_configured()
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is properly configured in the default Defender Outbound Spam Policy, but could be overridden by another bad-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.outbound_spam_policies["Default"].dict()
)
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Custom Outbound Spam policy Policy1 is properly configured and includes users: test@example.com; groups: group1; domains: example.com, "
"with priority 1 (0 is the highest). Also, the default policy is properly configured, so entities not included by this custom policy could still be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert (
result[1].resource
== defender_client.outbound_spam_policies["Policy1"].dict()
)
def test_case_3_default_ok_others_not(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 = {
"Default": OutboundSpamPolicy(
name="Default",
notify_limit_exceeded=True,
notify_sender_blocked=True,
notify_limit_exceeded_addresses=["admin@example.com"],
notify_sender_blocked_addresses=["admin@example.com"],
auto_forwarding_mode=False,
default=True,
),
"Policy1": OutboundSpamPolicy(
name="Policy1",
notify_limit_exceeded=False,
notify_sender_blocked=False,
notify_limit_exceeded_addresses=[],
notify_sender_blocked_addresses=[],
auto_forwarding_mode=False,
default=False,
),
}
defender_client.outbound_spam_rules = {
"Policy1": OutboundSpamRule(
state="Enabled",
priority=1,
users=["test@example.com"],
groups=["group1"],
domains=["example.com"],
)
}
check = defender_antispam_outbound_policy_configured()
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is properly configured in the default Defender Outbound Spam Policy, but could be overridden by another bad-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.outbound_spam_policies["Default"].dict()
)
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Outbound Spam policy Policy1 is not properly configured and includes users: test@example.com; groups: group1; domains: example.com, "
"with priority 1 (0 is the highest). However, the default policy is properly configured, so entities not included by this custom policy could be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert (
result[1].resource
== defender_client.outbound_spam_policies["Policy1"].dict()
)
def test_case_4_default_not_ok_custom_good(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 = {
"Default": OutboundSpamPolicy(
name="Default",
notify_limit_exceeded=False,
notify_sender_blocked=False,
notify_limit_exceeded_addresses=[],
notify_sender_blocked_addresses=[],
auto_forwarding_mode=False,
default=True,
),
"Policy1": OutboundSpamPolicy(
name="Policy1",
notify_limit_exceeded=True,
notify_sender_blocked=True,
notify_limit_exceeded_addresses=["admin@example.com"],
notify_sender_blocked_addresses=["admin@example.com"],
auto_forwarding_mode=False,
default=False,
),
}
defender_client.outbound_spam_rules = {
"Policy1": OutboundSpamRule(
state="Enabled",
priority=0,
users=["user1@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = defender_antispam_outbound_policy_configured()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is not properly configured in the default Defender Outbound Spam Policy, but could be overridden by another well-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.outbound_spam_policies["Default"].dict()
)
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Custom Outbound Spam policy Policy1 is properly configured and includes users: user1@example.com; groups: group1; domains: domain.com, "
"with priority 0 (0 is the highest). However, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert (
result[1].resource
== defender_client.outbound_spam_policies["Policy1"].dict()
)
def test_case_5_only_default_not_ok(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -195,13 +338,14 @@ class Test_defender_antispam_outbound_policy_configured:
)
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,
"Default": OutboundSpamPolicy(
name="Default",
notify_limit_exceeded=False,
notify_sender_blocked=False,
notify_limit_exceeded_addresses=[],
notify_sender_blocked_addresses=[],
auto_forwarding_mode=False,
default=True,
)
}
defender_client.outbound_spam_rules = {}
@@ -212,15 +356,98 @@ class Test_defender_antispam_outbound_policy_configured:
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Outbound Spam Policy PolicyX is not properly configured."
== "Default is the only policy and it's not properly configured in the default Defender Outbound Spam Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.outbound_spam_policies["PolicyX"].dict()
== defender_client.outbound_spam_policies["Default"].dict()
)
def test_case_6_default_and_custom_not_ok(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 = {
"Default": OutboundSpamPolicy(
name="Default",
notify_limit_exceeded=False,
notify_sender_blocked=False,
notify_limit_exceeded_addresses=[],
notify_sender_blocked_addresses=[],
auto_forwarding_mode=False,
default=True,
),
"Policy1": OutboundSpamPolicy(
name="Policy1",
notify_limit_exceeded=False,
notify_sender_blocked=False,
notify_limit_exceeded_addresses=[],
notify_sender_blocked_addresses=[],
auto_forwarding_mode=False,
default=False,
),
}
defender_client.outbound_spam_rules = {
"Policy1": OutboundSpamRule(
state="Enabled",
priority=5,
users=["user@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = defender_antispam_outbound_policy_configured()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is not properly configured in the default Defender Outbound Spam Policy, but could be overridden by another well-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.outbound_spam_policies["Default"].dict()
)
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Outbound Spam policy Policy1 is not properly configured and includes users: user@example.com; groups: group1; domains: domain.com, "
"with priority 5 (0 is the highest). Also, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert (
result[1].resource
== defender_client.outbound_spam_policies["Policy1"].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()
@@ -4,6 +4,456 @@ from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
class Test_defender_antispam_outbound_policy_forwarding_disabled:
def test_case_1_default_policy_forwarding_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_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
)
defender_client.outbound_spam_policies = {
"Default": OutboundSpamPolicy(
name="Default",
auto_forwarding_mode=False,
notify_limit_exceeded=True,
notify_sender_blocked=True,
notify_limit_exceeded_addresses=["admin@example.com"],
notify_sender_blocked_addresses=["admin@example.com"],
default=True,
)
}
defender_client.outbound_spam_rules = {}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is the only policy and mail forwarding is disabled."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.outbound_spam_policies["Default"].dict()
)
def test_case_2_all_policies_forwarding_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_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
OutboundSpamRule,
)
defender_client.outbound_spam_policies = {
"Default": OutboundSpamPolicy(
name="Default",
auto_forwarding_mode=False,
notify_limit_exceeded=True,
notify_sender_blocked=True,
notify_limit_exceeded_addresses=["admin@example.com"],
notify_sender_blocked_addresses=["admin@example.com"],
default=True,
),
"Policy1": OutboundSpamPolicy(
name="Policy1",
auto_forwarding_mode=False,
notify_limit_exceeded=True,
notify_sender_blocked=True,
notify_limit_exceeded_addresses=["admin@example.com"],
notify_sender_blocked_addresses=["admin@example.com"],
default=False,
),
}
defender_client.outbound_spam_rules = {
"Policy1": OutboundSpamRule(
state="Enabled",
priority=1,
users=["test@example.com"],
groups=["group1"],
domains=["example.com"],
)
}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is the default policy and mail forwarding is disabled, but it could be overridden by another misconfigured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.outbound_spam_policies["Default"].dict()
)
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Custom Outbound Spam policy Policy1 disables mail forwarding and includes users: test@example.com; groups: group1; domains: example.com, "
"with priority 1 (0 is the highest). Also, the default policy disables mail forwarding, so entities not included by this custom policy could still be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert (
result[1].resource
== defender_client.outbound_spam_policies["Policy1"].dict()
)
def test_case_3_default_ok_custom_not(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_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
OutboundSpamRule,
)
defender_client.outbound_spam_policies = {
"Default": OutboundSpamPolicy(
name="Default",
auto_forwarding_mode=False,
notify_limit_exceeded=True,
notify_sender_blocked=True,
notify_limit_exceeded_addresses=["admin@example.com"],
notify_sender_blocked_addresses=["admin@example.com"],
default=True,
),
"Policy1": OutboundSpamPolicy(
name="Policy1",
auto_forwarding_mode=True,
notify_limit_exceeded=False,
notify_sender_blocked=False,
notify_limit_exceeded_addresses=[],
notify_sender_blocked_addresses=[],
default=False,
),
}
defender_client.outbound_spam_rules = {
"Policy1": OutboundSpamRule(
state="Enabled",
priority=1,
users=["test@example.com"],
groups=["group1"],
domains=["example.com"],
)
}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is the default policy and mail forwarding is disabled, but it could be overridden by another misconfigured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.outbound_spam_policies["Default"].dict()
)
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Outbound Spam policy Policy1 allows mail forwarding and includes users: test@example.com; groups: group1; domains: example.com, "
"with priority 1 (0 is the highest). However, the default policy disables mail forwarding, so entities not included by this custom policy could be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert (
result[1].resource
== defender_client.outbound_spam_policies["Policy1"].dict()
)
def test_case_4_default_not_ok_custom_good(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_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
OutboundSpamRule,
)
defender_client.outbound_spam_policies = {
"Default": OutboundSpamPolicy(
name="Default",
auto_forwarding_mode=True,
notify_limit_exceeded=False,
notify_sender_blocked=False,
notify_limit_exceeded_addresses=[],
notify_sender_blocked_addresses=[],
default=True,
),
"Policy1": OutboundSpamPolicy(
name="Policy1",
auto_forwarding_mode=False,
notify_limit_exceeded=True,
notify_sender_blocked=True,
notify_limit_exceeded_addresses=["admin@example.com"],
notify_sender_blocked_addresses=["admin@example.com"],
default=False,
),
}
defender_client.outbound_spam_rules = {
"Policy1": OutboundSpamRule(
state="Enabled",
priority=0,
users=["user1@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is the default policy and mail forwarding is allowed, but it could be overridden by another well-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.outbound_spam_policies["Default"].dict()
)
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Custom Outbound Spam policy Policy1 disables mail forwarding and includes users: user1@example.com; groups: group1; domains: domain.com, "
"with priority 0 (0 is the highest). However, the default policy allows mail forwarding, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert (
result[1].resource
== defender_client.outbound_spam_policies["Policy1"].dict()
)
def test_case_5_only_default_not_ok(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_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
)
defender_client.outbound_spam_policies = {
"Default": OutboundSpamPolicy(
name="Default",
auto_forwarding_mode=True,
notify_limit_exceeded=False,
notify_sender_blocked=False,
notify_limit_exceeded_addresses=[],
notify_sender_blocked_addresses=[],
default=True,
)
}
defender_client.outbound_spam_rules = {}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is the only policy and mail forwarding is allowed."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.outbound_spam_policies["Default"].dict()
)
def test_case_6_default_and_custom_not_ok(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_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
OutboundSpamRule,
)
defender_client.outbound_spam_policies = {
"Default": OutboundSpamPolicy(
name="Default",
auto_forwarding_mode=True,
notify_limit_exceeded=False,
notify_sender_blocked=False,
notify_limit_exceeded_addresses=[],
notify_sender_blocked_addresses=[],
default=True,
),
"Policy1": OutboundSpamPolicy(
name="Policy1",
auto_forwarding_mode=True,
notify_limit_exceeded=False,
notify_sender_blocked=False,
notify_limit_exceeded_addresses=[],
notify_sender_blocked_addresses=[],
default=False,
),
}
defender_client.outbound_spam_rules = {
"Policy1": OutboundSpamRule(
state="Enabled",
priority=1,
users=["user@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is the default policy and mail forwarding is allowed, but it could be overridden by another well-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert (
result[0].resource
== defender_client.outbound_spam_policies["Default"].dict()
)
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Outbound Spam policy Policy1 allows mail forwarding and includes users: user@example.com; groups: group1; domains: domain.com, "
"with priority 1 (0 is the highest). Also, the default policy allows mail forwarding, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert (
result[1].resource
== defender_client.outbound_spam_policies["Policy1"].dict()
)
def test_no_outbound_spam_policies(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
@@ -32,221 +482,3 @@ class Test_defender_antispam_outbound_policy_forwarding_disabled:
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 0
def test_forwarding_disabled_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_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
OutboundSpamRule,
)
defender_client.outbound_spam_policies = {
"Policy1": OutboundSpamPolicy(
default=False,
notify_sender_blocked=True,
notify_limit_exceeded=True,
notify_limit_exceeded_addresses=["test@correo.com"],
notify_sender_blocked_addresses=["test@correo.com"],
auto_forwarding_mode=False,
)
}
defender_client.outbound_spam_rules = {
"Policy1": OutboundSpamRule(state="Enabled")
}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Outbound Spam Policy Policy1 does not allow mail forwarding."
)
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_forwarding_enabled_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_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
OutboundSpamRule,
)
defender_client.outbound_spam_policies = {
"Policy2": OutboundSpamPolicy(
default=False,
notify_sender_blocked=True,
notify_limit_exceeded=True,
notify_limit_exceeded_addresses=["test@correo.com"],
notify_sender_blocked_addresses=["test@correo.com"],
auto_forwarding_mode=True,
)
}
defender_client.outbound_spam_rules = {
"Policy2": OutboundSpamRule(state="Enabled")
}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Outbound Spam Policy Policy2 does allow mail forwarding."
)
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_forwarding_disabled_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_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
)
defender_client.outbound_spam_policies = {
"Default": OutboundSpamPolicy(
default=True,
notify_sender_blocked=True,
notify_limit_exceeded=True,
notify_limit_exceeded_addresses=["test@correo.com"],
notify_sender_blocked_addresses=["test@correo.com"],
auto_forwarding_mode=False,
)
}
defender_client.outbound_spam_rules = {}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Outbound Spam Policy Default does not allow mail forwarding."
)
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_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
)
defender_client.outbound_spam_policies = {
"PolicyX": OutboundSpamPolicy(
default=False,
notify_sender_blocked=True,
notify_limit_exceeded=True,
notify_limit_exceeded_addresses=["test@correo.com"],
notify_sender_blocked_addresses=["test@correo.com"],
auto_forwarding_mode=False,
)
}
defender_client.outbound_spam_rules = {}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Outbound Spam Policy PolicyX does allow mail forwarding."
)
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"
@@ -4,7 +4,7 @@ from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
class Test_defender_antispam_policy_inbound_no_allowed_domains:
def test_policy_without_allowed_domains(self):
def test_case_1_default_policy_no_allowed_domains(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -31,26 +31,240 @@ class Test_defender_antispam_policy_inbound_no_allowed_domains:
defender_client.inbound_spam_policies = [
DefenderInboundSpamPolicy(
identity="Policy1",
identity="Default",
default=True,
allowed_sender_domains=[],
)
]
defender_client.inbound_spam_rules = {}
check = defender_antispam_policy_inbound_no_allowed_domains()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Inbound anti-spam policy Policy1 does not contain allowed domains."
== "Default is the only policy and it does not contain allowed domains."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.inbound_spam_policies[0].dict()
assert result[0].resource_name == "Defender Inbound Spam Policy"
assert result[0].resource_id == "Policy1"
assert result[0].location == "global"
def test_policy_with_allowed_domains(self):
def test_case_2_all_policies_no_allowed_domains(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_policy_inbound_no_allowed_domains.defender_antispam_policy_inbound_no_allowed_domains.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_policy_inbound_no_allowed_domains.defender_antispam_policy_inbound_no_allowed_domains import (
defender_antispam_policy_inbound_no_allowed_domains,
)
from prowler.providers.m365.services.defender.defender_service import (
DefenderInboundSpamPolicy,
InboundSpamRule,
)
defender_client.inbound_spam_policies = [
DefenderInboundSpamPolicy(
identity="Default",
default=True,
allowed_sender_domains=[],
),
DefenderInboundSpamPolicy(
identity="Policy1",
default=False,
allowed_sender_domains=[],
),
]
defender_client.inbound_spam_rules = {
"Policy1": InboundSpamRule(
state="Enabled",
priority=1,
users=["user1@example.com"],
groups=["group1"],
domains=["example.com"],
)
}
check = defender_antispam_policy_inbound_no_allowed_domains()
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is the default policy and it does not contain allowed domains, but it could be overridden by another misconfigured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.inbound_spam_policies[0].dict()
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Custom Inbound Spam policy Policy1 does not contain allowed domains and includes users: user1@example.com; groups: group1; domains: example.com, "
"with priority 1 (0 is the highest). Also, the default policy does not contain allowed domains, so entities not included by this custom policy could still be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert result[1].resource == defender_client.inbound_spam_policies[1].dict()
def test_case_3_default_ok_custom_with_allowed_domains(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_policy_inbound_no_allowed_domains.defender_antispam_policy_inbound_no_allowed_domains.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_policy_inbound_no_allowed_domains.defender_antispam_policy_inbound_no_allowed_domains import (
defender_antispam_policy_inbound_no_allowed_domains,
)
from prowler.providers.m365.services.defender.defender_service import (
DefenderInboundSpamPolicy,
InboundSpamRule,
)
defender_client.inbound_spam_policies = [
DefenderInboundSpamPolicy(
identity="Default",
default=True,
allowed_sender_domains=[],
),
DefenderInboundSpamPolicy(
identity="Policy1",
default=False,
allowed_sender_domains=["spam.com"],
),
]
defender_client.inbound_spam_rules = {
"Policy1": InboundSpamRule(
state="Enabled",
priority=2,
users=["user@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = defender_antispam_policy_inbound_no_allowed_domains()
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is the default policy and it does not contain allowed domains, but it could be overridden by another misconfigured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.inbound_spam_policies[0].dict()
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Inbound Spam policy Policy1 contains allowed domains and includes users: user@example.com; groups: group1; domains: domain.com, "
"with priority 2 (0 is the highest). However, the default policy does not contain allowed domains, so entities not included by this custom policy could be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert result[1].resource == defender_client.inbound_spam_policies[1].dict()
def test_case_4_default_with_allowed_domains_custom_ok(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_policy_inbound_no_allowed_domains.defender_antispam_policy_inbound_no_allowed_domains.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_policy_inbound_no_allowed_domains.defender_antispam_policy_inbound_no_allowed_domains import (
defender_antispam_policy_inbound_no_allowed_domains,
)
from prowler.providers.m365.services.defender.defender_service import (
DefenderInboundSpamPolicy,
InboundSpamRule,
)
defender_client.inbound_spam_policies = [
DefenderInboundSpamPolicy(
identity="Default",
default=True,
allowed_sender_domains=["example.org"],
),
DefenderInboundSpamPolicy(
identity="Policy1",
default=False,
allowed_sender_domains=[],
),
]
defender_client.inbound_spam_rules = {
"Policy1": InboundSpamRule(
state="Enabled",
priority=0,
users=["user@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = defender_antispam_policy_inbound_no_allowed_domains()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is the default policy and it contains allowed domains: example.org, but it could be overridden by another well-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.inbound_spam_policies[0].dict()
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Custom Inbound Spam policy Policy1 does not contain allowed domains and includes users: user@example.com; groups: group1; domains: domain.com, "
"with priority 0 (0 is the highest). However, the default policy contains allowed domains, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert result[1].resource == defender_client.inbound_spam_policies[1].dict()
def test_case_5_default_with_allowed_domains(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -77,24 +291,96 @@ class Test_defender_antispam_policy_inbound_no_allowed_domains:
defender_client.inbound_spam_policies = [
DefenderInboundSpamPolicy(
identity="Policy2",
allowed_sender_domains=["bad-domain.com"],
identity="Default",
default=True,
allowed_sender_domains=["example.com"],
)
]
defender_client.inbound_spam_rules = {}
check = defender_antispam_policy_inbound_no_allowed_domains()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Inbound anti-spam policy Policy2 contains allowed domains: ['bad-domain.com']."
== "Default is the only policy and it contains allowed domains: example.com."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.inbound_spam_policies[0].dict()
assert result[0].resource_name == "Defender Inbound Spam Policy"
assert result[0].resource_id == "Policy2"
assert result[0].location == "global"
def test_case_6_both_default_and_custom_with_allowed_domains(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_policy_inbound_no_allowed_domains.defender_antispam_policy_inbound_no_allowed_domains.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_policy_inbound_no_allowed_domains.defender_antispam_policy_inbound_no_allowed_domains import (
defender_antispam_policy_inbound_no_allowed_domains,
)
from prowler.providers.m365.services.defender.defender_service import (
DefenderInboundSpamPolicy,
InboundSpamRule,
)
defender_client.inbound_spam_policies = [
DefenderInboundSpamPolicy(
identity="Default",
default=True,
allowed_sender_domains=["example.com"],
),
DefenderInboundSpamPolicy(
identity="Policy1",
default=False,
allowed_sender_domains=["spam.com"],
),
]
defender_client.inbound_spam_rules = {
"Policy1": InboundSpamRule(
state="Enabled",
priority=5,
users=["user@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = defender_antispam_policy_inbound_no_allowed_domains()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is the default policy and it contains allowed domains: example.com, but it could be overridden by another well-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.inbound_spam_policies[0].dict()
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Inbound Spam policy Policy1 contains allowed domains and includes users: user@example.com; groups: group1; domains: domain.com, "
"with priority 5 (0 is the highest). Also, the default policy contains allowed domains, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert result[1].resource == defender_client.inbound_spam_policies[1].dict()
def test_no_inbound_spam_policies(self):
defender_client = mock.MagicMock()
@@ -119,8 +405,8 @@ class Test_defender_antispam_policy_inbound_no_allowed_domains:
)
defender_client.inbound_spam_policies = []
defender_client.inbound_spam_rules = {}
check = defender_antispam_policy_inbound_no_allowed_domains()
result = check.execute()
assert len(result) == 0
@@ -4,7 +4,7 @@ from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
class Test_defender_malware_policy_common_attachments_filter_enabled:
def test_enable_file_filter_disabled(self):
def test_case_1_only_default_policy_enabled(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -31,30 +31,29 @@ class Test_defender_malware_policy_common_attachments_filter_enabled:
defender_client.malware_policies = [
MalwarePolicy(
enable_file_filter=False,
identity="Policy1",
enable_internal_sender_admin_notifications=False,
internal_sender_admin_address="",
file_types=[],
identity="Default",
enable_file_filter=True,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["exe", "js"],
is_default=True,
),
)
]
defender_client.malware_rules = {}
check = defender_malware_policy_common_attachments_filter_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Common Attachment Types Filter is not enabled in anti-malware policy Policy1."
== "Default is the only policy and Common Attachment Types Filter is enabled."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
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_enable_file_filter_enabled(self):
def test_case_2_all_policies_enabled(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -77,34 +76,345 @@ class Test_defender_malware_policy_common_attachments_filter_enabled:
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
MalwareRule,
)
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=True,
identity="Policy1",
enable_internal_sender_admin_notifications=False,
internal_sender_admin_address="",
file_types=[],
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["exe", "js"],
is_default=True,
),
MalwarePolicy(
identity="Policy1",
enable_file_filter=True,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["exe"],
is_default=False,
),
]
defender_client.malware_rules = {
"Policy1": MalwareRule(
state="Enabled",
priority=1,
users=["user1@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = defender_malware_policy_common_attachments_filter_enabled()
result = check.execute()
assert len(result) == 1
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Common Attachment Types Filter is enabled in anti-malware policy Policy1."
== "Default is the default policy and Common Attachment Types Filter is enabled, but it could be overridden by another misconfigured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
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_no_policy(self):
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Custom Malware policy Policy1 enables Common Attachment Types Filter and includes users: user1@example.com; groups: group1; domains: domain.com, "
"with priority 1 (0 is the highest). Also, the default policy enables the filter, so entities not included by this custom policy could still be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert result[1].resource == defender_client.malware_policies[1].dict()
def test_case_3_default_ok_custom_not_ok(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_common_attachments_filter_enabled.defender_malware_policy_common_attachments_filter_enabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_malware_policy_common_attachments_filter_enabled.defender_malware_policy_common_attachments_filter_enabled import (
defender_malware_policy_common_attachments_filter_enabled,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
MalwareRule,
)
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=True,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["exe"],
is_default=True,
),
MalwarePolicy(
identity="Policy1",
enable_file_filter=False,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["js"],
is_default=False,
),
]
defender_client.malware_rules = {
"Policy1": MalwareRule(
state="Enabled",
priority=2,
users=["user@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = defender_malware_policy_common_attachments_filter_enabled()
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is the default policy and Common Attachment Types Filter is enabled, but it could be overridden by another misconfigured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.malware_policies[0].dict()
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Malware policy Policy1 does not enable Common Attachment Types Filter and includes users: user@example.com; groups: group1; domains: domain.com, "
"with priority 2 (0 is the highest). However, the default policy enables the filter, so entities not included by this custom policy could be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert result[1].resource == defender_client.malware_policies[1].dict()
def test_case_4_default_not_ok_custom_ok(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_common_attachments_filter_enabled.defender_malware_policy_common_attachments_filter_enabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_malware_policy_common_attachments_filter_enabled.defender_malware_policy_common_attachments_filter_enabled import (
defender_malware_policy_common_attachments_filter_enabled,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
MalwareRule,
)
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=False,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=[],
is_default=True,
),
MalwarePolicy(
identity="Policy1",
enable_file_filter=True,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["exe"],
is_default=False,
),
]
defender_client.malware_rules = {
"Policy1": MalwareRule(
state="Enabled",
priority=0,
users=["user@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = defender_malware_policy_common_attachments_filter_enabled()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is the default policy and Common Attachment Types Filter is not enabled, but it could be overridden by another well-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.malware_policies[0].dict()
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Custom Malware policy Policy1 enables Common Attachment Types Filter and includes users: user@example.com; groups: group1; domains: domain.com, "
"with priority 0 (0 is the highest). However, the default policy does not enable the filter, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert result[1].resource == defender_client.malware_policies[1].dict()
def test_case_5_only_default_not_ok(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_common_attachments_filter_enabled.defender_malware_policy_common_attachments_filter_enabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_malware_policy_common_attachments_filter_enabled.defender_malware_policy_common_attachments_filter_enabled import (
defender_malware_policy_common_attachments_filter_enabled,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
)
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=False,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=[],
is_default=True,
)
]
defender_client.malware_rules = {}
check = defender_malware_policy_common_attachments_filter_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is the only policy and Common Attachment Types Filter is not enabled."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.malware_policies[0].dict()
def test_case_6_default_and_custom_not_ok(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_common_attachments_filter_enabled.defender_malware_policy_common_attachments_filter_enabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_malware_policy_common_attachments_filter_enabled.defender_malware_policy_common_attachments_filter_enabled import (
defender_malware_policy_common_attachments_filter_enabled,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
MalwareRule,
)
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=False,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=[],
is_default=True,
),
MalwarePolicy(
identity="Policy1",
enable_file_filter=False,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["exe"],
is_default=False,
),
]
defender_client.malware_rules = {
"Policy1": MalwareRule(
state="Enabled",
priority=5,
users=["user1@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = defender_malware_policy_common_attachments_filter_enabled()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is the default policy and Common Attachment Types Filter is not enabled, but it could be overridden by another well-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.malware_policies[0].dict()
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Malware policy Policy1 does not enable Common Attachment Types Filter and includes users: user1@example.com; groups: group1; domains: domain.com, "
"with priority 5 (0 is the highest). Also, the default policy does not enable the filter, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert result[1].resource == defender_client.malware_policies[1].dict()
def test_no_malware_policies(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -127,16 +437,8 @@ class Test_defender_malware_policy_common_attachments_filter_enabled:
)
defender_client.malware_policies = []
defender_client.malware_rules = {}
check = defender_malware_policy_common_attachments_filter_enabled()
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"
assert len(result) == 0
@@ -1,53 +1,10 @@
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):
def test_case_1_default_policy_properly_configured(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -68,192 +25,430 @@ class Test_defender_malware_policy_comprehensive_attachments_filter_applied:
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,
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
)
defender_client.audit_config = {
"recommended_blocked_file_types": ["exe", "bat", "js"]
}
defender_client.malware_rules = {
"PolicyPartial": MalwareRule(state="Enabled")
}
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=True,
identity="PolicyPartial",
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["exe"],
file_types=["exe", "bat", "js"],
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 == "PASS"
assert (
result[0].status_extended
== "Default is the only policy and Common Attachment Types Filter is properly configured."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.malware_policies[0].dict()
def test_case_2_all_policies_properly_configured(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,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
MalwareRule,
)
defender_client.audit_config = {
"recommended_blocked_file_types": ["exe", "bat"]
}
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=True,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["exe", "bat"],
is_default=True,
),
MalwarePolicy(
identity="Custom1",
enable_file_filter=True,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["exe", "bat"],
is_default=False,
),
]
defender_client.malware_rules = {
"Custom1": MalwareRule(
state="Enabled",
priority=1,
users=["user1@example.com"],
groups=["group1"],
domains=["example.com"],
)
}
check = defender_malware_policy_comprehensive_attachments_filter_applied()
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is the default policy and Common Attachment Types Filter is properly configured, but it could be overridden by another misconfigured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.malware_policies[0].dict()
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Custom Malware policy Custom1 is properly configured and includes users: user1@example.com; groups: group1; domains: example.com, "
"with priority 1 (0 is the highest). Also, the default policy is properly configured, so entities not included by this custom policy could still be correctly protected."
)
assert result[1].resource_name == "Custom1"
assert result[1].resource_id == "Custom1"
assert result[1].resource == defender_client.malware_policies[1].dict()
def test_case_3_default_ok_custom_not(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,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
MalwareRule,
)
defender_client.audit_config = {
"recommended_blocked_file_types": ["exe", "bat"]
}
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=True,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["exe", "bat"],
is_default=True,
),
MalwarePolicy(
identity="Custom1",
enable_file_filter=True,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["exe"], # missing bat
is_default=False,
),
]
defender_client.malware_rules = {
"Custom1": MalwareRule(
state="Enabled",
priority=1,
users=["user1@example.com"],
groups=["group1"],
domains=["example.com"],
)
}
check = defender_malware_policy_comprehensive_attachments_filter_applied()
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is the default policy and Common Attachment Types Filter is properly configured, but it could be overridden by another misconfigured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.malware_policies[0].dict()
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Malware policy Custom1 is not properly configured and includes users: user1@example.com; groups: group1; domains: example.com, "
"with priority 1 (0 is the highest). Missing recommended file types: bat. However, the default policy is properly configured, so entities not included by this custom policy could be correctly protected."
)
assert result[1].resource_name == "Custom1"
assert result[1].resource_id == "Custom1"
assert result[1].resource == defender_client.malware_policies[1].dict()
def test_case_4_default_not_ok_custom_ok(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,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
MalwareRule,
)
defender_client.audit_config = {
"recommended_blocked_file_types": ["exe", "bat"]
}
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=False,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=[],
is_default=True,
),
MalwarePolicy(
identity="Custom1",
enable_file_filter=True,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["exe", "bat"],
is_default=False,
),
]
defender_client.malware_rules = {
"Custom1": MalwareRule(
state="Enabled",
priority=0,
users=["user1@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = defender_malware_policy_comprehensive_attachments_filter_applied()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is the default policy and Common Attachment Types Filter is not properly configured, but it could be overridden by another well-configured Custom Policy. Missing recommended file types: exe, bat."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.malware_policies[0].dict()
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Custom Malware policy Custom1 is properly configured and includes users: user1@example.com; groups: group1; domains: domain.com, "
"with priority 0 (0 is the highest). However, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Custom1"
assert result[1].resource_id == "Custom1"
assert result[1].resource == defender_client.malware_policies[1].dict()
def test_case_5_only_default_not_ok(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,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
)
defender_client.audit_config = {
"recommended_blocked_file_types": ["exe", "bat"]
}
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=False,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=[],
is_default=True,
)
]
defender_client.malware_rules = {}
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."
== "Default is the only policy and Common Attachment Types Filter is not properly configured. Missing recommended file types: exe, bat."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
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_case_6_default_and_custom_not_ok(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,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
MalwareRule,
)
defender_client.audit_config = {
"recommended_blocked_file_types": ["exe", "bat"]
}
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=False,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=[],
is_default=True,
),
MalwarePolicy(
identity="Custom1",
enable_file_filter=True,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=["exe"], # missing bat
is_default=False,
),
]
defender_client.malware_rules = {
"Custom1": MalwareRule(
state="Enabled",
priority=2,
users=["user@example.com"],
groups=["group1"],
domains=["example.com"],
)
}
check = defender_malware_policy_comprehensive_attachments_filter_applied()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is the default policy and Common Attachment Types Filter is not properly configured, but it could be overridden by another well-configured Custom Policy. Missing recommended file types: exe, bat."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.malware_policies[0].dict()
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Malware policy Custom1 is not properly configured and includes users: user@example.com; groups: group1; domains: example.com, "
"with priority 2 (0 is the highest). Missing recommended file types: bat. Also, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Custom1"
assert result[1].resource_id == "Custom1"
assert result[1].resource == defender_client.malware_policies[1].dict()
def test_no_malware_policies(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
defender_client.malware_policies = []
defender_client.malware_rules = {}
defender_client.audit_config = {}
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) == 0
@@ -4,7 +4,7 @@ from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
class Test_defender_malware_policy_notifications_internal_users_malware_enabled:
def test_notifications_disabled(self):
def test_case_1_default_properly_configured(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -31,32 +31,322 @@ class Test_defender_malware_policy_notifications_internal_users_malware_enabled:
defender_client.malware_policies = [
MalwarePolicy(
enable_file_filter=True,
identity="Default",
enable_file_filter=False,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=[],
is_default=True,
)
]
defender_client.malware_rules = {}
check = (
defender_malware_policy_notifications_internal_users_malware_enabled()
)
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is the only policy and notifications for internal users sending malware are enabled."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.malware_policies[0].dict()
def test_case_2_default_and_custom_properly_configured(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_notifications_internal_users_malware_enabled.defender_malware_policy_notifications_internal_users_malware_enabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_malware_policy_notifications_internal_users_malware_enabled.defender_malware_policy_notifications_internal_users_malware_enabled import (
defender_malware_policy_notifications_internal_users_malware_enabled,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
MalwareRule,
)
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=False,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=[],
is_default=True,
),
MalwarePolicy(
identity="Policy1",
enable_file_filter=False,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=[],
is_default=False,
),
]
defender_client.malware_rules = {
"Policy1": MalwareRule(
state="Enabled",
priority=1,
users=["user@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = (
defender_malware_policy_notifications_internal_users_malware_enabled()
)
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is the default policy and notifications for internal users sending malware are enabled, but it could be overridden by another misconfigured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.malware_policies[0].dict()
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Custom Malware policy Policy1 is properly configured and includes users: user@example.com; groups: group1; domains: domain.com, "
"with priority 1 (0 is the highest). Also, the default policy is properly configured, so entities not included by this custom policy could still be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert result[1].resource == defender_client.malware_policies[1].dict()
def test_case_3_default_ok_custom_not_configured(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_notifications_internal_users_malware_enabled.defender_malware_policy_notifications_internal_users_malware_enabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_malware_policy_notifications_internal_users_malware_enabled.defender_malware_policy_notifications_internal_users_malware_enabled import (
defender_malware_policy_notifications_internal_users_malware_enabled,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
MalwareRule,
)
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=False,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=[],
is_default=True,
),
MalwarePolicy(
identity="Policy1",
enable_file_filter=False,
enable_internal_sender_admin_notifications=False,
internal_sender_admin_address="",
file_types=[],
is_default=False,
),
]
defender_client.malware_rules = {
"Policy1": MalwareRule(
state="Enabled",
priority=2,
users=["internal@example.com"],
groups=["group2"],
domains=["example.org"],
)
}
check = (
defender_malware_policy_notifications_internal_users_malware_enabled()
)
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Default is the default policy and notifications for internal users sending malware are enabled, but it could be overridden by another misconfigured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.malware_policies[0].dict()
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Malware policy Policy1 is not properly configured and includes users: internal@example.com; groups: group2; domains: example.org, "
"with priority 2 (0 is the highest). However, the default policy is properly configured, so entities not included by this custom policy could be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert result[1].resource == defender_client.malware_policies[1].dict()
def test_case_4_default_not_ok_custom_properly_configured(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_notifications_internal_users_malware_enabled.defender_malware_policy_notifications_internal_users_malware_enabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_malware_policy_notifications_internal_users_malware_enabled.defender_malware_policy_notifications_internal_users_malware_enabled import (
defender_malware_policy_notifications_internal_users_malware_enabled,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
MalwareRule,
)
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=False,
enable_internal_sender_admin_notifications=False,
internal_sender_admin_address="",
file_types=[],
is_default=True,
),
MalwarePolicy(
identity="Policy1",
enable_file_filter=False,
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="admin@example.com",
file_types=[],
is_default=False,
),
]
defender_client.malware_rules = {
"Policy1": MalwareRule(
state="Enabled",
priority=0,
users=["user@example.com"],
groups=["group1"],
domains=["domain.com"],
)
}
check = (
defender_malware_policy_notifications_internal_users_malware_enabled()
)
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Default is the default policy and notifications for internal users sending malware are not enabled, but it could be overridden by another well-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
assert result[0].resource == defender_client.malware_policies[0].dict()
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Custom Malware policy Policy1 is properly configured and includes users: user@example.com; groups: group1; domains: domain.com, "
"with priority 0 (0 is the highest). However, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert result[1].resource == defender_client.malware_policies[1].dict()
def test_case_5_only_default_not_ok(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_notifications_internal_users_malware_enabled.defender_malware_policy_notifications_internal_users_malware_enabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_malware_policy_notifications_internal_users_malware_enabled.defender_malware_policy_notifications_internal_users_malware_enabled import (
defender_malware_policy_notifications_internal_users_malware_enabled,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
)
defender_client.malware_policies = [
MalwarePolicy(
identity="Default",
enable_file_filter=False,
enable_internal_sender_admin_notifications=False,
internal_sender_admin_address="",
file_types=[],
is_default=True,
)
]
defender_client.malware_rules = {}
check = (
defender_malware_policy_notifications_internal_users_malware_enabled()
)
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Notifications for internal users sending malware are not enabled."
== "Default is the only policy and notifications for internal users sending malware are not enabled."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
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_notifications_enabled_without_email(self):
def test_case_6_default_and_custom_not_configured(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -79,36 +369,64 @@ class Test_defender_malware_policy_notifications_internal_users_malware_enabled:
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
MalwareRule,
)
defender_client.malware_policies = [
MalwarePolicy(
enable_file_filter=True,
identity="Default",
enable_internal_sender_admin_notifications=True,
enable_file_filter=False,
enable_internal_sender_admin_notifications=False,
internal_sender_admin_address="",
file_types=[],
is_default=True,
)
),
MalwarePolicy(
identity="Policy1",
enable_file_filter=False,
enable_internal_sender_admin_notifications=False,
internal_sender_admin_address="",
file_types=[],
is_default=False,
),
]
defender_client.malware_rules = {
"Policy1": MalwareRule(
state="Enabled",
priority=3,
users=["u@example.com"],
groups=["g1"],
domains=["d.com"],
)
}
check = (
defender_malware_policy_notifications_internal_users_malware_enabled()
)
result = check.execute()
assert len(result) == 2
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Notifications for internal users sending malware are enabled, but no email addresses are configured."
== "Default is the default policy and notifications for internal users sending malware are not enabled, but it could be overridden by another well-configured Custom Policy."
)
assert result[0].resource_name == "Default"
assert result[0].resource_id == "Default"
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_notifications_enabled_with_email(self):
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== "Custom Malware policy Policy1 is not properly configured and includes users: u@example.com; groups: g1; domains: d.com, "
"with priority 3 (0 is the highest). Also, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
assert result[1].resource_name == "Policy1"
assert result[1].resource_id == "Policy1"
assert result[1].resource == defender_client.malware_policies[1].dict()
def test_no_malware_policies(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
@@ -129,33 +447,12 @@ class Test_defender_malware_policy_notifications_internal_users_malware_enabled:
from prowler.providers.m365.services.defender.defender_malware_policy_notifications_internal_users_malware_enabled.defender_malware_policy_notifications_internal_users_malware_enabled import (
defender_malware_policy_notifications_internal_users_malware_enabled,
)
from prowler.providers.m365.services.defender.defender_service import (
MalwarePolicy,
)
defender_client.malware_policies = [
MalwarePolicy(
enable_file_filter=True,
identity="Default",
enable_internal_sender_admin_notifications=True,
internal_sender_admin_address="security@example.com",
file_types=[],
is_default=True,
)
]
defender_client.malware_policies = []
defender_client.malware_rules = {}
check = (
defender_malware_policy_notifications_internal_users_malware_enabled()
)
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Notifications for internal users sending malware are enabled."
)
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"
assert len(result) == 0
@@ -9,6 +9,7 @@ from prowler.providers.m365.services.defender.defender_service import (
Defender,
DefenderInboundSpamPolicy,
DkimConfig,
InboundSpamRule,
MalwarePolicy,
MalwareRule,
OutboundSpamPolicy,
@@ -41,14 +42,27 @@ def mock_defender_get_malware_filter_policy(_):
def mock_defender_get_malware_filter_rule(_):
return {
"Policy1": MalwareRule(state="Enabled"),
"Policy2": MalwareRule(state="Disabled"),
"Policy1": MalwareRule(
state="Enabled",
priority=1,
users=["test@example.com"],
groups=["example_group"],
domains=["example.com"],
),
"Policy2": MalwareRule(
state="Disabled",
priority=2,
users=["test@example.com"],
groups=["example_group"],
domains=["example.com"],
),
}
def mock_defender_get_antiphising_policy(_):
def mock_defender_get_antiphishing_policy(_):
return {
"Policy1": AntiphishingPolicy(
name="Policy1",
spoof_intelligence=True,
spoof_intelligence_action="Quarantine",
dmarc_reject_action="Reject",
@@ -60,6 +74,7 @@ def mock_defender_get_antiphising_policy(_):
default=False,
),
"Policy2": AntiphishingPolicy(
name="Policy2",
spoof_intelligence=False,
spoof_intelligence_action="None",
dmarc_reject_action="None",
@@ -73,13 +88,21 @@ def mock_defender_get_antiphising_policy(_):
}
def mock_defender_get_antiphising_rules(_):
def mock_defender_get_antiphishing_rules(_):
return {
"Policy1": AntiphishingRule(
state="Enabled",
priority=1,
users=["test@example.com"],
groups=["example_group"],
domains=["example.com"],
),
"Policy2": AntiphishingRule(
state="Disabled",
priority=2,
users=["test@example.com"],
groups=["example_group"],
domains=["example.com"],
),
}
@@ -89,14 +112,35 @@ def mock_defender_get_inbound_spam_policy(_):
DefenderInboundSpamPolicy(
identity="Policy1",
allowed_sender_domains=[],
default=False,
),
DefenderInboundSpamPolicy(
identity="Policy2",
allowed_sender_domains=["example.com"],
default=True,
),
]
def mock_defender_get_inbound_spam_rule(_):
return {
"Policy1": InboundSpamRule(
state="Enabled",
priority=1,
users=["test@example.com"],
groups=["example_group"],
domains=["example.com"],
),
"Policy2": InboundSpamRule(
state="Disabled",
priority=2,
users=["test@example.com"],
groups=["example_group"],
domains=["example.com"],
),
}
def mock_defender_get_connection_filter_policy(_):
return ConnectionFilterPolicy(
ip_allow_list=[],
@@ -131,6 +175,7 @@ def mock_defender_get_report_submission_policy(_):
def mock_defender_get_outbound_spam_filter_policy(_):
return {
"Policy1": OutboundSpamPolicy(
name="Policy1",
notify_sender_blocked=True,
notify_limit_exceeded=True,
notify_limit_exceeded_addresses=["security@example.com"],
@@ -139,6 +184,7 @@ def mock_defender_get_outbound_spam_filter_policy(_):
default=False,
),
"Policy2": OutboundSpamPolicy(
name="Policy2",
notify_sender_blocked=False,
notify_limit_exceeded=False,
notify_limit_exceeded_addresses=[],
@@ -153,9 +199,17 @@ def mock_defender_get_outbound_spam_filter_rule(_):
return {
"Policy1": OutboundSpamRule(
state="Enabled",
priority=1,
users=["test@example.com"],
groups=["example_group"],
domains=["example.com"],
),
"Policy2": OutboundSpamRule(
state="Disabled",
priority=2,
users=["test@example.com"],
groups=["example_group"],
domains=["example.com"],
),
}
@@ -230,14 +284,22 @@ class Test_Defender_Service:
)
malware_rules = defender_client.malware_rules
assert malware_rules["Policy1"].state == "Enabled"
assert malware_rules["Policy1"].priority == 1
assert malware_rules["Policy1"].users == ["test@example.com"]
assert malware_rules["Policy1"].groups == ["example_group"]
assert malware_rules["Policy1"].domains == ["example.com"]
assert malware_rules["Policy2"].state == "Disabled"
assert malware_rules["Policy2"].priority == 2
assert malware_rules["Policy2"].users == ["test@example.com"]
assert malware_rules["Policy2"].groups == ["example_group"]
assert malware_rules["Policy2"].domains == ["example.com"]
defender_client.powershell.close()
@patch(
"prowler.providers.m365.services.defender.defender_service.Defender._get_antiphising_policy",
new=mock_defender_get_antiphising_policy,
"prowler.providers.m365.services.defender.defender_service.Defender._get_antiphishing_policy",
new=mock_defender_get_antiphishing_policy,
)
def test_get_antiphising_policy(self):
def test_get_antiphishing_policy(self):
with (
mock.patch(
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online"
@@ -249,6 +311,7 @@ class Test_Defender_Service:
)
)
antiphishing_policies = defender_client.antiphishing_policies
assert antiphishing_policies["Policy1"].name == "Policy1"
assert antiphishing_policies["Policy1"].spoof_intelligence is True
assert (
antiphishing_policies["Policy1"].spoof_intelligence_action
@@ -265,6 +328,7 @@ class Test_Defender_Service:
assert antiphishing_policies["Policy1"].show_tag is True
assert antiphishing_policies["Policy1"].honor_dmarc_policy is True
assert antiphishing_policies["Policy1"].default is False
assert antiphishing_policies["Policy2"].name == "Policy2"
assert antiphishing_policies["Policy2"].spoof_intelligence is False
assert antiphishing_policies["Policy2"].spoof_intelligence_action == "None"
assert antiphishing_policies["Policy2"].dmarc_reject_action == "None"
@@ -279,10 +343,10 @@ class Test_Defender_Service:
defender_client.powershell.close()
@patch(
"prowler.providers.m365.services.defender.defender_service.Defender._get_antiphising_rules",
new=mock_defender_get_antiphising_rules,
"prowler.providers.m365.services.defender.defender_service.Defender._get_antiphishing_rules",
new=mock_defender_get_antiphishing_rules,
)
def test_get_antiphising_rules(self):
def test_get_antiphishing_rules(self):
with (
mock.patch(
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online"
@@ -293,9 +357,17 @@ class Test_Defender_Service:
identity=M365IdentityInfo(tenant_domain=DOMAIN)
)
)
antiphishing_rules = defender_client.antiphising_rules
antiphishing_rules = defender_client.antiphishing_rules
assert antiphishing_rules["Policy1"].state == "Enabled"
assert antiphishing_rules["Policy1"].priority == 1
assert antiphishing_rules["Policy1"].users == ["test@example.com"]
assert antiphishing_rules["Policy1"].groups == ["example_group"]
assert antiphishing_rules["Policy1"].domains == ["example.com"]
assert antiphishing_rules["Policy2"].state == "Disabled"
assert antiphishing_rules["Policy2"].priority == 2
assert antiphishing_rules["Policy2"].users == ["test@example.com"]
assert antiphishing_rules["Policy2"].groups == ["example_group"]
assert antiphishing_rules["Policy2"].domains == ["example.com"]
defender_client.powershell.close()
@patch(
@@ -357,6 +429,7 @@ class Test_Defender_Service:
)
)
outbound_spam_policies = defender_client.outbound_spam_policies
assert outbound_spam_policies["Policy1"].name == "Policy1"
assert outbound_spam_policies["Policy1"].notify_sender_blocked is True
assert outbound_spam_policies["Policy1"].notify_limit_exceeded is True
assert outbound_spam_policies[
@@ -367,6 +440,7 @@ class Test_Defender_Service:
].notify_sender_blocked_addresses == ["security@example.com"]
assert outbound_spam_policies["Policy1"].auto_forwarding_mode is False
assert outbound_spam_policies["Policy1"].default is False
assert outbound_spam_policies["Policy2"].name == "Policy2"
assert outbound_spam_policies["Policy2"].notify_sender_blocked is False
assert outbound_spam_policies["Policy2"].notify_limit_exceeded is False
assert (
@@ -377,6 +451,7 @@ class Test_Defender_Service:
)
assert outbound_spam_policies["Policy2"].auto_forwarding_mode is True
assert outbound_spam_policies["Policy2"].default is True
defender_client.powershell.close()
@patch(
"prowler.providers.m365.services.defender.defender_service.Defender._get_outbound_spam_filter_rule",
@@ -395,7 +470,16 @@ class Test_Defender_Service:
)
outbound_spam_rules = defender_client.outbound_spam_rules
assert outbound_spam_rules["Policy1"].state == "Enabled"
assert outbound_spam_rules["Policy1"].priority == 1
assert outbound_spam_rules["Policy1"].users == ["test@example.com"]
assert outbound_spam_rules["Policy1"].groups == ["example_group"]
assert outbound_spam_rules["Policy1"].domains == ["example.com"]
assert outbound_spam_rules["Policy2"].state == "Disabled"
assert outbound_spam_rules["Policy2"].priority == 2
assert outbound_spam_rules["Policy2"].users == ["test@example.com"]
assert outbound_spam_rules["Policy2"].groups == ["example_group"]
assert outbound_spam_rules["Policy2"].domains == ["example.com"]
defender_client.powershell.close()
@patch(
"prowler.providers.m365.services.defender.defender_service.Defender._get_inbound_spam_filter_policy",
@@ -417,6 +501,34 @@ class Test_Defender_Service:
assert inbound_spam_policies[1].allowed_sender_domains == ["example.com"]
defender_client.powershell.close()
@patch(
"prowler.providers.m365.services.defender.defender_service.Defender._get_inbound_spam_filter_rule",
new=mock_defender_get_inbound_spam_rule,
)
def test__get_inbound_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)
)
)
inbound_spam_rules = defender_client.inbound_spam_rules
assert inbound_spam_rules["Policy1"].state == "Enabled"
assert inbound_spam_rules["Policy1"].priority == 1
assert inbound_spam_rules["Policy1"].users == ["test@example.com"]
assert inbound_spam_rules["Policy1"].groups == ["example_group"]
assert inbound_spam_rules["Policy1"].domains == ["example.com"]
assert inbound_spam_rules["Policy2"].state == "Disabled"
assert inbound_spam_rules["Policy2"].priority == 2
assert inbound_spam_rules["Policy2"].users == ["test@example.com"]
assert inbound_spam_rules["Policy2"].groups == ["example_group"]
assert inbound_spam_rules["Policy2"].domains == ["example.com"]
defender_client.powershell.close()
@patch(
"prowler.providers.m365.services.defender.defender_service.Defender._get_report_submission_policy",
new=mock_defender_get_report_submission_policy,