Add E8-MAC-2.1: Win32 API call blocking ASR rule check#215
Add E8-MAC-2.1: Win32 API call blocking ASR rule check#215SidharthSabu wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 12e0be420f
ℹ️ 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".
|
@SidharthSabu I've sent you a teams message along with Emma so that a major path conflict can be resolved; that aside, I've also added a few changes here to address. |
|
three different style choices for "essential eight" are in play:
That works mechanically thanks to PR 210's normalizer, but it's a lot of stylistic surface. Two options:
|
| from collectors.graph_client import GraphClient | ||
|
|
||
|
|
||
| def _normalize_state(raw: str) -> str: |
There was a problem hiding this comment.
_normalize_state uses substring matching - this is a little brittle
def _normalize_state(raw: str) -> str:
raw = (raw or "").lower()
if "block" in raw:
return "block"
if "audit" in raw:
return "audit"
if "warn" in raw:
return "warn"
if "disable" in raw or "userdefined" in raw or "off" in raw:
return "disabled"
return raw or "not_configured"
Substring matching on Microsoft enum values is fragile — Microsoft has historically used values like enable, enabled, enableBlockMode, userDefined, notConfigured, plus regional variations. A future Intune update that adds e.g. auditModeWithExceptions would match the "audit" branch but lose information.
Recommend an explicit enum map instead:
INTUNE_ASR_STATE_MAP = {
"block": "block",
"audit": "audit",
"warn": "warn",
"disable": "disabled",
"userdefined": "disabled",
"notconfigured": "not_configured",
"off": "disabled",
}
def _normalize_state(raw: str) -> str:
return INTUNE_ASR_STATE_MAP.get((raw or "").lower(), "unknown")
Then "unknown" clearly surfaces as a non-Block state and the Rego fails correctly, without silently letting through a string that looks like noise. Pin the map to documented Microsoft Graph enum values and cite the source in a comment.
| if not config_id: | ||
| continue | ||
| full_config = await client.get( | ||
| f"/deviceManagement/deviceConfigurations/{config_id}", beta=True, |
There was a problem hiding this comment.
Not a Python style issue (PEP 8 allows trailing commas), just unusual to see one after a kwarg. Black/Ruff defaults often add it when arguments span multiple lines, so this may just be auto-formatting.
…-normalization Normalize framework name and control_id when building OPA package path. Lowercases the framework name and replaces hyphens with underscores so frameworks like essential-eight and control IDs like E8-MAC-2.1 resolve to the correct Rego package. Backwards-compatible on existing CIS inputs. Unblocks PR #215.
Defines all 12 controls of the macro settings benchmark. Only E8-MAC-2.1 is wired up in this PR (automation_status: ready); the other ML1/ML3 controls are scaffolded with not_started or manual so the full benchmark scope is documented in metadata. The worker skips non-ready controls during scans, and the UI filters skipped controls out of the per-scan results list. Future PRs implement them by flipping the flag. Author: Vrinda
Evaluates the win32_api_rule_state field returned by the ASR rules collector. Compliant only when the state is "block" — audit/warn/disabled all fail per ASD Essential Eight ML2, which explicitly requires Block mode (Audit is not accepted). Surfaces the rule state, source endpoint, and policy name in details for audit evidence. Author: Armeen
…ot_started controls test_control_schema_consistency requires that controls with automation_status="not_started" have null data_collector_id and policy_file. The original metadata referenced an unimplemented collector (entra.devices.configuration_policies) and rego files that don't exist yet. Setting both to null until the future PRs that implement those controls populate them. Author: Vrinda
Previously the collector returned on the first profile match, ignoring the rest. For tenants with multiple Endpoint Protection profiles configuring the rule differently (e.g.one in block, one in audit), this caused the result to depend on Graph API ordering, that is the same tenant could pass or fail across runs. The collector now inspects every profile, collects all states, and returns the weakest. ASD ML2 requires block on every profile; any non-block profile makes the tenant non-compliant. Addresses Codex review feedback (2nd Comment)
e722674 to
90059a1
Compare
Summary
Implements the first ASD Essential Eight Maturity Model control: E8-MAC-2.1 - Win32 API calls blocked from Office macros. Adds the data collector, the Rego policy, and the benchmark metadata so a Microsoft 365 connection can run an "ASD Essential Eight" scan and get a real PASS/FAIL for this control.
Type of Change
Affected Components
/backend-api/frontend/engine(collectors / policies)/security/infrastructure/.github/workflows/docsMotivation
First concrete ASD Essential Eight control delivered to AutoAudit. Unlocks the Macro Settings benchmark for the rest of the team to extend with their own controls. E8-MAC-2.1 is an ML2 control that requires Office macros to be blocked from making Win32 API calls — verifiable directly via Microsoft Graph against an Intune Endpoint Protection profile.
Dependency
Depends on #210 (Normalize framework name and control_id when building OPA package path). Without #210 merged first, the OPA lookup path will not match the Rego package and the control returns the generic OPA fallback instead of a real result.
Implementation
engine/collectors/entra/devices/asr_rules.py) — reads the Win32-API-from-macros ASR rule from a legacy Endpoint Protection device configuration. Uses the beta Graph endpoint for the per-config detail because the v1.0 schemawindows10EndpointProtectionConfigurationomits the ASR rule properties. ReturnsdefenderOfficeMacroCodeAllowWin32ImportsTypenormalised toblock/audit/disabled.engine/collectors/registry.py) — registers the new collector underentra.devices.asr_rules.engine/policies/.../e8_mac_2_1_win32_api_block.rego) — pass condition:win32_api_rule_state == "block". Anything else (includingaudit) is non-compliant per ASD Essential Eight ML2.engine/policies/.../metadata.json) — defines all 12 controls of the macro settings benchmark with E8-MAC-2.1 set toautomation_status: "ready"and the others markednot_startedormanualso they are documented in the benchmark scope but not dispatched.Testing Done
Unit tests pass locally
Tested manually — describe how:
Created a real Endpoint Protection profile in Intune (
E8-MAC-2.1-Legacy-ASR) with the Win32-imports-from-macros rule set to "Block" , ran an E8 scan → control returns PASS with message "Win32 API calls from Office macros are blocked in Block mode".Removed/changed the rule and re-ran → control returns FAIL with a meaningful message indicating the actual non-compliant state (
audit,disabled,not_configured).Confirmed CIS M365 scans behave identically — no regressions from the registry change.
No tests required — explain why:
Security Considerations
No new authentication surface area. The collector uses the existing Microsoft Graph client and the existing
DeviceManagementConfiguration.Read.Allapplication permission already required by other Intune collectors. No secrets, no new env vars, no new external endpoints.Breaking Changes
Rollback Plan
Contributors
asr_rules.py) and registry wiringmetadata.json)e8_mac_2_1_win32_api_block.rego)Checklist
Screenshots
Test Setup performed in Intune
Autoaudit Scan Result
Autoaudit Scan Result
Autoaudit Scan Result
