Compare commits

...

1 Commits

Author SHA1 Message Date
Hugo P.Brito
e8ae0902a5 feat(m365): add entra_conditional_access_policy_block_unknown_device_platforms security check
Add new security check entra_conditional_access_policy_block_unknown_device_platforms for m365 provider.
Includes check implementation, metadata, and unit tests.
2026-04-08 15:53:37 +01:00
7 changed files with 813 additions and 0 deletions

View File

@@ -20,6 +20,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
- `calendar_external_sharing_primary_calendar`, `calendar_external_sharing_secondary_calendar`, and `calendar_external_invitations_warning` checks for Google Workspace provider using the Cloud Identity Policy API [(#10597)](https://github.com/prowler-cloud/prowler/pull/10597)
- `entra_conditional_access_policy_device_registration_mfa_required` check and `entra_intune_enrollment_sign_in_frequency_every_time` enhancement for M365 provider [(#10222)](https://github.com/prowler-cloud/prowler/pull/10222)
- `entra_conditional_access_policy_block_elevated_insider_risk` check for M365 provider [(#10234)](https://github.com/prowler-cloud/prowler/pull/10234)
- `entra_conditional_access_policy_block_unknown_device_platforms` check for m365 provider [(#10615)](https://github.com/prowler-cloud/prowler/pull/10615)
- `Vercel` provider support with 30 checks [(#10189)](https://github.com/prowler-cloud/prowler/pull/10189)
### 🔄 Changed

View File

@@ -207,6 +207,7 @@
"entra_admin_portals_access_restriction",
"entra_admin_users_phishing_resistant_mfa_enabled",
"entra_conditional_access_policy_block_o365_elevated_insider_risk",
"entra_conditional_access_policy_block_unknown_device_platforms",
"entra_policy_guest_users_access_restrictions",
"entra_seamless_sso_disabled"
]
@@ -249,6 +250,7 @@
"entra_all_apps_conditional_access_coverage",
"entra_conditional_access_policy_device_registration_mfa_required",
"entra_intune_enrollment_sign_in_frequency_every_time",
"entra_conditional_access_policy_block_unknown_device_platforms",
"entra_conditional_access_policy_device_code_flow_blocked",
"entra_legacy_authentication_blocked",
"entra_managed_device_required_for_authentication",
@@ -626,6 +628,7 @@
"entra_admin_users_phishing_resistant_mfa_enabled",
"entra_conditional_access_policy_approved_client_app_required_for_mobile",
"entra_conditional_access_policy_app_enforced_restrictions",
"entra_conditional_access_policy_block_unknown_device_platforms",
"entra_conditional_access_policy_device_registration_mfa_required",
"entra_intune_enrollment_sign_in_frequency_every_time",
"entra_managed_device_required_for_authentication",
@@ -686,6 +689,7 @@
"entra_conditional_access_policy_app_enforced_restrictions",
"entra_conditional_access_policy_block_elevated_insider_risk",
"entra_conditional_access_policy_block_o365_elevated_insider_risk",
"entra_conditional_access_policy_block_unknown_device_platforms",
"entra_policy_guest_users_access_restrictions",
"sharepoint_external_sharing_restricted"
]
@@ -710,6 +714,7 @@
"entra_intune_enrollment_sign_in_frequency_every_time",
"entra_break_glass_account_fido2_security_key_registered",
"entra_conditional_access_policy_approved_client_app_required_for_mobile",
"entra_conditional_access_policy_block_unknown_device_platforms",
"entra_conditional_access_policy_device_code_flow_blocked",
"entra_identity_protection_sign_in_risk_enabled",
"entra_managed_device_required_for_authentication",

View File

@@ -0,0 +1,39 @@
{
"Provider": "m365",
"CheckID": "entra_conditional_access_policy_block_unknown_device_platforms",
"CheckTitle": "Conditional Access policy blocks access from unknown or unsupported device platforms",
"CheckType": [],
"ServiceName": "entra",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "medium",
"ResourceType": "NotDefined",
"ResourceGroup": "IAM",
"Description": "Microsoft Entra **Conditional Access** can block sign-ins from device platforms that the organization does not recognize or support. A policy that includes **all** platforms and excludes the known ones (Android, iOS, Windows, macOS, Linux) effectively blocks any **unknown or unsupported** platform, reducing the attack surface from unmanaged or unexpected devices.",
"Risk": "Without blocking unknown device platforms, attackers may authenticate from **spoofed or uncommon operating systems** that bypass platform-specific security controls. This increases the risk of **unauthorized access** and makes it harder to enforce device compliance, potentially leading to **credential theft** or **data exfiltration** from unmanaged environments.",
"RelatedUrl": "",
"AdditionalURLs": [
"https://learn.microsoft.com/en-us/entra/identity/conditional-access/howto-policy-unknown-unsupported-device",
"https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-conditional-access-conditions#device-platforms",
"https://maester.dev/docs/tests/MT.1015"
],
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "1. Navigate to the Microsoft Entra admin center https://entra.microsoft.com.\n2. Expand **Protection** > **Conditional Access** and select **Policies**.\n3. Select **New policy**.\n4. Under **Users**, include the desired scope (e.g., **All users**).\n5. Under **Target resources**, include **All cloud apps** or the applications you want to protect.\n6. Under **Conditions** > **Device platforms**, set **Configure** to **Yes**.\n7. Under **Include**, select **Any device**.\n8. Under **Exclude**, select **Android**, **iOS**, **Windows**, **macOS**, and **Linux**.\n9. Under **Grant**, select **Block access**.\n10. Set **Enable policy** to **On** and click **Create**.",
"Terraform": ""
},
"Recommendation": {
"Text": "Create a Conditional Access policy that includes **all device platforms** and excludes the known ones (Android, iOS, Windows, macOS, Linux), then set the grant control to **Block access**. This ensures that only recognized platforms can authenticate, following a **zero-trust** approach to device management.",
"Url": "https://hub.prowler.com/check/entra_conditional_access_policy_block_unknown_device_platforms"
}
},
"Categories": [
"trust-boundaries",
"e3"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": "This check corresponds to Maester test **MT.1015** (`Test-MtCaBlockUnknownOrUnsupportedDevicePlatform`). The recommended policy includes all platforms and excludes the five known platforms (Android, iOS, Windows, macOS, Linux) so that only unknown or unsupported platforms are blocked. The check requires the policy to be fully enabled; report-only policies are treated as non-compliant."
}

View File

@@ -0,0 +1,137 @@
"""Check for Conditional Access policy blocking unknown or unsupported device platforms."""
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 (
ConditionalAccessGrantControl,
ConditionalAccessPolicy,
ConditionalAccessPolicyState,
)
class entra_conditional_access_policy_block_unknown_device_platforms(Check):
"""Ensure a Conditional Access policy blocks access from unknown or unsupported device platforms.
This check verifies that at least one enabled Conditional Access policy
blocks access when the device platform is unknown or unsupported. The
recommended configuration includes all device platforms and excludes the
known platforms (Android, iOS, Windows, macOS, Linux), so only
unrecognised platforms are blocked.
- PASS: An enabled policy blocks access from unknown or unsupported device platforms.
- FAIL: No policy blocks access from unknown or unsupported device platforms.
"""
KNOWN_PLATFORMS = {"android", "ios", "windows", "macos", "linux"}
@staticmethod
def _normalize_platform(platform: object) -> str:
"""Normalize a platform value to a lowercase string.
Args:
platform: A platform value that may be a string or an enum.
Returns:
The lowercase string representation of the platform.
"""
normalized = getattr(platform, "value", platform)
return normalized.lower() if isinstance(normalized, str) else ""
def _is_candidate_policy(self, policy: ConditionalAccessPolicy) -> bool:
"""Determine whether a policy is a candidate for blocking unknown device platforms.
A candidate policy must:
- Not be disabled.
- Have platform conditions configured.
- Include all platforms.
- Exclude all known platforms so only unknown ones are affected.
- Use the block grant control.
Args:
policy: The Conditional Access policy to evaluate.
Returns:
True if the policy is a candidate, False otherwise.
"""
if policy.state == ConditionalAccessPolicyState.DISABLED:
return False
if not policy.conditions.platform_conditions:
return False
included_platforms = {
p
for p in map(
self._normalize_platform,
policy.conditions.platform_conditions.include_platforms,
)
if p
}
if "all" not in included_platforms:
return False
excluded_platforms = {
p
for p in map(
self._normalize_platform,
policy.conditions.platform_conditions.exclude_platforms,
)
if p
}
if not self.KNOWN_PLATFORMS.issubset(excluded_platforms):
return False
if (
ConditionalAccessGrantControl.BLOCK
not in policy.grant_controls.built_in_controls
):
return False
return True
def execute(self) -> list[CheckReportM365]:
"""Execute the check logic.
Returns:
A list of reports containing the result of the check.
"""
findings = []
report = CheckReportM365(
metadata=self.metadata(),
resource={},
resource_name="Conditional Access Policies",
resource_id="conditionalAccessPolicies",
)
report.status = "FAIL"
report.status_extended = "No Conditional Access Policy blocks access from unknown or unsupported device platforms."
for policy in entra_client.conditional_access_policies.values():
if not self._is_candidate_policy(policy):
continue
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.display_name,
resource_id=policy.id,
)
if policy.state == ConditionalAccessPolicyState.ENABLED_FOR_REPORTING:
report.status = "FAIL"
report.status_extended = (
f"Conditional Access Policy '{policy.display_name}' reports "
"blocking unknown or unsupported device platforms but does not enforce it."
)
else:
report.status = "PASS"
report.status_extended = (
f"Conditional Access Policy '{policy.display_name}' blocks "
"access from unknown or unsupported device platforms."
)
break
findings.append(report)
return findings

View File

@@ -0,0 +1,631 @@
from unittest import mock
from uuid import uuid4
from prowler.providers.m365.services.entra.entra_service import (
ApplicationsConditions,
ConditionalAccessGrantControl,
ConditionalAccessPolicyState,
Conditions,
GrantControlOperator,
GrantControls,
PersistentBrowser,
PlatformConditions,
SessionControls,
SignInFrequency,
SignInFrequencyInterval,
UsersConditions,
)
from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
CHECK_MODULE_PATH = "prowler.providers.m365.services.entra.entra_conditional_access_policy_block_unknown_device_platforms.entra_conditional_access_policy_block_unknown_device_platforms"
KNOWN_PLATFORMS = ["android", "iOS", "windows", "macOS", "linux"]
def _make_session_controls() -> SessionControls:
"""Return a minimal SessionControls instance for testing."""
return SessionControls(
persistent_browser=PersistentBrowser(is_enabled=False, mode="always"),
sign_in_frequency=SignInFrequency(
is_enabled=False,
frequency=None,
type=None,
interval=SignInFrequencyInterval.EVERY_TIME,
),
)
class Test_entra_conditional_access_policy_block_unknown_device_platforms:
"""Tests for the entra_conditional_access_policy_block_unknown_device_platforms check."""
def test_no_conditional_access_policies(self):
"""Test FAIL when there are no Conditional Access policies."""
entra_client = mock.MagicMock
entra_client.audited_tenant = "audited_tenant"
entra_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
f"{CHECK_MODULE_PATH}.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_conditional_access_policy_block_unknown_device_platforms.entra_conditional_access_policy_block_unknown_device_platforms import (
entra_conditional_access_policy_block_unknown_device_platforms,
)
entra_client.conditional_access_policies = {}
check = entra_conditional_access_policy_block_unknown_device_platforms()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "No Conditional Access Policy blocks access from unknown or unsupported device platforms."
)
assert result[0].resource == {}
assert result[0].resource_name == "Conditional Access Policies"
assert result[0].resource_id == "conditionalAccessPolicies"
assert result[0].location == "global"
def test_policy_disabled(self):
"""Test FAIL when the only matching policy is disabled."""
policy_id = str(uuid4())
display_name = "Block Unknown Platforms"
entra_client = mock.MagicMock
entra_client.audited_tenant = "audited_tenant"
entra_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
f"{CHECK_MODULE_PATH}.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_conditional_access_policy_block_unknown_device_platforms.entra_conditional_access_policy_block_unknown_device_platforms import (
entra_conditional_access_policy_block_unknown_device_platforms,
)
from prowler.providers.m365.services.entra.entra_service import (
ConditionalAccessPolicy,
)
entra_client.conditional_access_policies = {
policy_id: ConditionalAccessPolicy(
id=policy_id,
display_name=display_name,
conditions=Conditions(
application_conditions=ApplicationsConditions(
included_applications=["All"],
excluded_applications=[],
included_user_actions=[],
),
user_conditions=UsersConditions(
included_groups=[],
excluded_groups=[],
included_users=["All"],
excluded_users=[],
included_roles=[],
excluded_roles=[],
),
client_app_types=[],
user_risk_levels=[],
platform_conditions=PlatformConditions(
include_platforms=["all"],
exclude_platforms=KNOWN_PLATFORMS,
),
),
grant_controls=GrantControls(
built_in_controls=[ConditionalAccessGrantControl.BLOCK],
operator=GrantControlOperator.OR,
authentication_strength=None,
),
session_controls=_make_session_controls(),
state=ConditionalAccessPolicyState.DISABLED,
)
}
check = entra_conditional_access_policy_block_unknown_device_platforms()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "No Conditional Access Policy blocks access from unknown or unsupported device platforms."
)
assert result[0].resource == {}
assert result[0].resource_name == "Conditional Access Policies"
assert result[0].resource_id == "conditionalAccessPolicies"
assert result[0].location == "global"
def test_policy_enabled_for_reporting_only(self):
"""Test FAIL when the matching policy is only in report-only mode."""
policy_id = str(uuid4())
display_name = "Block Unknown Platforms"
entra_client = mock.MagicMock
entra_client.audited_tenant = "audited_tenant"
entra_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
f"{CHECK_MODULE_PATH}.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_conditional_access_policy_block_unknown_device_platforms.entra_conditional_access_policy_block_unknown_device_platforms import (
entra_conditional_access_policy_block_unknown_device_platforms,
)
from prowler.providers.m365.services.entra.entra_service import (
ConditionalAccessPolicy,
)
entra_client.conditional_access_policies = {
policy_id: ConditionalAccessPolicy(
id=policy_id,
display_name=display_name,
conditions=Conditions(
application_conditions=ApplicationsConditions(
included_applications=["All"],
excluded_applications=[],
included_user_actions=[],
),
user_conditions=UsersConditions(
included_groups=[],
excluded_groups=[],
included_users=["All"],
excluded_users=[],
included_roles=[],
excluded_roles=[],
),
client_app_types=[],
user_risk_levels=[],
platform_conditions=PlatformConditions(
include_platforms=["all"],
exclude_platforms=KNOWN_PLATFORMS,
),
),
grant_controls=GrantControls(
built_in_controls=[ConditionalAccessGrantControl.BLOCK],
operator=GrantControlOperator.OR,
authentication_strength=None,
),
session_controls=_make_session_controls(),
state=ConditionalAccessPolicyState.ENABLED_FOR_REPORTING,
)
}
check = entra_conditional_access_policy_block_unknown_device_platforms()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Conditional Access Policy 'Block Unknown Platforms' reports blocking unknown or unsupported device platforms but does not enforce it."
)
assert result[0].resource_name == "Block Unknown Platforms"
assert result[0].resource_id == policy_id
assert result[0].location == "global"
def test_policy_no_platform_conditions(self):
"""Test FAIL when the policy has no platform conditions configured."""
policy_id = str(uuid4())
display_name = "Block Unknown Platforms"
entra_client = mock.MagicMock
entra_client.audited_tenant = "audited_tenant"
entra_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
f"{CHECK_MODULE_PATH}.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_conditional_access_policy_block_unknown_device_platforms.entra_conditional_access_policy_block_unknown_device_platforms import (
entra_conditional_access_policy_block_unknown_device_platforms,
)
from prowler.providers.m365.services.entra.entra_service import (
ConditionalAccessPolicy,
)
entra_client.conditional_access_policies = {
policy_id: ConditionalAccessPolicy(
id=policy_id,
display_name=display_name,
conditions=Conditions(
application_conditions=ApplicationsConditions(
included_applications=["All"],
excluded_applications=[],
included_user_actions=[],
),
user_conditions=UsersConditions(
included_groups=[],
excluded_groups=[],
included_users=["All"],
excluded_users=[],
included_roles=[],
excluded_roles=[],
),
client_app_types=[],
user_risk_levels=[],
platform_conditions=None,
),
grant_controls=GrantControls(
built_in_controls=[ConditionalAccessGrantControl.BLOCK],
operator=GrantControlOperator.OR,
authentication_strength=None,
),
session_controls=_make_session_controls(),
state=ConditionalAccessPolicyState.ENABLED,
)
}
check = entra_conditional_access_policy_block_unknown_device_platforms()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "No Conditional Access Policy blocks access from unknown or unsupported device platforms."
)
def test_policy_does_not_include_all_platforms(self):
"""Test FAIL when the policy includes specific platforms instead of all."""
policy_id = str(uuid4())
display_name = "Block Specific Platforms"
entra_client = mock.MagicMock
entra_client.audited_tenant = "audited_tenant"
entra_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
f"{CHECK_MODULE_PATH}.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_conditional_access_policy_block_unknown_device_platforms.entra_conditional_access_policy_block_unknown_device_platforms import (
entra_conditional_access_policy_block_unknown_device_platforms,
)
from prowler.providers.m365.services.entra.entra_service import (
ConditionalAccessPolicy,
)
entra_client.conditional_access_policies = {
policy_id: ConditionalAccessPolicy(
id=policy_id,
display_name=display_name,
conditions=Conditions(
application_conditions=ApplicationsConditions(
included_applications=["All"],
excluded_applications=[],
included_user_actions=[],
),
user_conditions=UsersConditions(
included_groups=[],
excluded_groups=[],
included_users=["All"],
excluded_users=[],
included_roles=[],
excluded_roles=[],
),
client_app_types=[],
user_risk_levels=[],
platform_conditions=PlatformConditions(
include_platforms=["android", "iOS"],
exclude_platforms=[],
),
),
grant_controls=GrantControls(
built_in_controls=[ConditionalAccessGrantControl.BLOCK],
operator=GrantControlOperator.OR,
authentication_strength=None,
),
session_controls=_make_session_controls(),
state=ConditionalAccessPolicyState.ENABLED,
)
}
check = entra_conditional_access_policy_block_unknown_device_platforms()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
def test_policy_missing_excluded_known_platforms(self):
"""Test FAIL when the policy includes all platforms but does not exclude all known ones."""
policy_id = str(uuid4())
display_name = "Incomplete Platform Exclusion"
entra_client = mock.MagicMock
entra_client.audited_tenant = "audited_tenant"
entra_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
f"{CHECK_MODULE_PATH}.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_conditional_access_policy_block_unknown_device_platforms.entra_conditional_access_policy_block_unknown_device_platforms import (
entra_conditional_access_policy_block_unknown_device_platforms,
)
from prowler.providers.m365.services.entra.entra_service import (
ConditionalAccessPolicy,
)
# Only exclude 3 of 5 known platforms
entra_client.conditional_access_policies = {
policy_id: ConditionalAccessPolicy(
id=policy_id,
display_name=display_name,
conditions=Conditions(
application_conditions=ApplicationsConditions(
included_applications=["All"],
excluded_applications=[],
included_user_actions=[],
),
user_conditions=UsersConditions(
included_groups=[],
excluded_groups=[],
included_users=["All"],
excluded_users=[],
included_roles=[],
excluded_roles=[],
),
client_app_types=[],
user_risk_levels=[],
platform_conditions=PlatformConditions(
include_platforms=["all"],
exclude_platforms=["android", "iOS", "windows"],
),
),
grant_controls=GrantControls(
built_in_controls=[ConditionalAccessGrantControl.BLOCK],
operator=GrantControlOperator.OR,
authentication_strength=None,
),
session_controls=_make_session_controls(),
state=ConditionalAccessPolicyState.ENABLED,
)
}
check = entra_conditional_access_policy_block_unknown_device_platforms()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
def test_policy_no_block_grant_control(self):
"""Test FAIL when the policy has correct platform conditions but does not block."""
policy_id = str(uuid4())
display_name = "MFA Unknown Platforms"
entra_client = mock.MagicMock
entra_client.audited_tenant = "audited_tenant"
entra_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
f"{CHECK_MODULE_PATH}.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_conditional_access_policy_block_unknown_device_platforms.entra_conditional_access_policy_block_unknown_device_platforms import (
entra_conditional_access_policy_block_unknown_device_platforms,
)
from prowler.providers.m365.services.entra.entra_service import (
ConditionalAccessPolicy,
)
entra_client.conditional_access_policies = {
policy_id: ConditionalAccessPolicy(
id=policy_id,
display_name=display_name,
conditions=Conditions(
application_conditions=ApplicationsConditions(
included_applications=["All"],
excluded_applications=[],
included_user_actions=[],
),
user_conditions=UsersConditions(
included_groups=[],
excluded_groups=[],
included_users=["All"],
excluded_users=[],
included_roles=[],
excluded_roles=[],
),
client_app_types=[],
user_risk_levels=[],
platform_conditions=PlatformConditions(
include_platforms=["all"],
exclude_platforms=KNOWN_PLATFORMS,
),
),
grant_controls=GrantControls(
built_in_controls=[ConditionalAccessGrantControl.MFA],
operator=GrantControlOperator.OR,
authentication_strength=None,
),
session_controls=_make_session_controls(),
state=ConditionalAccessPolicyState.ENABLED,
)
}
check = entra_conditional_access_policy_block_unknown_device_platforms()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
def test_policy_enabled_and_compliant(self):
"""Test PASS when an enabled policy blocks unknown device platforms correctly."""
policy_id = str(uuid4())
display_name = "Block Unknown Platforms"
entra_client = mock.MagicMock
entra_client.audited_tenant = "audited_tenant"
entra_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
f"{CHECK_MODULE_PATH}.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_conditional_access_policy_block_unknown_device_platforms.entra_conditional_access_policy_block_unknown_device_platforms import (
entra_conditional_access_policy_block_unknown_device_platforms,
)
from prowler.providers.m365.services.entra.entra_service import (
ConditionalAccessPolicy,
)
entra_client.conditional_access_policies = {
policy_id: ConditionalAccessPolicy(
id=policy_id,
display_name=display_name,
conditions=Conditions(
application_conditions=ApplicationsConditions(
included_applications=["All"],
excluded_applications=[],
included_user_actions=[],
),
user_conditions=UsersConditions(
included_groups=[],
excluded_groups=[],
included_users=["All"],
excluded_users=[],
included_roles=[],
excluded_roles=[],
),
client_app_types=[],
user_risk_levels=[],
platform_conditions=PlatformConditions(
include_platforms=["all"],
exclude_platforms=KNOWN_PLATFORMS,
),
),
grant_controls=GrantControls(
built_in_controls=[ConditionalAccessGrantControl.BLOCK],
operator=GrantControlOperator.OR,
authentication_strength=None,
),
session_controls=_make_session_controls(),
state=ConditionalAccessPolicyState.ENABLED,
)
}
check = entra_conditional_access_policy_block_unknown_device_platforms()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Conditional Access Policy 'Block Unknown Platforms' blocks access from unknown or unsupported device platforms."
)
assert result[0].resource_name == "Block Unknown Platforms"
assert result[0].resource_id == policy_id
assert result[0].location == "global"
def test_mixed_policies_report_only_and_enabled(self):
"""Test PASS when both report-only and enabled compliant policies exist."""
report_policy_id = str(uuid4())
enabled_policy_id = str(uuid4())
entra_client = mock.MagicMock
entra_client.audited_tenant = "audited_tenant"
entra_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
f"{CHECK_MODULE_PATH}.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_conditional_access_policy_block_unknown_device_platforms.entra_conditional_access_policy_block_unknown_device_platforms import (
entra_conditional_access_policy_block_unknown_device_platforms,
)
from prowler.providers.m365.services.entra.entra_service import (
ConditionalAccessPolicy,
)
base_conditions = Conditions(
application_conditions=ApplicationsConditions(
included_applications=["All"],
excluded_applications=[],
included_user_actions=[],
),
user_conditions=UsersConditions(
included_groups=[],
excluded_groups=[],
included_users=["All"],
excluded_users=[],
included_roles=[],
excluded_roles=[],
),
client_app_types=[],
user_risk_levels=[],
platform_conditions=PlatformConditions(
include_platforms=["all"],
exclude_platforms=KNOWN_PLATFORMS,
),
)
grant_controls = GrantControls(
built_in_controls=[ConditionalAccessGrantControl.BLOCK],
operator=GrantControlOperator.OR,
authentication_strength=None,
)
entra_client.conditional_access_policies = {
report_policy_id: ConditionalAccessPolicy(
id=report_policy_id,
display_name="Report Only Policy",
conditions=base_conditions,
grant_controls=grant_controls,
session_controls=_make_session_controls(),
state=ConditionalAccessPolicyState.ENABLED_FOR_REPORTING,
),
enabled_policy_id: ConditionalAccessPolicy(
id=enabled_policy_id,
display_name="Enforced Block Policy",
conditions=base_conditions,
grant_controls=grant_controls,
session_controls=_make_session_controls(),
state=ConditionalAccessPolicyState.ENABLED,
),
}
check = entra_conditional_access_policy_block_unknown_device_platforms()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].resource_name == "Enforced Block Policy"
assert result[0].resource_id == enabled_policy_id