feat(compliance): add CIS 7.0 for the M365 provider (#11699)

This commit is contained in:
Pedro Martín
2026-06-26 12:45:12 +02:00
committed by GitHub
parent 086805df1d
commit 4c281aa464
4 changed files with 3626 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
import warnings
from dashboard.common_methods import get_section_containers_cis
warnings.filterwarnings("ignore")
def get_table(data):
aux = data[
[
"REQUIREMENTS_ID",
"REQUIREMENTS_DESCRIPTION",
"REQUIREMENTS_ATTRIBUTES_SECTION",
"CHECKID",
"STATUS",
"REGION",
"ACCOUNTID",
"RESOURCEID",
]
].copy()
return get_section_containers_cis(
aux, "REQUIREMENTS_ID", "REQUIREMENTS_ATTRIBUTES_SECTION"
)
+1
View File
@@ -9,6 +9,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
- `entra_conditional_access_policy_explicitly_targets_azure_devops` check for M365 provider, verifying at least one enabled Conditional Access policy explicitly includes the Azure DevOps cloud application instead of relying on a broad "All cloud apps" policy [(#11182)](https://github.com/prowler-cloud/prowler/pull/11182)
- `entra_conditional_access_policy_no_exclusion_gaps` check for M365 provider, verifying every user, group, role, or application excluded from an enabled Conditional Access policy stays in scope of another enabled policy [(#11577)](https://github.com/prowler-cloud/prowler/pull/11577)
- `stepfunctions_statemachine_encrypted_with_cmk` check for AWS provider, verifying that each Step Functions state machine uses a customer-managed KMS key for encryption at rest rather than the default AWS-owned key [(#11538)](https://github.com/prowler-cloud/prowler/pull/11538)
- CIS Microsoft 365 Foundations Benchmark v7.0.0 compliance framework for the M365 provider [(#11699)](https://github.com/prowler-cloud/prowler/pull/11699)
- `waf_regional_webacl_logging_enabled` check for AWS provider, verifying that each AWS WAF Classic Regional Web ACL has logging enabled to a Kinesis Data Firehose stream [(#11539)](https://github.com/prowler-cloud/prowler/pull/11539)
---
File diff suppressed because one or more lines are too long
@@ -0,0 +1,65 @@
import json
from pathlib import Path
from prowler.lib.check.compliance_models import (
CIS_Requirement_Attribute_AssessmentStatus,
CIS_Requirement_Attribute_Profile,
Compliance,
)
PROWLER_ROOT = Path(__file__).parents[5] / "prowler"
FRAMEWORK_PATH = PROWLER_ROOT / "compliance" / "m365" / "cis_7.0_m365.json"
M365_SERVICES_PATH = PROWLER_ROOT / "providers" / "m365" / "services"
VALID_PROFILES = {p.value for p in CIS_Requirement_Attribute_Profile}
VALID_STATUSES = {s.value for s in CIS_Requirement_Attribute_AssessmentStatus}
def _existing_m365_checks() -> set:
return {
metadata.stem.replace(".metadata", "")
for metadata in M365_SERVICES_PATH.rglob("*.metadata.json")
}
class TestCIS7_0_M365:
def test_framework_is_discoverable(self):
frameworks = Compliance.get_bulk("m365")
assert "cis_7.0_m365" in frameworks
def test_framework_metadata(self):
framework = Compliance.get_bulk("m365")["cis_7.0_m365"]
assert framework.Framework == "CIS"
assert framework.Provider == "M365"
assert framework.Version == "7.0"
assert framework.Name == "CIS Microsoft 365 Foundations Benchmark v7.0.0"
assert len(framework.Requirements) == 160
def test_requirement_ids_are_unique(self):
framework = Compliance.get_bulk("m365")["cis_7.0_m365"]
ids = [req.Id for req in framework.Requirements]
assert len(ids) == len(set(ids))
def test_each_requirement_has_one_attribute_with_section(self):
framework = Compliance.get_bulk("m365")["cis_7.0_m365"]
for req in framework.Requirements:
assert len(req.Attributes) == 1, f"{req.Id} must have exactly one attribute"
attribute = req.Attributes[0]
assert attribute.Section, f"{req.Id} has an empty Section"
assert attribute.Profile in VALID_PROFILES
assert attribute.AssessmentStatus in VALID_STATUSES
def test_all_mapped_checks_exist(self):
# Every check referenced by the framework must resolve to a real M365 check,
# otherwise the requirement would never be evaluated.
existing = _existing_m365_checks()
framework = json.loads(FRAMEWORK_PATH.read_text())
unknown = {
check
for req in framework["Requirements"]
for check in req["Checks"]
if check not in existing
}
assert (
not unknown
), f"Framework references unknown M365 checks: {sorted(unknown)}"