mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-03-21 18:58:04 +00:00
feat(m365): add entra_require_mfa_for_management_api security check (#10150)
Co-authored-by: Andoni Alonso <14891798+andoniaf@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
6d9ef78df1
commit
db1db7d366
@@ -8,7 +8,7 @@ from tests.providers.azure.azure_fixtures import (
|
||||
)
|
||||
|
||||
|
||||
class Test_entra_conditional_access_policy_require_mfa_for_management_api:
|
||||
class Test_entra_require_mfa_for_management_api:
|
||||
def test_entra_no_subscriptions(self):
|
||||
entra_client = mock.MagicMock
|
||||
|
||||
@@ -18,18 +18,18 @@ class Test_entra_conditional_access_policy_require_mfa_for_management_api:
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api.entra_client",
|
||||
"prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api import (
|
||||
entra_conditional_access_policy_require_mfa_for_management_api,
|
||||
from prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
|
||||
entra_client.conditional_access_policy = {}
|
||||
entra_client.tenant_ids = TENANT_IDS
|
||||
|
||||
check = entra_conditional_access_policy_require_mfa_for_management_api()
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
@@ -42,19 +42,19 @@ class Test_entra_conditional_access_policy_require_mfa_for_management_api:
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api.entra_client",
|
||||
"prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api import (
|
||||
entra_conditional_access_policy_require_mfa_for_management_api,
|
||||
from prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
|
||||
# No policies configured
|
||||
entra_client.conditional_access_policy = {DOMAIN: {}}
|
||||
entra_client.tenant_ids = TENANT_IDS
|
||||
|
||||
check = entra_conditional_access_policy_require_mfa_for_management_api()
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
@@ -76,12 +76,12 @@ class Test_entra_conditional_access_policy_require_mfa_for_management_api:
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api.entra_client",
|
||||
"prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api import (
|
||||
entra_conditional_access_policy_require_mfa_for_management_api,
|
||||
from prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
from prowler.providers.azure.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
@@ -99,7 +99,7 @@ class Test_entra_conditional_access_policy_require_mfa_for_management_api:
|
||||
entra_client.conditional_access_policy = {DOMAIN: {policy_id: policy}}
|
||||
entra_client.tenant_ids = TENANT_IDS
|
||||
|
||||
check = entra_conditional_access_policy_require_mfa_for_management_api()
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
@@ -122,12 +122,12 @@ class Test_entra_conditional_access_policy_require_mfa_for_management_api:
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api.entra_client",
|
||||
"prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api import (
|
||||
entra_conditional_access_policy_require_mfa_for_management_api,
|
||||
from prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
from prowler.providers.azure.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
@@ -145,7 +145,7 @@ class Test_entra_conditional_access_policy_require_mfa_for_management_api:
|
||||
entra_client.conditional_access_policy = {DOMAIN: {policy_id: policy}}
|
||||
entra_client.tenant_ids = TENANT_IDS
|
||||
|
||||
check = entra_conditional_access_policy_require_mfa_for_management_api()
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
@@ -167,12 +167,12 @@ class Test_entra_conditional_access_policy_require_mfa_for_management_api:
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api.entra_client",
|
||||
"prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api import (
|
||||
entra_conditional_access_policy_require_mfa_for_management_api,
|
||||
from prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
from prowler.providers.azure.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
@@ -190,7 +190,7 @@ class Test_entra_conditional_access_policy_require_mfa_for_management_api:
|
||||
entra_client.conditional_access_policy = {DOMAIN: {policy_id: policy}}
|
||||
entra_client.tenant_ids = TENANT_IDS
|
||||
|
||||
check = entra_conditional_access_policy_require_mfa_for_management_api()
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
@@ -213,12 +213,12 @@ class Test_entra_conditional_access_policy_require_mfa_for_management_api:
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api.entra_client",
|
||||
"prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api import (
|
||||
entra_conditional_access_policy_require_mfa_for_management_api,
|
||||
from prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
from prowler.providers.azure.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
@@ -236,7 +236,7 @@ class Test_entra_conditional_access_policy_require_mfa_for_management_api:
|
||||
entra_client.conditional_access_policy = {DOMAIN: {policy_id: policy}}
|
||||
entra_client.tenant_ids = TENANT_IDS
|
||||
|
||||
check = entra_conditional_access_policy_require_mfa_for_management_api()
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
@@ -259,12 +259,12 @@ class Test_entra_conditional_access_policy_require_mfa_for_management_api:
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api.entra_client",
|
||||
"prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_management_api.entra_conditional_access_policy_require_mfa_for_management_api import (
|
||||
entra_conditional_access_policy_require_mfa_for_management_api,
|
||||
from prowler.providers.azure.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
from prowler.providers.azure.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
@@ -282,7 +282,7 @@ class Test_entra_conditional_access_policy_require_mfa_for_management_api:
|
||||
entra_client.conditional_access_policy = {DOMAIN: {policy_id: policy}}
|
||||
entra_client.tenant_ids = TENANT_IDS
|
||||
|
||||
check = entra_conditional_access_policy_require_mfa_for_management_api()
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
@@ -0,0 +1,696 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.m365.services.entra.entra_service import (
|
||||
ApplicationsConditions,
|
||||
ConditionalAccessGrantControl,
|
||||
ConditionalAccessPolicyState,
|
||||
Conditions,
|
||||
GrantControlOperator,
|
||||
GrantControls,
|
||||
PersistentBrowser,
|
||||
SessionControls,
|
||||
SignInFrequency,
|
||||
SignInFrequencyInterval,
|
||||
UsersConditions,
|
||||
)
|
||||
from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
|
||||
|
||||
AZURE_MANAGEMENT_API_APP_ID = "797f4846-ba00-4fd7-ba43-dac1f8f63013"
|
||||
|
||||
CHECK_MODULE_PATH = "prowler.providers.m365.services.entra.entra_require_mfa_for_management_api.entra_require_mfa_for_management_api"
|
||||
|
||||
|
||||
class Test_m365_entra_require_mfa_for_management_api:
|
||||
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_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
|
||||
entra_client.conditional_access_policies = {}
|
||||
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy requires MFA for Azure Management API."
|
||||
)
|
||||
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 = "Require MFA for Azure Management"
|
||||
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_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
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=[AZURE_MANAGEMENT_API_APP_ID],
|
||||
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=[],
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[ConditionalAccessGrantControl.MFA],
|
||||
operator=GrantControlOperator.OR,
|
||||
authentication_strength=None,
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=False,
|
||||
frequency=None,
|
||||
type=None,
|
||||
interval=SignInFrequencyInterval.EVERY_TIME,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.DISABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy requires MFA for Azure Management API."
|
||||
)
|
||||
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 = "Require MFA for Azure Management"
|
||||
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_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
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=[AZURE_MANAGEMENT_API_APP_ID],
|
||||
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=[],
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[ConditionalAccessGrantControl.MFA],
|
||||
operator=GrantControlOperator.OR,
|
||||
authentication_strength=None,
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=False,
|
||||
frequency=None,
|
||||
type=None,
|
||||
interval=SignInFrequencyInterval.EVERY_TIME,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED_FOR_REPORTING,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Conditional Access Policy {display_name} targets Azure Management API with MFA but is only in report-only mode."
|
||||
)
|
||||
assert (
|
||||
result[0].resource
|
||||
== entra_client.conditional_access_policies[policy_id].dict()
|
||||
)
|
||||
assert result[0].resource_name == display_name
|
||||
assert result[0].resource_id == policy_id
|
||||
assert result[0].location == "global"
|
||||
|
||||
def test_policy_no_application_conditions(self):
|
||||
"""Test FAIL when the policy has no application conditions."""
|
||||
policy_id = str(uuid4())
|
||||
display_name = "Policy Without App Conditions"
|
||||
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_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
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=None,
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[],
|
||||
excluded_groups=[],
|
||||
included_users=["All"],
|
||||
excluded_users=[],
|
||||
included_roles=[],
|
||||
excluded_roles=[],
|
||||
),
|
||||
client_app_types=[],
|
||||
user_risk_levels=[],
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[ConditionalAccessGrantControl.MFA],
|
||||
operator=GrantControlOperator.OR,
|
||||
authentication_strength=None,
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=False,
|
||||
frequency=None,
|
||||
type=None,
|
||||
interval=SignInFrequencyInterval.EVERY_TIME,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy requires MFA for Azure Management API."
|
||||
)
|
||||
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_does_not_target_azure_management_api(self):
|
||||
"""Test FAIL when the policy targets a different application."""
|
||||
policy_id = str(uuid4())
|
||||
display_name = "Require MFA for All Apps"
|
||||
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_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
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=["some-other-app-id"],
|
||||
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=[],
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[ConditionalAccessGrantControl.MFA],
|
||||
operator=GrantControlOperator.OR,
|
||||
authentication_strength=None,
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=False,
|
||||
frequency=None,
|
||||
type=None,
|
||||
interval=SignInFrequencyInterval.EVERY_TIME,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy requires MFA for Azure Management API."
|
||||
)
|
||||
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_no_mfa_grant_control(self):
|
||||
"""Test FAIL when the policy does not require MFA as a grant control."""
|
||||
policy_id = str(uuid4())
|
||||
display_name = "Azure Management No MFA"
|
||||
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_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
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=[AZURE_MANAGEMENT_API_APP_ID],
|
||||
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=[],
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[
|
||||
ConditionalAccessGrantControl.COMPLIANT_DEVICE
|
||||
],
|
||||
operator=GrantControlOperator.OR,
|
||||
authentication_strength=None,
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=False,
|
||||
frequency=None,
|
||||
type=None,
|
||||
interval=SignInFrequencyInterval.EVERY_TIME,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy requires MFA for Azure Management API."
|
||||
)
|
||||
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_does_not_target_all_users(self):
|
||||
"""Test FAIL when the policy does not target all users."""
|
||||
policy_id = str(uuid4())
|
||||
display_name = "Require MFA for Azure Management - Specific Users"
|
||||
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_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
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=[AZURE_MANAGEMENT_API_APP_ID],
|
||||
excluded_applications=[],
|
||||
included_user_actions=[],
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[],
|
||||
excluded_groups=[],
|
||||
included_users=[str(uuid4())],
|
||||
excluded_users=[],
|
||||
included_roles=[],
|
||||
excluded_roles=[],
|
||||
),
|
||||
client_app_types=[],
|
||||
user_risk_levels=[],
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[ConditionalAccessGrantControl.MFA],
|
||||
operator=GrantControlOperator.OR,
|
||||
authentication_strength=None,
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=False,
|
||||
frequency=None,
|
||||
type=None,
|
||||
interval=SignInFrequencyInterval.EVERY_TIME,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy requires MFA for Azure Management API."
|
||||
)
|
||||
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_with_all_apps_included(self):
|
||||
"""Test PASS when an enabled policy targets 'All' apps with MFA, covering Azure Management API."""
|
||||
policy_id = str(uuid4())
|
||||
display_name = "Require MFA for All Apps"
|
||||
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_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
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=[],
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[ConditionalAccessGrantControl.MFA],
|
||||
operator=GrantControlOperator.OR,
|
||||
authentication_strength=None,
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=False,
|
||||
frequency=None,
|
||||
type=None,
|
||||
interval=SignInFrequencyInterval.EVERY_TIME,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Conditional Access Policy {display_name} requires MFA for Azure Management API."
|
||||
)
|
||||
assert (
|
||||
result[0].resource
|
||||
== entra_client.conditional_access_policies[policy_id].dict()
|
||||
)
|
||||
assert result[0].resource_name == display_name
|
||||
assert result[0].resource_id == policy_id
|
||||
assert result[0].location == "global"
|
||||
|
||||
def test_policy_enabled_and_compliant(self):
|
||||
"""Test PASS when an enabled policy requires MFA for Azure Management API."""
|
||||
policy_id = str(uuid4())
|
||||
display_name = "Require MFA for Azure Management"
|
||||
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_require_mfa_for_management_api.entra_require_mfa_for_management_api import (
|
||||
entra_require_mfa_for_management_api,
|
||||
)
|
||||
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=[AZURE_MANAGEMENT_API_APP_ID],
|
||||
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=[],
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[ConditionalAccessGrantControl.MFA],
|
||||
operator=GrantControlOperator.OR,
|
||||
authentication_strength=None,
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=False,
|
||||
frequency=None,
|
||||
type=None,
|
||||
interval=SignInFrequencyInterval.EVERY_TIME,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_require_mfa_for_management_api()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Conditional Access Policy {display_name} requires MFA for Azure Management API."
|
||||
)
|
||||
assert (
|
||||
result[0].resource
|
||||
== entra_client.conditional_access_policies[policy_id].dict()
|
||||
)
|
||||
assert result[0].resource_name == display_name
|
||||
assert result[0].resource_id == policy_id
|
||||
assert result[0].location == "global"
|
||||
Reference in New Issue
Block a user