feat(m365): add CIS M365 v7.0.0 entra password protection and default user permission checks - #12153
Conversation
📝 WalkthroughWalkthroughChangesThe Entra service now loads directory settings and B2B collaboration policy data. Seven CIS v7 checks evaluate password protection, group creation, and guest invitation restrictions. Each check includes metadata and test coverage. Entra CIS v7 checks
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Entra
participant MicrosoftGraph
participant EntraChecks
Entra->>MicrosoftGraph: Request B2B policies and directory settings
MicrosoftGraph-->>Entra: Return policy and settings data
Entra->>EntraChecks: Expose parsed service data
EntraChecks->>EntraChecks: Evaluate CIS v7 controls
EntraChecks-->>Entra: Return PASS, FAIL, or no finding
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
✅ No Conflicts No conflict markers, and the branch merges cleanly into its base. |
Compliance Mapping ReviewThis PR adds new checks. Please verify that they have been mapped to the relevant compliance framework requirements. New checks already mapped in this PR
Use the |
|
✅ All required changelog fragments are present. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #12153 +/- ##
==========================================
- Coverage 94.55% 93.81% -0.75%
==========================================
Files 271 431 +160
Lines 42211 49011 +6800
==========================================
+ Hits 39914 45980 +6066
- Misses 2297 3031 +734
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
🔒 Container Security ScanImage: ✅ No Vulnerabilities DetectedThe container image passed all security checks. No known CVEs were found.📋 Resources:
|
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@prowler/changelog.d/m365-cis7-entra-directory-settings.added.md`:
- Line 1: Update the changelog entry text to explicitly include guest invitation
domain restrictions alongside the existing password protection and default-user
permission controls, accurately covering all seven M365 Entra checks.
In
`@prowler/providers/m365/services/entra/entra_password_protection_lockout_duration_configured/entra_password_protection_lockout_duration_configured.py`:
- Line 24: Add a Google-style docstring directly inside the public execute
method, documenting its purpose, return value, and any relevant behavior while
preserving the existing List[CheckReportM365] return contract.
In
`@prowler/providers/m365/services/entra/entra_password_protection_on_premises_enforced/entra_password_protection_on_premises_enforced.py`:
- Around line 33-65: Update the control implementation around the Entra
synchronization data to determine whether the tenant has on-premises directory
synchronization before creating the CheckReportM365 report. Return no findings
for cloud-only tenants, while preserving the existing password protection status
evaluation for hybrid tenants. Add a test covering a cloud-only tenant that
expects an empty findings result.
In
`@prowler/providers/m365/services/entra/entra_policy_default_user_cannot_create_security_groups/entra_policy_default_user_cannot_create_security_groups.py`:
- Around line 41-47: Update the condition in the Entra policy check to pass only
when allowed_to_create_security_groups is explicitly False, treating None or any
other non-False value as FAIL. Add a test covering
allowed_to_create_security_groups=None and assert that it reports FAIL.
In
`@prowler/providers/m365/services/entra/entra_policy_guest_invitations_restricted_to_allowed_domains/entra_policy_guest_invitations_restricted_to_allowed_domains.metadata.json`:
- Line 12: Update the Entra guest-invitations check to treat restriction enabled
with an empty AllowedDomains list as compliant, matching the loader’s
most-restrictive state. Revise the metadata description to state that both an
explicit allow-list and block-all mode are compliant. In
tests/providers/m365/services/entra/entra_policy_guest_invitations_restricted_to_allowed_domains/entra_policy_guest_invitations_restricted_to_allowed_domains_test.py
lines 49-56, expect PASS for the restricted empty-list case.
In
`@prowler/providers/m365/services/entra/entra_policy_guest_invitations_restricted_to_allowed_domains/entra_policy_guest_invitations_restricted_to_allowed_domains.py`:
- Around line 10-14: Update the check and corresponding docstrings in the
invitations-restriction implementation so it passes whenever
invitations_restricted_to_allowed_domains is true, including when AllowedDomains
is an empty list. Preserve failure behavior when the policy is not restricted,
and add a test covering the active empty allow-list configuration.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 7f912eaa-7b33-4b97-831b-dbc080a06f07
⛔ Files ignored due to path filters (1)
prowler/compliance/m365/cis_7.0_m365.jsonis excluded by!prowler/compliance/**/*.json
📒 Files selected for processing (31)
prowler/changelog.d/m365-cis7-entra-device-registration.added.mdprowler/changelog.d/m365-cis7-entra-directory-settings.added.mdprowler/providers/m365/services/entra/entra_password_protection_custom_banned_list_enforced/__init__.pyprowler/providers/m365/services/entra/entra_password_protection_custom_banned_list_enforced/entra_password_protection_custom_banned_list_enforced.metadata.jsonprowler/providers/m365/services/entra/entra_password_protection_custom_banned_list_enforced/entra_password_protection_custom_banned_list_enforced.pyprowler/providers/m365/services/entra/entra_password_protection_lockout_duration_configured/__init__.pyprowler/providers/m365/services/entra/entra_password_protection_lockout_duration_configured/entra_password_protection_lockout_duration_configured.metadata.jsonprowler/providers/m365/services/entra/entra_password_protection_lockout_duration_configured/entra_password_protection_lockout_duration_configured.pyprowler/providers/m365/services/entra/entra_password_protection_lockout_threshold_limited/__init__.pyprowler/providers/m365/services/entra/entra_password_protection_lockout_threshold_limited/entra_password_protection_lockout_threshold_limited.metadata.jsonprowler/providers/m365/services/entra/entra_password_protection_lockout_threshold_limited/entra_password_protection_lockout_threshold_limited.pyprowler/providers/m365/services/entra/entra_password_protection_on_premises_enforced/__init__.pyprowler/providers/m365/services/entra/entra_password_protection_on_premises_enforced/entra_password_protection_on_premises_enforced.metadata.jsonprowler/providers/m365/services/entra/entra_password_protection_on_premises_enforced/entra_password_protection_on_premises_enforced.pyprowler/providers/m365/services/entra/entra_policy_default_user_cannot_create_m365_groups/__init__.pyprowler/providers/m365/services/entra/entra_policy_default_user_cannot_create_m365_groups/entra_policy_default_user_cannot_create_m365_groups.metadata.jsonprowler/providers/m365/services/entra/entra_policy_default_user_cannot_create_m365_groups/entra_policy_default_user_cannot_create_m365_groups.pyprowler/providers/m365/services/entra/entra_policy_default_user_cannot_create_security_groups/__init__.pyprowler/providers/m365/services/entra/entra_policy_default_user_cannot_create_security_groups/entra_policy_default_user_cannot_create_security_groups.metadata.jsonprowler/providers/m365/services/entra/entra_policy_default_user_cannot_create_security_groups/entra_policy_default_user_cannot_create_security_groups.pyprowler/providers/m365/services/entra/entra_policy_guest_invitations_restricted_to_allowed_domains/__init__.pyprowler/providers/m365/services/entra/entra_policy_guest_invitations_restricted_to_allowed_domains/entra_policy_guest_invitations_restricted_to_allowed_domains.metadata.jsonprowler/providers/m365/services/entra/entra_policy_guest_invitations_restricted_to_allowed_domains/entra_policy_guest_invitations_restricted_to_allowed_domains.pyprowler/providers/m365/services/entra/entra_service.pytests/providers/m365/services/entra/entra_password_protection_custom_banned_list_enforced/entra_password_protection_custom_banned_list_enforced_test.pytests/providers/m365/services/entra/entra_password_protection_lockout_duration_configured/entra_password_protection_lockout_duration_configured_test.pytests/providers/m365/services/entra/entra_password_protection_lockout_threshold_limited/entra_password_protection_lockout_threshold_limited_test.pytests/providers/m365/services/entra/entra_password_protection_on_premises_enforced/entra_password_protection_on_premises_enforced_test.pytests/providers/m365/services/entra/entra_policy_default_user_cannot_create_m365_groups/entra_policy_default_user_cannot_create_m365_groups_test.pytests/providers/m365/services/entra/entra_policy_default_user_cannot_create_security_groups/entra_policy_default_user_cannot_create_security_groups_test.pytests/providers/m365/services/entra/entra_policy_guest_invitations_restricted_to_allowed_domains/entra_policy_guest_invitations_restricted_to_allowed_domains_test.py
🔎 Container Security Scan (Grype)Image: ✅ Nothing BlockingNo findings at critical or high severity. Not blocking at this cutoff — medium: 20, low: 4, negligible: 1. 56 finding(s) excluded by 📋 Resources:
|
- Handle cloud-only and unknown policy states safely - Accept block-all guest invitation restrictions - Expand regression coverage and documentation
- Prevent organization state from leaking between checks - Configure the plural organizations attribute used by production
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/providers/m365/services/entra/entra_password_protection_on_premises_enforced/entra_password_protection_on_premises_enforced_test.py (1)
65-81: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAdd a hybrid-tenant case for disabled enforcement.
test_cloud_only_tenant_has_no_findinguses"False", but the production check exits before evaluating settings for cloud-only tenants. The suite does not verify that a hybrid tenant withEnableBannedPasswordCheckOnPremises == "False"returnsFAIL. Add this case.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/providers/m365/services/entra/entra_password_protection_on_premises_enforced/entra_password_protection_on_premises_enforced_test.py` around lines 65 - 81, Add a test alongside test_audit_mode that runs the check for a hybrid organization with EnableBannedPasswordCheckOnPremises set to "False" and asserts the result status is "FAIL". Reuse the existing _run setup and Organization configuration, changing only the enforcement setting needed to cover disabled enforcement.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In
`@tests/providers/m365/services/entra/entra_password_protection_on_premises_enforced/entra_password_protection_on_premises_enforced_test.py`:
- Around line 65-81: Add a test alongside test_audit_mode that runs the check
for a hybrid organization with EnableBannedPasswordCheckOnPremises set to
"False" and asserts the result status is "FAIL". Reuse the existing _run setup
and Organization configuration, changing only the enforcement setting needed to
cover disabled enforcement.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 331b6f93-5140-4ea2-88cb-b0308bb02400
📒 Files selected for processing (2)
tests/providers/m365/services/entra/entra_password_hash_sync_enabled/entra_password_hash_sync_enabled_test.pytests/providers/m365/services/entra/entra_password_protection_on_premises_enforced/entra_password_protection_on_premises_enforced_test.py
…o pr-12153-conflicts
…o pr-12153-conflicts
Context
Part of the split of #12102, which added 37 M365 checks in a single pull request. It was broken up per service so each piece can be reviewed on its own; this one covers entra.
The CIS Microsoft 365 Foundations Benchmark v7.0.0 framework (
cis_7.0_m365) defines 160 controls, and 64 of its "Automated" controls had no Prowler check.This is part 2 of 4 of the
entrastack (28 checks), which is split by the service getter each group of checks depends on. It is stacked on top of the previous branch, so the diff shown here is only its own.Description
Adds 7 M365
entrachecks mapped tocis_7.0_m365.json:entra_policy_default_user_cannot_create_security_groupsentra_policy_default_user_cannot_create_m365_groupsentra_policy_guest_invitations_restricted_to_allowed_domainsentra_password_protection_custom_banned_list_enforcedentra_password_protection_on_premises_enforcedentra_password_protection_lockout_threshold_limitedentra_password_protection_lockout_duration_configuredData collection. New getters for
groupSettings(directory settings) and the legacy B2B management policy. The security-groups check (5.1.3.1) reads the already-collectedauthorizationPolicy.Caveat. Guest-invitation domains (5.1.6.1) reads the legacy B2B management policy, which should be confirmed against a live tenant.
Each check ships with its implementation,
metadata.jsonand unit tests (PASS / FAIL / edge cases).Steps to review
uv run --frozen python prowler-cli.py m365 --list-checks | grep -E "entra_policy_default_user_cannot_create_security_groups|entra_policy_default_user_cannot_create_m365_groups|entra_policy_guest_invitations_restricted_to_allowed_domains|entra_password_protection_custom_banned_list_enforced|entra_password_protection_on_premises_enforced|entra_password_protection_lockout_threshold_limited|entra_password_protection_lockout_duration_configured"uv run --frozen python prowler-cli.py m365 --verbose --env-auth -c entra_policy_default_user_cannot_create_security_groups entra_policy_default_user_cannot_create_m365_groups entra_policy_guest_invitations_restricted_to_allowed_domains entra_password_protection_custom_banned_list_enforced entra_password_protection_on_premises_enforced entra_password_protection_lockout_threshold_limited entra_password_protection_lockout_duration_configureduv run pytest tests/providers/m365/services/entra/prowler/compliance/m365/cis_7.0_m365.json.entra_service.py.Checklist
Community Checklist
License
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
SDK/CLI
Directory.Read.All,Policy.Read.All.Summary by CodeRabbit