refactor(m365): normalize CA platforms at model level (#10635)

Co-authored-by: Hugo P.Brito <hugopbrito@Mac.home>
This commit is contained in:
Hugo Pereira Brito
2026-04-14 15:00:23 +02:00
committed by GitHub
co-authored by Hugo P.Brito
parent 90a619a8b4
commit a82eaa885d
5 changed files with 34 additions and 42 deletions
+1
View File
@@ -21,6 +21,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
### 🔄 Changed
- Bump Poetry to `2.3.4` and consolidate SDK workflows onto the `setup-python-poetry` composite action with opt-in lockfile regeneration [(#10681)](https://github.com/prowler-cloud/prowler/pull/10681)
- Normalize Conditional Access platform values in Entra models and simplify platform-based checks [(#10635)](https://github.com/prowler-cloud/prowler/pull/10635)
---
@@ -23,13 +23,6 @@ class entra_conditional_access_policy_approved_client_app_required_for_mobile(Ch
ConditionalAccessGrantControl.COMPLIANT_APPLICATION,
}
@staticmethod
def _normalize_platform(platform: object) -> str:
normalized_platform = getattr(platform, "value", platform)
return (
normalized_platform.lower() if isinstance(normalized_platform, str) else ""
)
def execute(self) -> list[CheckReportM365]:
"""Execute the check logic.
@@ -54,22 +47,12 @@ class entra_conditional_access_policy_approved_client_app_required_for_mobile(Ch
if not policy.conditions.platform_conditions:
continue
included_platforms = {
normalized_platform
for normalized_platform in map(
self._normalize_platform,
policy.conditions.platform_conditions.include_platforms,
)
if normalized_platform
}
excluded_platforms = {
normalized_platform
for normalized_platform in map(
self._normalize_platform,
policy.conditions.platform_conditions.exclude_platforms,
)
if normalized_platform
}
included_platforms = set(
policy.conditions.platform_conditions.include_platforms
)
excluded_platforms = set(
policy.conditions.platform_conditions.exclude_platforms
)
targets_mobile_platforms = (
"all" in included_platforms
@@ -102,10 +85,10 @@ class entra_conditional_access_policy_approved_client_app_required_for_mobile(Ch
)
if policy.state == ConditionalAccessPolicyState.ENABLED_FOR_REPORTING:
report.status = "FAIL"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' reports the requirement of approved client apps or app protection for mobile devices but does not enforce it."
report.status_extended = f"Conditional Access Policy {policy.display_name} reports the requirement of approved client apps or app protection for mobile devices but does not enforce it."
else:
report.status = "PASS"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' requires approved client apps or app protection for mobile devices."
report.status_extended = f"Conditional Access Policy {policy.display_name} requires approved client apps or app protection for mobile devices."
break
findings.append(report)
@@ -47,18 +47,12 @@ class entra_conditional_access_policy_block_unknown_device_platforms(Check):
if not policy.conditions.platform_conditions:
continue
included_platforms = {
(getattr(p, "value", p) or "").lower()
for p in policy.conditions.platform_conditions.include_platforms
}
if "all" not in included_platforms:
if "all" not in policy.conditions.platform_conditions.include_platforms:
continue
excluded_platforms = {
(getattr(p, "value", p) or "").lower()
for p in policy.conditions.platform_conditions.exclude_platforms
}
if not self.KNOWN_PLATFORMS.issubset(excluded_platforms):
if not self.KNOWN_PLATFORMS.issubset(
set(policy.conditions.platform_conditions.exclude_platforms)
):
continue
if (
@@ -9,7 +9,7 @@ from msgraph.generated.models.o_data_errors.o_data_error import ODataError
from msgraph.generated.security.microsoft_graph_security_run_hunting_query.run_hunting_query_post_request_body import (
RunHuntingQueryPostRequestBody,
)
from pydantic.v1 import BaseModel
from pydantic.v1 import BaseModel, validator
from prowler.lib.logger import logger
from prowler.providers.m365.lib.service.service import M365Service
@@ -1172,6 +1172,20 @@ class PlatformConditions(BaseModel):
include_platforms: List[str] = []
exclude_platforms: List[str] = []
@validator("include_platforms", "exclude_platforms", pre=True)
@classmethod
def normalize_platforms(cls, values):
if not values:
return []
normalized = []
for platform in values:
value = getattr(platform, "value", platform)
if isinstance(value, str) and value:
normalized.append(value.lower())
return normalized
class TransferMethod(Enum):
"""Transfer methods for authentication flows in Conditional Access policies."""
@@ -222,7 +222,7 @@ class Test_entra_conditional_access_policy_approved_client_app_required_for_mobi
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Conditional Access Policy '{display_name}' reports the requirement of approved client apps or app protection for mobile devices but does not enforce it."
== f"Conditional Access Policy {display_name} reports the requirement of approved client apps or app protection for mobile devices but does not enforce it."
)
assert (
result[0].resource
@@ -312,7 +312,7 @@ class Test_entra_conditional_access_policy_approved_client_app_required_for_mobi
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Conditional Access Policy '{display_name}' requires approved client apps or app protection for mobile devices."
== f"Conditional Access Policy {display_name} requires approved client apps or app protection for mobile devices."
)
assert (
result[0].resource
@@ -738,7 +738,7 @@ class Test_entra_conditional_access_policy_approved_client_app_required_for_mobi
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Conditional Access Policy '{display_name}' requires approved client apps or app protection for mobile devices."
== f"Conditional Access Policy {display_name} requires approved client apps or app protection for mobile devices."
)
assert (
result[0].resource
@@ -827,7 +827,7 @@ class Test_entra_conditional_access_policy_approved_client_app_required_for_mobi
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Conditional Access Policy '{display_name}' requires approved client apps or app protection for mobile devices."
== f"Conditional Access Policy {display_name} requires approved client apps or app protection for mobile devices."
)
assert (
result[0].resource
@@ -963,7 +963,7 @@ class Test_entra_conditional_access_policy_approved_client_app_required_for_mobi
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Conditional Access Policy '{enabled_name}' requires approved client apps or app protection for mobile devices."
== f"Conditional Access Policy {enabled_name} requires approved client apps or app protection for mobile devices."
)
assert (
result[0].resource
@@ -1052,7 +1052,7 @@ class Test_entra_conditional_access_policy_approved_client_app_required_for_mobi
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Conditional Access Policy '{display_name}' requires approved client apps or app protection for mobile devices."
== f"Conditional Access Policy {display_name} requires approved client apps or app protection for mobile devices."
)
assert (
result[0].resource