Issue search
Which component is affected?
Prowler CLI/SDK
Cloud Provider (if applicable)
Microsoft 365
Steps to Reproduce
- Use a Microsoft 365 tenant with the built-in preset security policies enabled (Standard or Strict). This is the configuration Microsoft recommends, so most tenants have it.
- Run an M365 scan, for example
prowler m365 -c defender_malware_policy_notifications_internal_users_malware_enabled.
- The check crashes with a
KeyError on the name of the preset policy.
Expected behavior
A policy with no matching entry in the corresponding rules dictionary should be handled gracefully, either skipped or reported with a status that reflects that Microsoft manages it through a preset security policy rule. The check should still evaluate the remaining policies instead of aborting.
Actual Result with Screenshots or Logs
Something went wrong in defender_malware_policy_notifications_internal_users_malware_enabled, please use --log-level ERROR
[File: check.py:744] [Module: check] ERROR: defender_malware_policy_notifications_internal_users_malware_enabled -- KeyError[83]: 'Standard Preset Security Policy1716879803305'
The check aborts and returns no findings. The rest of the scan completes.
How did you install Prowler?
From pip package (pip install prowler)
Environment Resource
Workstation
OS used
Not reported
Prowler version
5.34.0, and still present on current master (5.40.0)
Python version
Not reported
Pip version
Not reported
Context
defender_service.py builds malware_rules from Get-MalwareFilterRule only, keyed by the MalwareFilterPolicy name. Preset security policies are returned by Get-MalwareFilterPolicy with IsDefault=False, but their rules come from Get-ATPProtectionPolicyRule and Get-EOPProtectionPolicyRule, so they never appear as keys in malware_rules.
The check treats every non-default policy as a custom policy and indexes the dictionary directly at line 83 of defender_malware_policy_notifications_internal_users_malware_enabled.py, which is the line the traceback points at. The helper in that same file already guards for the missing key at line 158 (policy.identity in defender_client.malware_rules), so the case was anticipated, but the four reporting branches index it without checking.
The same pattern is copied across seven Defender checks, so a tenant with preset security policies loses all of them, not just the one in the report:
defender_malware_policy_notifications_internal_users_malware_enabled (malware_rules, line 83)
defender_malware_policy_common_attachments_filter_enabled (malware_rules, line 83)
defender_malware_policy_comprehensive_attachments_filter_applied (malware_rules, line 153)
defender_antiphishing_policy_configured (antiphishing_rules, line 80)
defender_antispam_outbound_policy_configured (outbound_spam_rules, line 80)
defender_antispam_outbound_policy_forwarding_disabled (outbound_spam_rules, line 83)
defender_antispam_policy_inbound_no_allowed_domains (inbound_spam_rules, line 83)
The scan exits normally and the only trace is the log line, so the loss is silent. Seven email security controls return no findings on tenants running Microsoft's recommended configuration.
Proposed direction:
- Resolve the rule once per policy with
.get() and decide explicitly, rather than indexing. Skipping a policy with no rule avoids a false FAIL on policies that Microsoft configures by design.
- Reporting those policies with an explicit status would be more transparent than skipping them, but it needs its own
status_extended wording, so it is worth deciding separately.
- Apply the same fix to the seven checks and add a regression test covering a policy list that includes a preset policy with no matching rule.
- Longer term, the service already collects what is needed to evaluate preset policies properly.
eop_protection_policy_rules and atp_protection_policy_rules exist in defender_service.py and are already used by defender_strict_preset_security_policy_enabled. Wiring the policy checks to them would let preset policies be assessed instead of ignored.
Origin of this issue
Reported by a user in the Prowler Community Slack: https://prowler-workspace.slack.com/archives/C045DAM2NE9/p1788682771936239
The reporter identified the cause correctly. The scope across the other six checks was found while confirming the report.
Issue search
Which component is affected?
Prowler CLI/SDK
Cloud Provider (if applicable)
Microsoft 365
Steps to Reproduce
prowler m365 -c defender_malware_policy_notifications_internal_users_malware_enabled.KeyErroron the name of the preset policy.Expected behavior
A policy with no matching entry in the corresponding rules dictionary should be handled gracefully, either skipped or reported with a status that reflects that Microsoft manages it through a preset security policy rule. The check should still evaluate the remaining policies instead of aborting.
Actual Result with Screenshots or Logs
The check aborts and returns no findings. The rest of the scan completes.
How did you install Prowler?
From pip package (pip install prowler)
Environment Resource
Workstation
OS used
Not reported
Prowler version
5.34.0, and still present on current
master(5.40.0)Python version
Not reported
Pip version
Not reported
Context
defender_service.pybuildsmalware_rulesfromGet-MalwareFilterRuleonly, keyed by theMalwareFilterPolicyname. Preset security policies are returned byGet-MalwareFilterPolicywithIsDefault=False, but their rules come fromGet-ATPProtectionPolicyRuleandGet-EOPProtectionPolicyRule, so they never appear as keys inmalware_rules.The check treats every non-default policy as a custom policy and indexes the dictionary directly at line 83 of
defender_malware_policy_notifications_internal_users_malware_enabled.py, which is the line the traceback points at. The helper in that same file already guards for the missing key at line 158 (policy.identity in defender_client.malware_rules), so the case was anticipated, but the four reporting branches index it without checking.The same pattern is copied across seven Defender checks, so a tenant with preset security policies loses all of them, not just the one in the report:
defender_malware_policy_notifications_internal_users_malware_enabled(malware_rules, line 83)defender_malware_policy_common_attachments_filter_enabled(malware_rules, line 83)defender_malware_policy_comprehensive_attachments_filter_applied(malware_rules, line 153)defender_antiphishing_policy_configured(antiphishing_rules, line 80)defender_antispam_outbound_policy_configured(outbound_spam_rules, line 80)defender_antispam_outbound_policy_forwarding_disabled(outbound_spam_rules, line 83)defender_antispam_policy_inbound_no_allowed_domains(inbound_spam_rules, line 83)The scan exits normally and the only trace is the log line, so the loss is silent. Seven email security controls return no findings on tenants running Microsoft's recommended configuration.
Proposed direction:
.get()and decide explicitly, rather than indexing. Skipping a policy with no rule avoids a false FAIL on policies that Microsoft configures by design.status_extendedwording, so it is worth deciding separately.eop_protection_policy_rulesandatp_protection_policy_rulesexist indefender_service.pyand are already used bydefender_strict_preset_security_policy_enabled. Wiring the policy checks to them would let preset policies be assessed instead of ignored.Origin of this issue
Reported by a user in the Prowler Community Slack: https://prowler-workspace.slack.com/archives/C045DAM2NE9/p1788682771936239
The reporter identified the cause correctly. The scope across the other six checks was found while confirming the report.