Skip to content

Add Rego policies for CIS 5.1.4.2, 5.1.4.3, and 5.1.4.4 (Entra device join controls)#195

Open
williamywccc wants to merge 4 commits into
mainfrom
feature/add-cis-5.1.4.x-policy
Open

Add Rego policies for CIS 5.1.4.2, 5.1.4.3, and 5.1.4.4 (Entra device join controls)#195
williamywccc wants to merge 4 commits into
mainfrom
feature/add-cis-5.1.4.x-policy

Conversation

@williamywccc
Copy link
Copy Markdown
Collaborator

Summary

  • Adds Rego policies for CIS Microsoft 365 Foundations v6.0.0 controls 5.1.4.2, 5.1.4.3, and 5.1.4.4
  • Corrects a metadata error: these three controls were incorrectly mapped to entra.devices.device_management_settings; the actual data lives in entra.devices.device_registration_policy (/policies/deviceRegistrationPolicy beta endpoint)
  • Extends the existing collector to extract localAdministratorsConfiguration fields needed for 5.1.4.3 and 5.1.4.4

Controls

Control Check
5.1.4.2 userDeviceQuota > 0 — a value of 0 is treated as unlimited
5.1.4.3 azureADJoin.localAdministratorsConfiguration.enableGlobalAdmins == false
5.1.4.4 azureADJoin.localAdministratorsConfiguration.registeringUsers == notAllowed

localAdministratorsConfiguration is a beta-only field and may be absent on older tenants. Both policies return a descriptive non-compliant message rather than a silent failure in that case.

Test plan

  • Run OPA eval against sample inputs to verify compliant and non-compliant paths
  • Verify collector output shape against a live tenant with Policy.Read.DeviceConfiguration permission
  • Confirm localAdministratorsConfiguration field presence on the target tenant's beta endpoint

Made with Cursor

All three controls use the existing device_registration_policy collector
(/policies/deviceRegistrationPolicy beta endpoint). The metadata for
5.1.4.2-5.1.4.4 previously referenced entra.devices.device_management_settings
which does not expose the relevant fields; corrected to
entra.devices.device_registration_policy.

5.1.4.2 - checks userDeviceQuota > 0 (0 treated as unlimited)
5.1.4.3 - checks azureADJoin.localAdministratorsConfiguration.enableGlobalAdmins == false
5.1.4.4 - checks azureADJoin.localAdministratorsConfiguration.registeringUsers == notAllowed

The localAdministratorsConfiguration fields are beta-only and may be absent
on older tenants; the policy returns a clear non-compliant message in that case.

Made-with: Cursor
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1fb337a408

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@du-dhartley
Copy link
Copy Markdown
Collaborator

du-dhartley commented Apr 30, 2026

@williamywccc Good catch on the collector mapping correction, thanks for fixing that.
There are a couple of things to clean up though - the conditional bug in 5.1.4.4 (need to make the if block mutually exclusive) and the quota checking for 5.1.4.2.

Comment thread engine/policies/cis/microsoft-365-foundations/v6.0.0/metadata.json Outdated
@du-dhartley
Copy link
Copy Markdown
Collaborator

For 5.1.4.2:
I'd also expect to see at least a few screenshots, showing you setting the value to something that should fail and proving it does with a scan, and then setting the value to something that should pass (such as 5 or 10) and then proving that it is successful. It's also worth the edge case, setting the value to 20 and showing that it also passes.

williamywccc and others added 3 commits May 11, 2026 21:23
The API returns azureADJoin.localAdmins (not localAdministratorsConfiguration).
Also maps registeringUsers @odata.type to a readable string so the 5.1.4.4
Rego policy can evaluate it correctly.

Co-authored-by: Cursor <cursoragent@cursor.com>
Update 5.1.4.3 null message to reference correct localAdmins API key.
Fix 5.1.4.4 build_message conflict by adding mutual exclusion guards
to prevent both rules firing when val equals notallowed.

Co-authored-by: Cursor <cursoragent@cursor.com>
5.1.4.2: tighten quota check to require userDeviceQuota <= 20 per CIS
v6.0.0; update build_message to use else-chain and distinguish compliant
vs over-limit values; update metadata notes to reflect 20-or-less rule.

5.1.4.4: refactor build_message to a single else-chain rule to guarantee
mutual exclusivity and eliminate any eval_conflict_error.

Co-authored-by: Cursor <cursoragent@cursor.com>
@williamywccc
Copy link
Copy Markdown
Collaborator Author

螢幕截圖 2026-05-12 下午11 00 58 螢幕截圖 2026-05-12 下午11 01 08 螢幕截圖 2026-05-12 下午11 01 18

Hi David I have updated the Rego logic and the metadata file as we discussed. I refactored the build message rules into single else chains to ensure they are mutually exclusive and to avoid any evaluation conflicts.

I have also completed the three manual scans against the live test tenant to verify the quota logic for 5.1.4.2. Please see the attached screenshots for the results:

Scenario 1 (Fail): Quota set to 25 results in non-compliant status with the correct error message

Scenario 2 (Pass): Quota set to 10 results in compliant status

Scenario 3 (Edge Case): Quota set to exactly 20 also results in compliant status as required

Everything should be ready for your final review now. Thanks!

@williamywccc williamywccc requested a review from du-dhartley May 14, 2026 06:08
Copy link
Copy Markdown
Collaborator

@du-dhartley du-dhartley left a comment

Choose a reason for hiding this comment

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

The three screenshots don't show a manual scan, they show a manual policy execution against JSON that we can't verify. If you've used real tenant data then it's worth showing that here.
With that said, I tested this against a live tenant. Couple of things to note:

  1. Field name is localAdmins, not localAdministratorsConfiguration. Confirmed from the raw Graph beta response:
    azureADJoin keys: ['isAdminConfigurable', 'allowedToJoin', 'localAdmins']
    The collector code is correct. Please update to match in three places:
  • PR description (the Controls table)
  • docs/engine/policies/cis/microsoft-365-foundations/v6.0.0/controls.md (5.1.4.3 and 5.1.4.4 rows)
  • engine/policies/cis/microsoft-365-foundations/v6.0.0/metadata.json (5.1.4.3 and 5.1.4.4 notes)

The 5.1.4.3 fallback message ("… azureADJoin.localAdmins not returned by API") already uses the right name, so the user-facing string is fine.

  1. 5.1.4.2 threshold is <= 20, not just > 0. The Rego enforces quota > 0 AND quota <= 20, but the PR body table and controls.md only mention > 0. Please update those to match what the policy actually enforces.

When the doc/metadata fix is complete, this will be good to merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants