mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-03-29 19:39:43 +00:00
Compare commits
1 Commits
dependabot
...
feat/prowl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56d7c24dd4 |
@@ -6,6 +6,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
- `entra_sign_in_frequency_for_non_corporate_devices` check for m365 provider [(#10139)](https://github.com/prowler-cloud/prowler/pull/10139)
|
||||
- `entra_app_registration_no_unused_privileged_permissions` check for m365 provider [(#10080)](https://github.com/prowler-cloud/prowler/pull/10080)
|
||||
- `defenderidentity_health_issues_no_open` check for M365 provider [(#10087)](https://github.com/prowler-cloud/prowler/pull/10087)
|
||||
- `organization_verified_badge` check for GitHub provider [(#10033)](https://github.com/prowler-cloud/prowler/pull/10033)
|
||||
|
||||
@@ -1189,7 +1189,8 @@
|
||||
"Id": "5.2.2.4",
|
||||
"Description": "In complex deployments, organizations might have a need to restrict authentication sessions. Conditional Access policies allow for the targeting of specific user accounts. Some scenarios might include:- Resource access from an unmanaged or shared device- Access to sensitive information from an external network- High-privileged users- Business-critical applicationsEnsure Sign-in frequency periodic reauthentication does not exceed `4 hours` for E3 tenants, or `24 hours` for E5 tenants using Privileged Identity Management.Ensure `Persistent browser session` is set to `Never persistent`**Note:** This CA policy can be added to the previous CA policy in this benchmark \"Ensure multifactor authentication is enabled for all users in administrative roles\"",
|
||||
"Checks": [
|
||||
"entra_admin_users_sign_in_frequency_enabled"
|
||||
"entra_admin_users_sign_in_frequency_enabled",
|
||||
"entra_sign_in_frequency_for_non_corporate_devices"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
|
||||
@@ -1580,7 +1580,8 @@
|
||||
"Id": "5.2.2.11",
|
||||
"Description": "Sign-in frequency defines the time period before a user is asked to sign in again when attempting to access a resource. Ensure sign-in frequency for Intune Enrollment is set to 'Every time'.",
|
||||
"Checks": [
|
||||
"entra_admin_users_sign_in_frequency_enabled"
|
||||
"entra_admin_users_sign_in_frequency_enabled",
|
||||
"entra_sign_in_frequency_for_non_corporate_devices"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
|
||||
@@ -243,6 +243,7 @@
|
||||
"entra_legacy_authentication_blocked",
|
||||
"entra_managed_device_required_for_authentication",
|
||||
"entra_seamless_sso_disabled",
|
||||
"entra_sign_in_frequency_for_non_corporate_devices",
|
||||
"entra_users_mfa_enabled",
|
||||
"exchange_organization_modern_authentication_enabled",
|
||||
"exchange_transport_config_smtp_auth_disabled",
|
||||
@@ -616,6 +617,7 @@
|
||||
"entra_users_mfa_enabled",
|
||||
"entra_managed_device_required_for_mfa_registration",
|
||||
"entra_admin_users_phishing_resistant_mfa_enabled",
|
||||
"entra_sign_in_frequency_for_non_corporate_devices",
|
||||
"entra_users_mfa_capable"
|
||||
]
|
||||
},
|
||||
@@ -687,6 +689,7 @@
|
||||
"entra_admin_users_mfa_enabled",
|
||||
"entra_managed_device_required_for_authentication",
|
||||
"entra_seamless_sso_disabled",
|
||||
"entra_sign_in_frequency_for_non_corporate_devices",
|
||||
"entra_users_mfa_enabled",
|
||||
"entra_identity_protection_sign_in_risk_enabled"
|
||||
]
|
||||
|
||||
@@ -805,7 +805,8 @@
|
||||
"Id": "1.3.8",
|
||||
"Description": "Ensure Sign-in frequency is enabled and browser sessions are not persistent for Administrative users",
|
||||
"Checks": [
|
||||
"entra_admin_users_sign_in_frequency_enabled"
|
||||
"entra_admin_users_sign_in_frequency_enabled",
|
||||
"entra_sign_in_frequency_for_non_corporate_devices"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"Provider": "m365",
|
||||
"CheckID": "entra_sign_in_frequency_for_non_corporate_devices",
|
||||
"CheckTitle": "Conditional Access Policy enforces sign-in frequency for non-corporate devices",
|
||||
"CheckType": [],
|
||||
"ServiceName": "entra",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "Conditional Access Policy",
|
||||
"ResourceGroup": "IAM",
|
||||
"Description": "At least one Conditional Access Policy targets **all users** and **all cloud apps** with **sign-in frequency** controls enabled, reducing the session lifetime on unmanaged or non-corporate devices.",
|
||||
"Risk": "Without sign-in frequency controls, sessions on non-corporate or unmanaged devices can remain active indefinitely. Attackers who steal a session token from such a device can maintain access for an extended period, increasing the risk of **data exfiltration** and **lateral movement**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://learn.microsoft.com/en-us/entra/identity/conditional-access/howto-conditional-access-session-lifetime"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Navigate to the Microsoft Entra admin center (https://entra.microsoft.com/).\n2. Go to **Protection** > **Conditional Access** > **Policies**.\n3. Click **New policy**.\n4. Under **Users**, select **All users**.\n5. Under **Target resources**, select **All cloud apps**.\n6. Under **Session**, enable **Sign-in frequency** and set it to a value appropriate for your organization (e.g., 1 hour for unmanaged devices).\n7. Set the policy state to **On** and click **Create**.",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Create a Conditional Access Policy that enforces a short sign-in frequency for non-corporate devices. This limits the window of exposure from stolen tokens on unmanaged endpoints and aligns with **zero trust** principles.",
|
||||
"Url": "https://hub.prowler.com/check/entra_sign_in_frequency_for_non_corporate_devices"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"e3",
|
||||
"identity-access"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
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 (
|
||||
ConditionalAccessPolicyState,
|
||||
SignInFrequencyInterval,
|
||||
)
|
||||
|
||||
|
||||
class entra_sign_in_frequency_for_non_corporate_devices(Check):
|
||||
"""Check if at least one Conditional Access Policy enforces sign-in frequency for non-corporate devices."""
|
||||
|
||||
def execute(self) -> list[CheckReportM365]:
|
||||
"""Execute the check for sign-in frequency enforcement on non-corporate devices.
|
||||
|
||||
Returns:
|
||||
list[CheckReportM365]: A list containing the check result.
|
||||
"""
|
||||
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 enforces sign-in frequency for non-corporate devices."
|
||||
|
||||
for policy in entra_client.conditional_access_policies.values():
|
||||
if (
|
||||
policy.state == ConditionalAccessPolicyState.DISABLED
|
||||
or "All"
|
||||
not in policy.conditions.user_conditions.included_users
|
||||
or "All"
|
||||
not in policy.conditions.application_conditions.included_applications
|
||||
or not policy.session_controls.sign_in_frequency.is_enabled
|
||||
or policy.session_controls.sign_in_frequency.interval
|
||||
!= SignInFrequencyInterval.TIME_BASED
|
||||
):
|
||||
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 sign-in frequency for non-corporate devices but does not enforce it."
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Conditional Access Policy '{policy.display_name}' enforces sign-in frequency for non-corporate devices."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,562 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.m365.services.entra.entra_service import (
|
||||
ApplicationsConditions,
|
||||
ConditionalAccessPolicyState,
|
||||
Conditions,
|
||||
GrantControlOperator,
|
||||
GrantControls,
|
||||
PersistentBrowser,
|
||||
SessionControls,
|
||||
SignInFrequency,
|
||||
SignInFrequencyInterval,
|
||||
SignInFrequencyType,
|
||||
UsersConditions,
|
||||
)
|
||||
from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
|
||||
|
||||
|
||||
class Test_entra_sign_in_frequency_for_non_corporate_devices:
|
||||
def test_entra_no_conditional_access_policies(self):
|
||||
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(
|
||||
"prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices import (
|
||||
entra_sign_in_frequency_for_non_corporate_devices,
|
||||
)
|
||||
|
||||
entra_client.conditional_access_policies = {}
|
||||
|
||||
check = entra_sign_in_frequency_for_non_corporate_devices()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy enforces sign-in frequency for non-corporate devices."
|
||||
)
|
||||
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_entra_policy_disabled(self):
|
||||
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(
|
||||
"prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices import (
|
||||
entra_sign_in_frequency_for_non_corporate_devices,
|
||||
)
|
||||
from prowler.providers.m365.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
)
|
||||
|
||||
entra_client.conditional_access_policies = {
|
||||
id: ConditionalAccessPolicy(
|
||||
id=id,
|
||||
display_name="Test",
|
||||
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=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[], operator=GrantControlOperator.AND
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=True,
|
||||
frequency=1,
|
||||
type=SignInFrequencyType.HOURS,
|
||||
interval=SignInFrequencyInterval.TIME_BASED,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.DISABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_sign_in_frequency_for_non_corporate_devices()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy enforces sign-in frequency for non-corporate devices."
|
||||
)
|
||||
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_entra_policy_sign_in_frequency_disabled(self):
|
||||
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(
|
||||
"prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices import (
|
||||
entra_sign_in_frequency_for_non_corporate_devices,
|
||||
)
|
||||
from prowler.providers.m365.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
)
|
||||
|
||||
entra_client.conditional_access_policies = {
|
||||
id: ConditionalAccessPolicy(
|
||||
id=id,
|
||||
display_name="Test",
|
||||
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=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[], operator=GrantControlOperator.AND
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=False,
|
||||
frequency=None,
|
||||
type=None,
|
||||
interval=None,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_sign_in_frequency_for_non_corporate_devices()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy enforces sign-in frequency for non-corporate devices."
|
||||
)
|
||||
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_entra_policy_not_targeting_all_users(self):
|
||||
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(
|
||||
"prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices import (
|
||||
entra_sign_in_frequency_for_non_corporate_devices,
|
||||
)
|
||||
from prowler.providers.m365.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
)
|
||||
|
||||
entra_client.conditional_access_policies = {
|
||||
id: ConditionalAccessPolicy(
|
||||
id=id,
|
||||
display_name="Test",
|
||||
conditions=Conditions(
|
||||
application_conditions=ApplicationsConditions(
|
||||
included_applications=["All"],
|
||||
excluded_applications=[],
|
||||
included_user_actions=[],
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[],
|
||||
excluded_groups=[],
|
||||
included_users=["some-user-id"],
|
||||
excluded_users=[],
|
||||
included_roles=[],
|
||||
excluded_roles=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[], operator=GrantControlOperator.AND
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=True,
|
||||
frequency=1,
|
||||
type=SignInFrequencyType.HOURS,
|
||||
interval=SignInFrequencyInterval.TIME_BASED,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_sign_in_frequency_for_non_corporate_devices()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy enforces sign-in frequency for non-corporate devices."
|
||||
)
|
||||
assert result[0].resource == {}
|
||||
|
||||
def test_entra_policy_not_targeting_all_applications(self):
|
||||
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(
|
||||
"prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices import (
|
||||
entra_sign_in_frequency_for_non_corporate_devices,
|
||||
)
|
||||
from prowler.providers.m365.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
)
|
||||
|
||||
entra_client.conditional_access_policies = {
|
||||
id: ConditionalAccessPolicy(
|
||||
id=id,
|
||||
display_name="Test",
|
||||
conditions=Conditions(
|
||||
application_conditions=ApplicationsConditions(
|
||||
included_applications=["some-app-id"],
|
||||
excluded_applications=[],
|
||||
included_user_actions=[],
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[],
|
||||
excluded_groups=[],
|
||||
included_users=["All"],
|
||||
excluded_users=[],
|
||||
included_roles=[],
|
||||
excluded_roles=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[], operator=GrantControlOperator.AND
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=True,
|
||||
frequency=1,
|
||||
type=SignInFrequencyType.HOURS,
|
||||
interval=SignInFrequencyInterval.TIME_BASED,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_sign_in_frequency_for_non_corporate_devices()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy enforces sign-in frequency for non-corporate devices."
|
||||
)
|
||||
assert result[0].resource == {}
|
||||
|
||||
def test_entra_policy_every_time_interval_skipped(self):
|
||||
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(
|
||||
"prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices import (
|
||||
entra_sign_in_frequency_for_non_corporate_devices,
|
||||
)
|
||||
from prowler.providers.m365.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
)
|
||||
|
||||
entra_client.conditional_access_policies = {
|
||||
id: ConditionalAccessPolicy(
|
||||
id=id,
|
||||
display_name="Test",
|
||||
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=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[], operator=GrantControlOperator.AND
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=True,
|
||||
frequency=None,
|
||||
type=None,
|
||||
interval=SignInFrequencyInterval.EVERY_TIME,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_sign_in_frequency_for_non_corporate_devices()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy enforces sign-in frequency for non-corporate devices."
|
||||
)
|
||||
assert result[0].resource == {}
|
||||
|
||||
def test_entra_policy_enabled_for_reporting(self):
|
||||
id = str(uuid4())
|
||||
display_name = "Sign-in Frequency Policy"
|
||||
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(
|
||||
"prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices import (
|
||||
entra_sign_in_frequency_for_non_corporate_devices,
|
||||
)
|
||||
from prowler.providers.m365.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
)
|
||||
|
||||
entra_client.conditional_access_policies = {
|
||||
id: ConditionalAccessPolicy(
|
||||
id=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=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[], operator=GrantControlOperator.AND
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=True,
|
||||
frequency=1,
|
||||
type=SignInFrequencyType.HOURS,
|
||||
interval=SignInFrequencyInterval.TIME_BASED,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED_FOR_REPORTING,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_sign_in_frequency_for_non_corporate_devices()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Conditional Access Policy '{display_name}' reports sign-in frequency for non-corporate devices but does not enforce it."
|
||||
)
|
||||
assert (
|
||||
result[0].resource
|
||||
== entra_client.conditional_access_policies[id].dict()
|
||||
)
|
||||
assert result[0].resource_name == display_name
|
||||
assert result[0].resource_id == id
|
||||
assert result[0].location == "global"
|
||||
|
||||
def test_entra_policy_enabled_and_enforcing(self):
|
||||
id = str(uuid4())
|
||||
display_name = "Sign-in Frequency Policy"
|
||||
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(
|
||||
"prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_sign_in_frequency_for_non_corporate_devices.entra_sign_in_frequency_for_non_corporate_devices import (
|
||||
entra_sign_in_frequency_for_non_corporate_devices,
|
||||
)
|
||||
from prowler.providers.m365.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
)
|
||||
|
||||
entra_client.conditional_access_policies = {
|
||||
id: ConditionalAccessPolicy(
|
||||
id=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=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[], operator=GrantControlOperator.AND
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=False, mode="always"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=True,
|
||||
frequency=1,
|
||||
type=SignInFrequencyType.HOURS,
|
||||
interval=SignInFrequencyInterval.TIME_BASED,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_sign_in_frequency_for_non_corporate_devices()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Conditional Access Policy '{display_name}' enforces sign-in frequency for non-corporate devices."
|
||||
)
|
||||
assert (
|
||||
result[0].resource
|
||||
== entra_client.conditional_access_policies[id].dict()
|
||||
)
|
||||
assert result[0].resource_name == display_name
|
||||
assert result[0].resource_id == id
|
||||
assert result[0].location == "global"
|
||||
Reference in New Issue
Block a user