Skip to content

feat(m365): add CIS M365 v7.0.0 entra password protection and default user permission checks - #12153

Merged
HugoPBrito merged 8 commits into
masterfrom
feat/m365-cis7-entra-directory-settings
Aug 7, 2026
Merged

HugoPBrito merged 8 commits into
masterfrom
feat/m365-cis7-entra-directory-settings

Conversation

@pedrooot

@pedrooot pedrooot commented Jul 28, 2026

Copy link
Copy Markdown
Member

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 entra stack (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.

Merge after part 1 of the stack.

Description

Adds 7 M365 entra checks mapped to cis_7.0_m365.json:

CIS control Check
5.1.3.1 entra_policy_default_user_cannot_create_security_groups
5.1.3.4 entra_policy_default_user_cannot_create_m365_groups
5.1.6.1 entra_policy_guest_invitations_restricted_to_allowed_domains
5.2.3.2 entra_password_protection_custom_banned_list_enforced
5.2.3.3 entra_password_protection_on_premises_enforced
5.2.3.8 entra_password_protection_lockout_threshold_limited
5.2.3.9 entra_password_protection_lockout_duration_configured

Data collection. New getters for groupSettings (directory settings) and the legacy B2B management policy. The security-groups check (5.1.3.1) reads the already-collected authorizationPolicy.

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.json and unit tests (PASS / FAIL / edge cases).

Steps to review

  1. Discovery: 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"
  2. Live smoke test: 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_configured
  3. Tests: uv run pytest tests/providers/m365/services/entra/
  4. Compliance mapping: review the 7 requirement(s) touched in prowler/compliance/m365/cis_7.0_m365.json.
  5. Data collection: review the new getters and models in entra_service.py.
  6. Spot-check the check logic against the CIS audit procedures (fail-closed defaults; Conditional Access report-only treated as FAIL).

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

  • Are there new checks included in this PR? Yes (7 M365 checks)
    • Additional read scopes required: Directory.Read.All, Policy.Read.All.

Summary by CodeRabbit

  • New Features
    • Added Microsoft Entra security checks for password protection, smart lockout settings, on-premises enforcement, group creation policies, and guest invitation domain restrictions.
    • Reports now identify configurations meeting recommended security requirements, including CIS Microsoft 365 Foundations Benchmark v7 controls.
  • Documentation
    • Added guidance covering severity, risks, remediation steps, recommendations, and Microsoft documentation.
  • Tests
    • Added coverage for compliant, non-compliant, missing, and edge-case configurations.

@pedrooot
pedrooot requested a review from a team July 28, 2026 11:37
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The 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

Layer / File(s) Summary
Entra data collection
prowler/providers/m365/services/entra/entra_service.py, tests/providers/m365/services/entra/microsoft365_entra_service_test.py
The service retrieves B2B policies and /groupSettings data. It stores parsed settings and policy models.
Password protection checks
prowler/providers/m365/services/entra/entra_password_protection_*/, tests/providers/m365/services/entra/entra_password_protection_*/
Four checks evaluate banned password lists, lockout duration, lockout threshold, and on-premises enforcement.
Entra policy checks
prowler/providers/m365/services/entra/entra_policy_*/, tests/providers/m365/services/entra/entra_policy_*/, prowler/changelog.d/*entra*
Three checks evaluate Microsoft 365 group creation, security-group creation, and guest invitation domains.

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
Loading

Possibly related PRs

Suggested reviewers: danibarranqueroo

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the seven Entra CIS v7.0.0 checks for password protection and default user permissions, which are the main changes.
Description check ✅ Passed The description covers context, scope, implementation details, review steps, tests, checklist items, changelog, license, and required permissions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/m365-cis7-entra-directory-settings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pedrooot pedrooot mentioned this pull request Jul 28, 2026
24 tasks
@HugoPBrito HugoPBrito self-assigned this Aug 5, 2026
Base automatically changed from feat/m365-cis7-entra-device-registration to master August 5, 2026 11:51
@github-actions github-actions Bot added compliance Issues/PRs related with the Compliance Frameworks provider/m365 Issues/PRs related with the M365 provider metadata-review labels Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

No Conflicts

No conflict markers, and the branch merges cleanly into its base.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Compliance Mapping Review

This PR adds new checks. Please verify that they have been mapped to the relevant compliance framework requirements.

New checks already mapped in this PR

  • entra_password_protection_custom_banned_list_enforced (m365): cis_7.0_m365
  • entra_password_protection_lockout_duration_configured (m365): cis_7.0_m365
  • entra_password_protection_lockout_threshold_limited (m365): cis_7.0_m365
  • entra_password_protection_on_premises_enforced (m365): cis_7.0_m365
  • entra_policy_default_user_cannot_create_m365_groups (m365): cis_7.0_m365
  • entra_policy_default_user_cannot_create_security_groups (m365): cis_7.0_m365
  • entra_policy_guest_invitations_restricted_to_allowed_domains (m365): cis_7.0_m365

Use the no-compliance-check label to skip this check.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

✅ All required changelog fragments are present.

@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.58416% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.81%. Comparing base (cf558c5) to head (cbd01c5).
⚠️ Report is 1 commits behind head on master.

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     
Flag Coverage Δ
prowler-py3.10-m365 89.15% <91.58%> (?)
prowler-py3.11-m365 88.71% <90.59%> (?)
prowler-py3.12-m365 89.15% <91.58%> (?)
prowler-py3.13-m365 89.24% <91.58%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
prowler 89.24% <91.58%> (∅)
api 94.55% <ø> (-0.01%) ⬇️
mcp_server ∅ <ø> (∅)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🔒 Container Security Scan

Image: prowler:ce892cc
Last scan: 2026-08-07 08:00:38 UTC

✅ No Vulnerabilities Detected

The container image passed all security checks. No known CVEs were found.

📋 Resources:

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between d7816f1 and 06b5790.

⛔ Files ignored due to path filters (1)
  • prowler/compliance/m365/cis_7.0_m365.json is excluded by !prowler/compliance/**/*.json
📒 Files selected for processing (31)
  • prowler/changelog.d/m365-cis7-entra-device-registration.added.md
  • prowler/changelog.d/m365-cis7-entra-directory-settings.added.md
  • prowler/providers/m365/services/entra/entra_password_protection_custom_banned_list_enforced/__init__.py
  • prowler/providers/m365/services/entra/entra_password_protection_custom_banned_list_enforced/entra_password_protection_custom_banned_list_enforced.metadata.json
  • prowler/providers/m365/services/entra/entra_password_protection_custom_banned_list_enforced/entra_password_protection_custom_banned_list_enforced.py
  • prowler/providers/m365/services/entra/entra_password_protection_lockout_duration_configured/__init__.py
  • prowler/providers/m365/services/entra/entra_password_protection_lockout_duration_configured/entra_password_protection_lockout_duration_configured.metadata.json
  • prowler/providers/m365/services/entra/entra_password_protection_lockout_duration_configured/entra_password_protection_lockout_duration_configured.py
  • prowler/providers/m365/services/entra/entra_password_protection_lockout_threshold_limited/__init__.py
  • prowler/providers/m365/services/entra/entra_password_protection_lockout_threshold_limited/entra_password_protection_lockout_threshold_limited.metadata.json
  • prowler/providers/m365/services/entra/entra_password_protection_lockout_threshold_limited/entra_password_protection_lockout_threshold_limited.py
  • prowler/providers/m365/services/entra/entra_password_protection_on_premises_enforced/__init__.py
  • prowler/providers/m365/services/entra/entra_password_protection_on_premises_enforced/entra_password_protection_on_premises_enforced.metadata.json
  • prowler/providers/m365/services/entra/entra_password_protection_on_premises_enforced/entra_password_protection_on_premises_enforced.py
  • prowler/providers/m365/services/entra/entra_policy_default_user_cannot_create_m365_groups/__init__.py
  • prowler/providers/m365/services/entra/entra_policy_default_user_cannot_create_m365_groups/entra_policy_default_user_cannot_create_m365_groups.metadata.json
  • prowler/providers/m365/services/entra/entra_policy_default_user_cannot_create_m365_groups/entra_policy_default_user_cannot_create_m365_groups.py
  • prowler/providers/m365/services/entra/entra_policy_default_user_cannot_create_security_groups/__init__.py
  • prowler/providers/m365/services/entra/entra_policy_default_user_cannot_create_security_groups/entra_policy_default_user_cannot_create_security_groups.metadata.json
  • prowler/providers/m365/services/entra/entra_policy_default_user_cannot_create_security_groups/entra_policy_default_user_cannot_create_security_groups.py
  • prowler/providers/m365/services/entra/entra_policy_guest_invitations_restricted_to_allowed_domains/__init__.py
  • prowler/providers/m365/services/entra/entra_policy_guest_invitations_restricted_to_allowed_domains/entra_policy_guest_invitations_restricted_to_allowed_domains.metadata.json
  • prowler/providers/m365/services/entra/entra_policy_guest_invitations_restricted_to_allowed_domains/entra_policy_guest_invitations_restricted_to_allowed_domains.py
  • prowler/providers/m365/services/entra/entra_service.py
  • tests/providers/m365/services/entra/entra_password_protection_custom_banned_list_enforced/entra_password_protection_custom_banned_list_enforced_test.py
  • tests/providers/m365/services/entra/entra_password_protection_lockout_duration_configured/entra_password_protection_lockout_duration_configured_test.py
  • tests/providers/m365/services/entra/entra_password_protection_lockout_threshold_limited/entra_password_protection_lockout_threshold_limited_test.py
  • tests/providers/m365/services/entra/entra_password_protection_on_premises_enforced/entra_password_protection_on_premises_enforced_test.py
  • tests/providers/m365/services/entra/entra_policy_default_user_cannot_create_m365_groups/entra_policy_default_user_cannot_create_m365_groups_test.py
  • tests/providers/m365/services/entra/entra_policy_default_user_cannot_create_security_groups/entra_policy_default_user_cannot_create_security_groups_test.py
  • tests/providers/m365/services/entra/entra_policy_guest_invitations_restricted_to_allowed_domains/entra_policy_guest_invitations_restricted_to_allowed_domains_test.py

Comment thread prowler/changelog.d/m365-cis7-entra-directory-settings.added.md Outdated
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🔎 Container Security Scan (Grype)

Image: prowler:ce892cc
Last scan: 2026-08-07 08:03:02 UTC

✅ Nothing Blocking

No findings at critical or high severity.

Not blocking at this cutoff — medium: 20, low: 4, negligible: 1.

56 finding(s) excluded by .grype.yaml, each with a documented reason.


📋 Resources:

- Handle cloud-only and unknown policy states safely
- Accept block-all guest invitation restrictions
- Expand regression coverage and documentation
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 6, 2026
- Prevent organization state from leaking between checks
- Configure the plural organizations attribute used by production

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Add a hybrid-tenant case for disabled enforcement.

test_cloud_only_tenant_has_no_finding uses "False", but the production check exits before evaluating settings for cloud-only tenants. The suite does not verify that a hybrid tenant with EnableBannedPasswordCheckOnPremises == "False" returns FAIL. 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

📥 Commits

Reviewing files that changed from the base of the PR and between c6e2a31 and 22d305a.

📒 Files selected for processing (2)
  • tests/providers/m365/services/entra/entra_password_hash_sync_enabled/entra_password_hash_sync_enabled_test.py
  • tests/providers/m365/services/entra/entra_password_protection_on_premises_enforced/entra_password_protection_on_premises_enforced_test.py

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 6, 2026
@HugoPBrito
HugoPBrito merged commit f2a00f1 into master Aug 7, 2026
44 of 46 checks passed
@HugoPBrito
HugoPBrito deleted the feat/m365-cis7-entra-directory-settings branch August 7, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

compliance Issues/PRs related with the Compliance Frameworks metadata-review provider/m365 Issues/PRs related with the M365 provider

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants