mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-11 05:54:17 +00:00
refactor(m365): normalize platforms at PlatformConditions model level
- Add pydantic validator to PlatformConditions that lowercases and extracts .value from enum objects on construction - Remove duplicated _normalize_platform() from both CA checks - Export MOBILE_PLATFORMS from entra_service for the mobile check
This commit is contained in:
3 files changed
+29
-70
No files matched your search
+14
-42
@@ -1,11 +1,17 @@
|
||||
from prowler.lib.check.models import Check, CheckReportM365
|
||||
from prowler.providers.m365.services.entra.entra_client import entra_client
|
||||
from prowler.providers.m365.services.entra.entra_service import (
|
||||
MOBILE_PLATFORMS,
|
||||
ConditionalAccessGrantControl,
|
||||
ConditionalAccessPolicyState,
|
||||
GrantControlOperator,
|
||||
)
|
||||
|
||||
MOBILE_APP_GRANT_CONTROLS = {
|
||||
ConditionalAccessGrantControl.APPROVED_APPLICATION,
|
||||
ConditionalAccessGrantControl.COMPLIANT_APPLICATION,
|
||||
}
|
||||
|
||||
|
||||
class entra_conditional_access_policy_approved_client_app_required_for_mobile(Check):
|
||||
"""Check if a Conditional Access policy requires approved client apps or app protection for mobile devices.
|
||||
@@ -17,19 +23,6 @@ class entra_conditional_access_policy_approved_client_app_required_for_mobile(Ch
|
||||
- FAIL: No policy restricts mobile app access to approved or protected apps.
|
||||
"""
|
||||
|
||||
REQUIRED_MOBILE_PLATFORMS = {"android", "ios"}
|
||||
MOBILE_APP_GRANT_CONTROLS = {
|
||||
ConditionalAccessGrantControl.APPROVED_APPLICATION,
|
||||
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,43 +47,22 @@ 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 = set(policy.conditions.platform_conditions.include_platforms)
|
||||
excluded = set(policy.conditions.platform_conditions.exclude_platforms)
|
||||
|
||||
targets_mobile_platforms = (
|
||||
"all" in included_platforms
|
||||
or self.REQUIRED_MOBILE_PLATFORMS.issubset(included_platforms)
|
||||
) and not (
|
||||
"all" in excluded_platforms
|
||||
or self.REQUIRED_MOBILE_PLATFORMS.intersection(excluded_platforms)
|
||||
)
|
||||
if not targets_mobile_platforms:
|
||||
targets_mobile = (
|
||||
"all" in included or MOBILE_PLATFORMS.issubset(included)
|
||||
) and not ("all" in excluded or MOBILE_PLATFORMS.intersection(excluded))
|
||||
if not targets_mobile:
|
||||
continue
|
||||
|
||||
built_in_controls = set(policy.grant_controls.built_in_controls)
|
||||
has_mobile_app_control = bool(
|
||||
self.MOBILE_APP_GRANT_CONTROLS.intersection(built_in_controls)
|
||||
)
|
||||
if not has_mobile_app_control:
|
||||
if not MOBILE_APP_GRANT_CONTROLS.intersection(built_in_controls):
|
||||
continue
|
||||
|
||||
if (
|
||||
policy.grant_controls.operator == GrantControlOperator.OR
|
||||
and not built_in_controls.issubset(self.MOBILE_APP_GRANT_CONTROLS)
|
||||
and not built_in_controls.issubset(MOBILE_APP_GRANT_CONTROLS)
|
||||
):
|
||||
continue
|
||||
|
||||
|
||||
+4
-27
@@ -19,13 +19,6 @@ class entra_conditional_access_policy_unknown_device_blocked(Check):
|
||||
- FAIL: No CA policy restricts access from unrecognized device platforms.
|
||||
"""
|
||||
|
||||
@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.
|
||||
|
||||
@@ -49,29 +42,13 @@ class entra_conditional_access_policy_unknown_device_blocked(Check):
|
||||
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 = set(policy.conditions.platform_conditions.include_platforms)
|
||||
excluded = set(policy.conditions.platform_conditions.exclude_platforms)
|
||||
|
||||
if "all" not in included_platforms:
|
||||
if "all" not in included:
|
||||
continue
|
||||
|
||||
if not {
|
||||
self._normalize_platform(platform) for platform in KNOWN_PLATFORMS
|
||||
}.issubset(excluded_platforms):
|
||||
if not KNOWN_PLATFORMS.issubset(excluded):
|
||||
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, Field
|
||||
from pydantic.v1 import BaseModel, Field, validator
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.m365.lib.service.service import M365Service
|
||||
@@ -1003,8 +1003,18 @@ class PlatformConditions(BaseModel):
|
||||
class Config:
|
||||
allow_population_by_field_name = True
|
||||
|
||||
@validator("include_platforms", "exclude_platforms", pre=True)
|
||||
def normalize_platforms(cls, v):
|
||||
normalized = []
|
||||
for platform in v:
|
||||
value = getattr(platform, "value", platform)
|
||||
if isinstance(value, str) and value:
|
||||
normalized.append(value.lower())
|
||||
return normalized
|
||||
|
||||
|
||||
KNOWN_PLATFORMS = frozenset({"android", "ios", "windows", "macos", "linux"})
|
||||
MOBILE_PLATFORMS = frozenset({"android", "ios"})
|
||||
|
||||
|
||||
class TransferMethod(Enum):
|
||||
|
||||
Reference in new issue
Block a user