mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
feat(microsoft365): add new check entra_admin_users_sign_in_frequency_enabled (#7020)
Co-authored-by: MrCloudSec <hello@mistercloudsec.com>
This commit is contained in:
committed by
GitHub
parent
605613e220
commit
c88ae32b7f
@@ -475,3 +475,10 @@ kubernetes:
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
]
|
||||
|
||||
|
||||
# Microsoft365 Configuration
|
||||
microsoft365:
|
||||
# Conditional Access Policy
|
||||
# policy.session_controls.sign_in_frequency.frequency in hours
|
||||
sign_in_frequency: 4
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "microsoft365",
|
||||
"CheckID": "entra_admin_portals_role_limited_access",
|
||||
"CheckTitle": "Ensure that only administrative roles have access to Microsoft Admin Portals",
|
||||
"CheckType": [],
|
||||
"ServiceName": "entra",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Conditional Access Policy",
|
||||
"Description": "Ensure that only administrative roles have access to Microsoft Admin Portals to prevent unauthorized changes, privilege escalation, and security misconfigurations.",
|
||||
"Risk": "Allowing non-administrative users to access Microsoft Admin Portals increases the risk of unauthorized changes, privilege escalation, and potential security misconfigurations. Attackers could exploit these privileges to manipulate settings, disable security features, or access sensitive data.",
|
||||
"RelatedUrl": "https://learn.microsoft.com/en-us/microsoft-365/admin/add-users/about-admin-roles?view=o365-worldwide",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Navigate to the Microsoft Entra admin center https://entra.microsoft.com. 2. Click expand Protection > Conditional Access select Policies. 3. Click New Policy. Under Users include All Users. Under Users select Exclude and check Directory roles and select only administrative roles and a group of PIM eligible users. Under Target resources select Cloud apps and Select apps then select the Microsoft Admin Portals app. Confirm by clicking Select. Under Grant select Block access and click Select. 4. Under Enable policy set it to Report Only until the organization is ready to enable it. 5. Click Create.",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enforce Conditional Access policies to restrict Microsoft Admin Portals to predefined administrative roles. Ensure that only necessary users have access to these portals, applying the principle of least privilege and conducting periodic access reviews to maintain security compliance.",
|
||||
"Url": "https://learn.microsoft.com/en-us/entra/identity/conditional-access/overview"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
from prowler.lib.check.models import Check, CheckReportMicrosoft365
|
||||
from prowler.providers.microsoft365.services.entra.entra_client import entra_client
|
||||
from prowler.providers.microsoft365.services.entra.entra_service import (
|
||||
AdminRoles,
|
||||
ConditionalAccessGrantControl,
|
||||
ConditionalAccessPolicyState,
|
||||
)
|
||||
|
||||
|
||||
class entra_admin_portals_role_limited_access(Check):
|
||||
"""Check if Conditional Access policies deny access to the Microsoft 365 admin center for users with limited access roles.
|
||||
|
||||
This check ensures that Conditional Access policies are in place to deny access to the Microsoft 365 admin center for users with limited access roles.
|
||||
"""
|
||||
|
||||
def execute(self) -> list[CheckReportMicrosoft365]:
|
||||
"""Execute the check to ensure that Conditional Access policies deny access to the Microsoft 365 admin center for users with limited access roles.
|
||||
|
||||
Returns:
|
||||
list[CheckReportMicrosoft365]: A list containing the results of the check.
|
||||
"""
|
||||
findings = []
|
||||
|
||||
report = CheckReportMicrosoft365(
|
||||
metadata=self.metadata(),
|
||||
resource={},
|
||||
resource_name="Conditional Access Policies",
|
||||
resource_id="conditionalAccessPolicies",
|
||||
)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No Conditional Access Policy limits Entra Admin Center access to administrative roles."
|
||||
|
||||
for policy in entra_client.conditional_access_policies.values():
|
||||
if policy.state == ConditionalAccessPolicyState.DISABLED:
|
||||
continue
|
||||
|
||||
if not (
|
||||
{
|
||||
role for role in policy.conditions.user_conditions.excluded_roles
|
||||
}.issubset({admin_role.value for admin_role in AdminRoles})
|
||||
and "All" in policy.conditions.user_conditions.included_users
|
||||
):
|
||||
continue
|
||||
|
||||
if (
|
||||
"MicrosoftAdminPortals"
|
||||
not in policy.conditions.application_conditions.included_applications
|
||||
):
|
||||
continue
|
||||
|
||||
if (
|
||||
ConditionalAccessGrantControl.BLOCK
|
||||
in policy.grant_controls.built_in_controls
|
||||
):
|
||||
report = CheckReportMicrosoft365(
|
||||
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 Entra Admin Center access to administrative roles but does not limit it."
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Conditional Access Policy '{policy.display_name}' limits Entra Admin Center access to administrative roles."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "microsoft365",
|
||||
"CheckID": "entra_admin_users_sign_in_frequency_enabled",
|
||||
"CheckTitle": "Ensure Sign-in frequency periodic reauthentication is enabled and properly configured.",
|
||||
"CheckType": [],
|
||||
"ServiceName": "entra",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Conditional Access Policy",
|
||||
"Description": "Ensure Sign-in frequency periodic reauthentication is enabled and properly configured to reduce the risk of unauthorized access and session hijacking.",
|
||||
"Risk": "Allowing persistent browser sessions and long sign-in frequencies for administrative users increases the risk of unauthorized access. Attackers could exploit session persistence to maintain access to an admin account without reauthentication, increasing the likelihood of account compromise, especially in cases of credential theft or session hijacking.",
|
||||
"RelatedUrl": "https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-conditional-access-session#sign-in-frequency",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Navigate to Microsoft Entra admin center https://entra.microsoft.com/. 2. Click to expand Protection > Conditional Access Select Policies. 3. Click New policy. Under Users include, select users and groups and check Directory roles. At a minimum, include the directory roles listed below in this section of the document. Under Target resources, include All cloud apps and do not create any exclusions. Under Grant, select Grant Access and check Require multifactor authentication. Under Session, select Sign-in frequency, select Periodic reauthentication, and set it to 4 hours for E3 tenants. E5 tenants with PIM can be set to a maximum value of 24 hours. Check Persistent browser session, then select Never persistent in the drop-down menu. 4. Under Enable policy, set it to Report Only until the organization is ready to enable it.",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enforce a sign-in frequency limit of no more than 4 hours for E3 tenants (or 24 hours for E5 with Privileged Identity Management) and set browser sessions to Never persistent. This ensures that administrative users are regularly reauthenticated, reducing the risk of prolonged unauthorized access and mitigating session hijacking threats.",
|
||||
"Url": "https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-session-lifetime#user-sign-in-frequency"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
from prowler.lib.check.models import Check, CheckReportMicrosoft365
|
||||
from prowler.providers.microsoft365.services.entra.entra_client import entra_client
|
||||
from prowler.providers.microsoft365.services.entra.entra_service import (
|
||||
AdminRoles,
|
||||
ConditionalAccessPolicyState,
|
||||
SignInFrequencyInterval,
|
||||
SignInFrequencyType,
|
||||
)
|
||||
|
||||
|
||||
class entra_admin_users_sign_in_frequency_enabled(Check):
|
||||
"""Check if Conditional Access policies enforce sign-in frequency for admin users."""
|
||||
|
||||
def execute(self) -> list[CheckReportMicrosoft365]:
|
||||
"""Validate sign-in frequency enforcement for admin users."""
|
||||
findings = []
|
||||
report = CheckReportMicrosoft365(
|
||||
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 admin users."
|
||||
)
|
||||
recommended_frequency = entra_client.audit_config.get("sign_in_frequency", 4)
|
||||
|
||||
for policy in entra_client.conditional_access_policies.values():
|
||||
if (
|
||||
policy.state == ConditionalAccessPolicyState.DISABLED
|
||||
or not {role.value for role in AdminRoles}.issuperset(
|
||||
policy.conditions.user_conditions.included_roles
|
||||
)
|
||||
or "All"
|
||||
not in policy.conditions.application_conditions.included_applications
|
||||
or not policy.session_controls.sign_in_frequency.is_enabled
|
||||
or not policy.session_controls.persistent_browser.is_enabled
|
||||
or policy.session_controls.persistent_browser.mode != "never"
|
||||
):
|
||||
continue
|
||||
|
||||
report = CheckReportMicrosoft365(
|
||||
metadata=self.metadata(),
|
||||
resource=policy,
|
||||
resource_name=policy.display_name,
|
||||
resource_id=policy.id,
|
||||
)
|
||||
|
||||
if (
|
||||
policy.session_controls.sign_in_frequency.interval
|
||||
== SignInFrequencyInterval.EVERY_TIME
|
||||
):
|
||||
if policy.state == ConditionalAccessPolicyState.ENABLED_FOR_REPORTING:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Conditional Access Policy '{policy.display_name}' only reports when sign-in frequency is 'Every Time' for admin users but does not enforce it."
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Conditional Access Policy '{policy.display_name}' enforces sign-in frequency 'Every Time' for admin users."
|
||||
break
|
||||
elif (
|
||||
policy.session_controls.sign_in_frequency.interval
|
||||
== SignInFrequencyInterval.TIME_BASED
|
||||
):
|
||||
frequency_hours = (
|
||||
policy.session_controls.sign_in_frequency.frequency
|
||||
if policy.session_controls.sign_in_frequency.type
|
||||
== SignInFrequencyType.HOURS
|
||||
else policy.session_controls.sign_in_frequency.frequency * 24
|
||||
)
|
||||
if frequency_hours > recommended_frequency:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Conditional Access Policy '{policy.display_name}' enforces sign-in frequency at {frequency_hours} hours for admin users, exceeding the recommended {recommended_frequency} hours."
|
||||
else:
|
||||
if (
|
||||
policy.state
|
||||
== ConditionalAccessPolicyState.ENABLED_FOR_REPORTING
|
||||
):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Conditional Access Policy '{policy.display_name}' only reports when sign-in frequency is {frequency_hours} hours for admin users but does not enforce it."
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Conditional Access Policy '{policy.display_name}' enforces sign-in frequency at {frequency_hours} hours for admin users."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -1,4 +1,5 @@
|
||||
from asyncio import gather, get_event_loop
|
||||
from enum import Enum
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
@@ -17,18 +18,19 @@ class Entra(Microsoft365Service):
|
||||
attributes = loop.run_until_complete(
|
||||
gather(
|
||||
self._get_authorization_policy(),
|
||||
self._get_groups(),
|
||||
self._get_conditional_access_policies(),
|
||||
self._get_admin_consent_policy(),
|
||||
self._get_groups(),
|
||||
)
|
||||
)
|
||||
|
||||
self.authorization_policy = attributes[0]
|
||||
self.groups = attributes[1]
|
||||
self.conditional_access_policies = attributes[1]
|
||||
self.admin_consent_policy = attributes[2]
|
||||
self.groups = attributes[3]
|
||||
|
||||
async def _get_authorization_policy(self):
|
||||
logger.info("Entra - Getting authorization policy...")
|
||||
|
||||
authorization_policy = None
|
||||
try:
|
||||
auth_policy = await self.client.policies.authorization_policy.get()
|
||||
@@ -84,9 +86,175 @@ class Entra(Microsoft365Service):
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
return authorization_policy
|
||||
|
||||
async def _get_conditional_access_policies(self):
|
||||
logger.info("Entra - Getting conditional access policies...")
|
||||
conditional_access_policies = {}
|
||||
try:
|
||||
conditional_access_policies_list = (
|
||||
await self.client.identity.conditional_access.policies.get()
|
||||
)
|
||||
for policy in conditional_access_policies_list.value:
|
||||
conditional_access_policies[policy.id] = ConditionalAccessPolicy(
|
||||
id=policy.id,
|
||||
display_name=policy.display_name,
|
||||
conditions=Conditions(
|
||||
application_conditions=ApplicationsConditions(
|
||||
included_applications=[
|
||||
application
|
||||
for application in getattr(
|
||||
policy.conditions.applications,
|
||||
"include_applications",
|
||||
[],
|
||||
)
|
||||
],
|
||||
excluded_applications=[
|
||||
application
|
||||
for application in getattr(
|
||||
policy.conditions.applications,
|
||||
"exclude_applications",
|
||||
[],
|
||||
)
|
||||
],
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[
|
||||
group
|
||||
for group in getattr(
|
||||
policy.conditions.users,
|
||||
"include_groups",
|
||||
[],
|
||||
)
|
||||
],
|
||||
excluded_groups=[
|
||||
group
|
||||
for group in getattr(
|
||||
policy.conditions.users,
|
||||
"exclude_groups",
|
||||
[],
|
||||
)
|
||||
],
|
||||
included_users=[
|
||||
user
|
||||
for user in getattr(
|
||||
policy.conditions.users,
|
||||
"include_users",
|
||||
[],
|
||||
)
|
||||
],
|
||||
excluded_users=[
|
||||
user
|
||||
for user in getattr(
|
||||
policy.conditions.users,
|
||||
"exclude_users",
|
||||
[],
|
||||
)
|
||||
],
|
||||
included_roles=[
|
||||
role
|
||||
for role in getattr(
|
||||
policy.conditions.users,
|
||||
"include_roles",
|
||||
[],
|
||||
)
|
||||
],
|
||||
excluded_roles=[
|
||||
role
|
||||
for role in getattr(
|
||||
policy.conditions.users,
|
||||
"exclude_roles",
|
||||
[],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=(
|
||||
[
|
||||
ConditionalAccessGrantControl(control.value)
|
||||
for control in getattr(
|
||||
policy.grant_controls, "built_in_controls", {}
|
||||
)
|
||||
]
|
||||
if policy.grant_controls
|
||||
else []
|
||||
)
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=(
|
||||
policy.session_controls.persistent_browser.is_enabled
|
||||
if policy.session_controls
|
||||
and policy.session_controls.persistent_browser
|
||||
else False
|
||||
),
|
||||
mode=(
|
||||
policy.session_controls.persistent_browser.mode
|
||||
if policy.session_controls
|
||||
and policy.session_controls.persistent_browser
|
||||
else "always"
|
||||
),
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=(
|
||||
policy.session_controls.sign_in_frequency.is_enabled
|
||||
if policy.session_controls
|
||||
and policy.session_controls.sign_in_frequency
|
||||
else False
|
||||
),
|
||||
frequency=(
|
||||
policy.session_controls.sign_in_frequency.value
|
||||
if policy.session_controls
|
||||
and policy.session_controls.sign_in_frequency
|
||||
else None
|
||||
),
|
||||
type=(
|
||||
SignInFrequencyType(
|
||||
policy.session_controls.sign_in_frequency.type
|
||||
)
|
||||
if policy.session_controls
|
||||
and policy.session_controls.sign_in_frequency
|
||||
and policy.session_controls.sign_in_frequency.type
|
||||
else None
|
||||
),
|
||||
interval=(
|
||||
SignInFrequencyInterval(
|
||||
policy.session_controls.sign_in_frequency.frequency_interval
|
||||
)
|
||||
if policy.session_controls
|
||||
and policy.session_controls.sign_in_frequency
|
||||
else None
|
||||
),
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState(
|
||||
getattr(policy, "state", "disabled")
|
||||
),
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return conditional_access_policies
|
||||
|
||||
async def _get_admin_consent_policy(self):
|
||||
logger.info("Entra - Getting group settings...")
|
||||
admin_consent_policy = None
|
||||
try:
|
||||
policy = await self.client.policies.admin_consent_request_policy.get()
|
||||
admin_consent_policy = AdminConsentPolicy(
|
||||
admin_consent_enabled=policy.is_enabled,
|
||||
notify_reviewers=policy.notify_reviewers,
|
||||
email_reminders_to_reviewers=policy.reminders_enabled,
|
||||
duration_in_days=policy.request_duration_in_days,
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return admin_consent_policy
|
||||
|
||||
async def _get_groups(self):
|
||||
logger.info("Entra - Getting groups...")
|
||||
groups = []
|
||||
@@ -107,23 +275,75 @@ class Entra(Microsoft365Service):
|
||||
)
|
||||
return groups
|
||||
|
||||
async def _get_admin_consent_policy(self):
|
||||
logger.info("Entra - Getting group settings...")
|
||||
admin_consent_policy = None
|
||||
try:
|
||||
policy = await self.client.policies.admin_consent_request_policy.get()
|
||||
admin_consent_policy = AdminConsentPolicy(
|
||||
admin_consent_enabled=policy.is_enabled,
|
||||
notify_reviewers=policy.notify_reviewers,
|
||||
email_reminders_to_reviewers=policy.reminders_enabled,
|
||||
duration_in_days=policy.request_duration_in_days,
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
return admin_consent_policy
|
||||
class ConditionalAccessPolicyState(Enum):
|
||||
ENABLED = "enabled"
|
||||
DISABLED = "disabled"
|
||||
ENABLED_FOR_REPORTING = "enabledForReportingButNotEnforced"
|
||||
|
||||
|
||||
class ApplicationsConditions(BaseModel):
|
||||
included_applications: List[str]
|
||||
excluded_applications: List[str]
|
||||
|
||||
|
||||
class UsersConditions(BaseModel):
|
||||
included_groups: List[str]
|
||||
excluded_groups: List[str]
|
||||
included_users: List[str]
|
||||
excluded_users: List[str]
|
||||
included_roles: List[str]
|
||||
excluded_roles: List[str]
|
||||
|
||||
|
||||
class Conditions(BaseModel):
|
||||
application_conditions: Optional[ApplicationsConditions]
|
||||
user_conditions: Optional[UsersConditions]
|
||||
|
||||
|
||||
class PersistentBrowser(BaseModel):
|
||||
is_enabled: bool
|
||||
mode: str
|
||||
|
||||
|
||||
class SignInFrequencyInterval(Enum):
|
||||
TIME_BASED = "timeBased"
|
||||
EVERY_TIME = "everyTime"
|
||||
|
||||
|
||||
class SignInFrequencyType(Enum):
|
||||
HOURS = "hours"
|
||||
DAYS = "days"
|
||||
|
||||
|
||||
class SignInFrequency(BaseModel):
|
||||
is_enabled: bool
|
||||
frequency: Optional[int]
|
||||
type: Optional[SignInFrequencyType]
|
||||
interval: Optional[SignInFrequencyInterval]
|
||||
|
||||
|
||||
class SessionControls(BaseModel):
|
||||
persistent_browser: PersistentBrowser
|
||||
sign_in_frequency: SignInFrequency
|
||||
|
||||
|
||||
class ConditionalAccessGrantControl(Enum):
|
||||
MFA = "mfa"
|
||||
BLOCK = "block"
|
||||
|
||||
|
||||
class GrantControls(BaseModel):
|
||||
built_in_controls: List[ConditionalAccessGrantControl]
|
||||
|
||||
|
||||
class ConditionalAccessPolicy(BaseModel):
|
||||
id: str
|
||||
display_name: str
|
||||
conditions: Conditions
|
||||
session_controls: SessionControls
|
||||
grant_controls: GrantControls
|
||||
state: ConditionalAccessPolicyState
|
||||
|
||||
|
||||
class DefaultUserRolePermissions(BaseModel):
|
||||
@@ -155,3 +375,21 @@ class AdminConsentPolicy(BaseModel):
|
||||
notify_reviewers: bool
|
||||
email_reminders_to_reviewers: bool
|
||||
duration_in_days: int
|
||||
|
||||
|
||||
class AdminRoles(Enum):
|
||||
APPLICATION_ADMINISTRATOR = "9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3"
|
||||
AUTHENTICATION_ADMINISTRATOR = "c4e39bd9-1100-46d3-8c65-fb160da0071f"
|
||||
BILLING_ADMINISTRATOR = "b0f54661-2d74-4c50-afa3-1ec803f12efe"
|
||||
CLOUD_APPLICATION_ADMINISTRATOR = "158c047a-c907-4556-b7ef-446551a6b5f7"
|
||||
CONDITIONAL_ACCESS_ADMINISTRATOR = "b1be1c3e-b65d-4f19-8427-f6fa0d97feb9"
|
||||
EXCHANGE_ADMINISTRATOR = "29232cdf-9323-42fd-ade2-1d097af3e4de"
|
||||
GLOBAL_ADMINISTRATOR = "62e90394-69f5-4237-9190-012177145e10"
|
||||
GLOBAL_READER = "f2ef992c-3afb-46b9-b7cf-a126ee74c451"
|
||||
HELPDESK_ADMINISTRATOR = "729827e3-9c14-49f7-bb1b-9608f156bbb8"
|
||||
PASSWORD_ADMINISTRATOR = "966707d0-3269-4727-9be2-8c3a10f19b9d"
|
||||
PRIVILEGED_AUTHENTICATION_ADMINISTRATOR = "7be44c8a-adaf-4e2a-84d6-ab2649e08a13"
|
||||
PRIVILEGED_ROLE_ADMINISTRATOR = "e8611ab8-c189-46e8-94e1-60213ab1f814"
|
||||
SECURITY_ADMINISTRATOR = "194ae4cb-b126-40b2-bd5b-6091b380977d"
|
||||
SHAREPOINT_ADMINISTRATOR = "f28a1f50-f6e7-4571-818b-6a12f2af6b6c"
|
||||
USER_ADMINISTRATOR = "fe930be7-5e62-47db-91af-98c3a49a38b1"
|
||||
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.microsoft365.services.entra.entra_service import (
|
||||
ApplicationsConditions,
|
||||
ConditionalAccessGrantControl,
|
||||
ConditionalAccessPolicyState,
|
||||
Conditions,
|
||||
GrantControls,
|
||||
PersistentBrowser,
|
||||
SessionControls,
|
||||
SignInFrequency,
|
||||
SignInFrequencyInterval,
|
||||
UsersConditions,
|
||||
)
|
||||
from tests.providers.microsoft365.microsoft365_fixtures import (
|
||||
DOMAIN,
|
||||
set_mocked_microsoft365_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_entra_admin_portals_role_limited_access:
|
||||
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_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access import (
|
||||
entra_admin_portals_role_limited_access,
|
||||
)
|
||||
|
||||
entra_client.conditional_access_policies = {}
|
||||
|
||||
check = entra_admin_portals_role_limited_access()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy limits Entra Admin Center access to administrative roles."
|
||||
)
|
||||
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_admin_center_limited_access_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_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access import (
|
||||
entra_admin_portals_role_limited_access,
|
||||
)
|
||||
from prowler.providers.microsoft365.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=[], excluded_applications=[]
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[],
|
||||
excluded_groups=[],
|
||||
included_users=[],
|
||||
excluded_users=[],
|
||||
included_roles=[],
|
||||
excluded_roles=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(built_in_controls=[]),
|
||||
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_admin_portals_role_limited_access()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "No Conditional Access Policy limits Entra Admin Center access to administrative roles."
|
||||
)
|
||||
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_admin_center_limited_access_enabled_for_reporting(self):
|
||||
id = str(uuid4())
|
||||
display_name = "Test"
|
||||
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_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access import (
|
||||
entra_admin_portals_role_limited_access,
|
||||
)
|
||||
from prowler.providers.microsoft365.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=["MicrosoftAdminPortals"],
|
||||
excluded_applications=[],
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[],
|
||||
excluded_groups=[],
|
||||
included_users=["All"],
|
||||
excluded_users=[],
|
||||
included_roles=[],
|
||||
excluded_roles=["9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3"],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[ConditionalAccessGrantControl.BLOCK]
|
||||
),
|
||||
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_admin_portals_role_limited_access()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Conditional Access Policy '{display_name}' reports Entra Admin Center access to administrative roles but does not limit 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_admin_center_limited_access_enabled(self):
|
||||
id = str(uuid4())
|
||||
display_name = "Test"
|
||||
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_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access import (
|
||||
entra_admin_portals_role_limited_access,
|
||||
)
|
||||
from prowler.providers.microsoft365.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=["MicrosoftAdminPortals"],
|
||||
excluded_applications=[],
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[],
|
||||
excluded_groups=[],
|
||||
included_users=["All"],
|
||||
excluded_users=[],
|
||||
included_roles=[],
|
||||
excluded_roles=["9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3"],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[ConditionalAccessGrantControl.BLOCK]
|
||||
),
|
||||
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_admin_portals_role_limited_access()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Conditional Access Policy '{display_name}' limits Entra Admin Center access to administrative roles."
|
||||
)
|
||||
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"
|
||||
+586
@@ -0,0 +1,586 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.microsoft365.services.entra.entra_service import (
|
||||
ApplicationsConditions,
|
||||
ConditionalAccessPolicyState,
|
||||
Conditions,
|
||||
GrantControls,
|
||||
PersistentBrowser,
|
||||
SessionControls,
|
||||
SignInFrequency,
|
||||
SignInFrequencyInterval,
|
||||
SignInFrequencyType,
|
||||
UsersConditions,
|
||||
)
|
||||
from tests.providers.microsoft365.microsoft365_fixtures import (
|
||||
DOMAIN,
|
||||
set_mocked_microsoft365_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_entra_admin_users_sign_in_frequency_enabled:
|
||||
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_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled import (
|
||||
entra_admin_users_sign_in_frequency_enabled,
|
||||
)
|
||||
|
||||
entra_client.conditional_access_policies = {}
|
||||
entra_client.audit_config = {"sign_in_frequency": 4}
|
||||
|
||||
check = entra_admin_users_sign_in_frequency_enabled()
|
||||
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 admin users."
|
||||
)
|
||||
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_sign_in_frequency_disabled(self):
|
||||
id = str(uuid4())
|
||||
entra_client = mock.MagicMock
|
||||
entra_client.audited_tenant = "audited_tenant"
|
||||
entra_client.audited_domain = DOMAIN
|
||||
entra_client.audit_config = {"sign_in_frequency": 4}
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled import (
|
||||
entra_admin_users_sign_in_frequency_enabled,
|
||||
)
|
||||
from prowler.providers.microsoft365.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=[], excluded_applications=[]
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[],
|
||||
excluded_groups=[],
|
||||
included_users=[],
|
||||
excluded_users=[],
|
||||
included_roles=[],
|
||||
excluded_roles=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(built_in_controls=[]),
|
||||
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_admin_users_sign_in_frequency_enabled()
|
||||
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 admin users."
|
||||
)
|
||||
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_sign_in_frequency_enabled_every_time(self):
|
||||
id = str(uuid4())
|
||||
freq = None
|
||||
display_name = "Test"
|
||||
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_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled import (
|
||||
entra_admin_users_sign_in_frequency_enabled,
|
||||
)
|
||||
from prowler.providers.microsoft365.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
)
|
||||
|
||||
entra_client.audit_config = {"sign_in_frequency": 4}
|
||||
entra_client.conditional_access_policies = {
|
||||
id: ConditionalAccessPolicy(
|
||||
id=id,
|
||||
display_name=display_name,
|
||||
conditions=Conditions(
|
||||
application_conditions=ApplicationsConditions(
|
||||
included_applications=["All"], excluded_applications=[]
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[],
|
||||
excluded_groups=[],
|
||||
included_users=[],
|
||||
excluded_users=[],
|
||||
included_roles=[
|
||||
"9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3",
|
||||
"c4e39bd9-1100-46d3-8c65-fb160da0071f",
|
||||
"b0f54661-2d74-4c50-afa3-1ec803f12efe",
|
||||
"158c047a-c907-4556-b7ef-446551a6b5f7",
|
||||
"b1be1c3e-b65d-4f19-8427-f6fa0d97feb9",
|
||||
"29232cdf-9323-42fd-ade2-1d097af3e4de",
|
||||
"62e90394-69f5-4237-9190-012177145e10",
|
||||
"f2ef992c-3afb-46b9-b7cf-a126ee74c451",
|
||||
"729827e3-9c14-49f7-bb1b-9608f156bbb8",
|
||||
"966707d0-3269-4727-9be2-8c3a10f19b9d",
|
||||
"7be44c8a-adaf-4e2a-84d6-ab2649e08a13",
|
||||
"e8611ab8-c189-46e8-94e1-60213ab1f814",
|
||||
"194ae4cb-b126-40b2-bd5b-6091b380977d",
|
||||
"f28a1f50-f6e7-4571-818b-6a12f2af6b6c",
|
||||
"fe930be7-5e62-47db-91af-98c3a49a38b1",
|
||||
],
|
||||
excluded_roles=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(built_in_controls=[]),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=True, mode="never"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=True,
|
||||
frequency=freq,
|
||||
type=None,
|
||||
interval=SignInFrequencyInterval.EVERY_TIME,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_admin_users_sign_in_frequency_enabled()
|
||||
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 'Every Time' for admin users."
|
||||
)
|
||||
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_sign_in_frequency_enabled_bad_frequency(self):
|
||||
id = str(uuid4())
|
||||
freq = 3600
|
||||
recommended_sign_in_frequency = 4
|
||||
display_name = "Test"
|
||||
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_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled import (
|
||||
entra_admin_users_sign_in_frequency_enabled,
|
||||
)
|
||||
from prowler.providers.microsoft365.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
)
|
||||
|
||||
entra_client.audit_config = {
|
||||
"sign_in_frequency": recommended_sign_in_frequency
|
||||
}
|
||||
entra_client.conditional_access_policies = {
|
||||
id: ConditionalAccessPolicy(
|
||||
id=id,
|
||||
display_name=display_name,
|
||||
conditions=Conditions(
|
||||
application_conditions=ApplicationsConditions(
|
||||
included_applications=["All"], excluded_applications=[]
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[],
|
||||
excluded_groups=[],
|
||||
included_users=[],
|
||||
excluded_users=[],
|
||||
included_roles=[
|
||||
"9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3",
|
||||
"c4e39bd9-1100-46d3-8c65-fb160da0071f",
|
||||
"b0f54661-2d74-4c50-afa3-1ec803f12efe",
|
||||
"158c047a-c907-4556-b7ef-446551a6b5f7",
|
||||
"b1be1c3e-b65d-4f19-8427-f6fa0d97feb9",
|
||||
"29232cdf-9323-42fd-ade2-1d097af3e4de",
|
||||
"62e90394-69f5-4237-9190-012177145e10",
|
||||
"f2ef992c-3afb-46b9-b7cf-a126ee74c451",
|
||||
"729827e3-9c14-49f7-bb1b-9608f156bbb8",
|
||||
"966707d0-3269-4727-9be2-8c3a10f19b9d",
|
||||
"7be44c8a-adaf-4e2a-84d6-ab2649e08a13",
|
||||
"e8611ab8-c189-46e8-94e1-60213ab1f814",
|
||||
"194ae4cb-b126-40b2-bd5b-6091b380977d",
|
||||
"f28a1f50-f6e7-4571-818b-6a12f2af6b6c",
|
||||
"fe930be7-5e62-47db-91af-98c3a49a38b1",
|
||||
],
|
||||
excluded_roles=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(built_in_controls=[]),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=True, mode="never"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=True,
|
||||
frequency=freq,
|
||||
type=SignInFrequencyType.HOURS,
|
||||
interval=SignInFrequencyInterval.TIME_BASED,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_admin_users_sign_in_frequency_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Conditional Access Policy '{display_name}' enforces sign-in frequency at {freq} hours for admin users, exceeding the recommended {recommended_sign_in_frequency} hours."
|
||||
)
|
||||
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_sign_in_frequency_enabled_for_reporting(self):
|
||||
id = str(uuid4())
|
||||
freq = 4
|
||||
display_name = "Test"
|
||||
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_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled import (
|
||||
entra_admin_users_sign_in_frequency_enabled,
|
||||
)
|
||||
from prowler.providers.microsoft365.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
)
|
||||
|
||||
entra_client.audit_config = {"sign_in_frequency": freq}
|
||||
entra_client.conditional_access_policies = {
|
||||
id: ConditionalAccessPolicy(
|
||||
id=id,
|
||||
display_name=display_name,
|
||||
conditions=Conditions(
|
||||
application_conditions=ApplicationsConditions(
|
||||
included_applications=["All"], excluded_applications=[]
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[],
|
||||
excluded_groups=[],
|
||||
included_users=[],
|
||||
excluded_users=[],
|
||||
included_roles=[
|
||||
"9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3",
|
||||
"c4e39bd9-1100-46d3-8c65-fb160da0071f",
|
||||
"b0f54661-2d74-4c50-afa3-1ec803f12efe",
|
||||
"158c047a-c907-4556-b7ef-446551a6b5f7",
|
||||
"b1be1c3e-b65d-4f19-8427-f6fa0d97feb9",
|
||||
"29232cdf-9323-42fd-ade2-1d097af3e4de",
|
||||
"62e90394-69f5-4237-9190-012177145e10",
|
||||
"f2ef992c-3afb-46b9-b7cf-a126ee74c451",
|
||||
"729827e3-9c14-49f7-bb1b-9608f156bbb8",
|
||||
"966707d0-3269-4727-9be2-8c3a10f19b9d",
|
||||
"7be44c8a-adaf-4e2a-84d6-ab2649e08a13",
|
||||
"e8611ab8-c189-46e8-94e1-60213ab1f814",
|
||||
"194ae4cb-b126-40b2-bd5b-6091b380977d",
|
||||
"f28a1f50-f6e7-4571-818b-6a12f2af6b6c",
|
||||
"fe930be7-5e62-47db-91af-98c3a49a38b1",
|
||||
],
|
||||
excluded_roles=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(built_in_controls=[]),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=True, mode="never"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=True,
|
||||
frequency=freq,
|
||||
type=SignInFrequencyType.HOURS,
|
||||
interval=SignInFrequencyInterval.TIME_BASED,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED_FOR_REPORTING,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_admin_users_sign_in_frequency_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Conditional Access Policy '{display_name}' only reports when sign-in frequency is {freq} hours for admin users 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_sign_in_frequency_enabled(self):
|
||||
id = str(uuid4())
|
||||
freq = 4
|
||||
display_name = "Test"
|
||||
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_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled import (
|
||||
entra_admin_users_sign_in_frequency_enabled,
|
||||
)
|
||||
from prowler.providers.microsoft365.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
)
|
||||
|
||||
entra_client.audit_config = {"sign_in_frequency": freq}
|
||||
entra_client.conditional_access_policies = {
|
||||
id: ConditionalAccessPolicy(
|
||||
id=id,
|
||||
display_name=display_name,
|
||||
conditions=Conditions(
|
||||
application_conditions=ApplicationsConditions(
|
||||
included_applications=["All"], excluded_applications=[]
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[],
|
||||
excluded_groups=[],
|
||||
included_users=[],
|
||||
excluded_users=[],
|
||||
included_roles=[
|
||||
"9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3",
|
||||
"c4e39bd9-1100-46d3-8c65-fb160da0071f",
|
||||
"b0f54661-2d74-4c50-afa3-1ec803f12efe",
|
||||
"158c047a-c907-4556-b7ef-446551a6b5f7",
|
||||
"b1be1c3e-b65d-4f19-8427-f6fa0d97feb9",
|
||||
"29232cdf-9323-42fd-ade2-1d097af3e4de",
|
||||
"62e90394-69f5-4237-9190-012177145e10",
|
||||
"f2ef992c-3afb-46b9-b7cf-a126ee74c451",
|
||||
"729827e3-9c14-49f7-bb1b-9608f156bbb8",
|
||||
"966707d0-3269-4727-9be2-8c3a10f19b9d",
|
||||
"7be44c8a-adaf-4e2a-84d6-ab2649e08a13",
|
||||
"e8611ab8-c189-46e8-94e1-60213ab1f814",
|
||||
"194ae4cb-b126-40b2-bd5b-6091b380977d",
|
||||
"f28a1f50-f6e7-4571-818b-6a12f2af6b6c",
|
||||
"fe930be7-5e62-47db-91af-98c3a49a38b1",
|
||||
],
|
||||
excluded_roles=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(built_in_controls=[]),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=True, mode="never"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=True,
|
||||
frequency=freq,
|
||||
type=SignInFrequencyType.HOURS,
|
||||
interval=SignInFrequencyInterval.TIME_BASED,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_admin_users_sign_in_frequency_enabled()
|
||||
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 at {freq} hours for admin users."
|
||||
)
|
||||
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_sign_in_frequency_enabled_in_days(self):
|
||||
id = str(uuid4())
|
||||
freq = 1
|
||||
recommended_sign_in_frequency = 24
|
||||
display_name = "Test"
|
||||
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_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.entra.entra_admin_users_sign_in_frequency_enabled.entra_admin_users_sign_in_frequency_enabled import (
|
||||
entra_admin_users_sign_in_frequency_enabled,
|
||||
)
|
||||
from prowler.providers.microsoft365.services.entra.entra_service import (
|
||||
ConditionalAccessPolicy,
|
||||
)
|
||||
|
||||
entra_client.audit_config = {
|
||||
"sign_in_frequency": recommended_sign_in_frequency
|
||||
}
|
||||
entra_client.conditional_access_policies = {
|
||||
id: ConditionalAccessPolicy(
|
||||
id=id,
|
||||
display_name=display_name,
|
||||
conditions=Conditions(
|
||||
application_conditions=ApplicationsConditions(
|
||||
included_applications=["All"], excluded_applications=[]
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=[],
|
||||
excluded_groups=[],
|
||||
included_users=[],
|
||||
excluded_users=[],
|
||||
included_roles=[
|
||||
"9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3",
|
||||
"c4e39bd9-1100-46d3-8c65-fb160da0071f",
|
||||
"b0f54661-2d74-4c50-afa3-1ec803f12efe",
|
||||
"158c047a-c907-4556-b7ef-446551a6b5f7",
|
||||
"b1be1c3e-b65d-4f19-8427-f6fa0d97feb9",
|
||||
"29232cdf-9323-42fd-ade2-1d097af3e4de",
|
||||
"62e90394-69f5-4237-9190-012177145e10",
|
||||
"f2ef992c-3afb-46b9-b7cf-a126ee74c451",
|
||||
"729827e3-9c14-49f7-bb1b-9608f156bbb8",
|
||||
"966707d0-3269-4727-9be2-8c3a10f19b9d",
|
||||
"7be44c8a-adaf-4e2a-84d6-ab2649e08a13",
|
||||
"e8611ab8-c189-46e8-94e1-60213ab1f814",
|
||||
"194ae4cb-b126-40b2-bd5b-6091b380977d",
|
||||
"f28a1f50-f6e7-4571-818b-6a12f2af6b6c",
|
||||
"fe930be7-5e62-47db-91af-98c3a49a38b1",
|
||||
],
|
||||
excluded_roles=[],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(built_in_controls=[]),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=True, mode="never"
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=True,
|
||||
frequency=freq,
|
||||
type=SignInFrequencyType.DAYS,
|
||||
interval=SignInFrequencyInterval.TIME_BASED,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED,
|
||||
)
|
||||
}
|
||||
|
||||
check = entra_admin_users_sign_in_frequency_enabled()
|
||||
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 at {recommended_sign_in_frequency} hours for admin users."
|
||||
)
|
||||
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"
|
||||
@@ -3,9 +3,21 @@ from unittest.mock import patch
|
||||
from prowler.providers.microsoft365.models import Microsoft365IdentityInfo
|
||||
from prowler.providers.microsoft365.services.entra.entra_service import (
|
||||
AdminConsentPolicy,
|
||||
ApplicationsConditions,
|
||||
AuthorizationPolicy,
|
||||
ConditionalAccessGrantControl,
|
||||
ConditionalAccessPolicy,
|
||||
ConditionalAccessPolicyState,
|
||||
Conditions,
|
||||
DefaultUserRolePermissions,
|
||||
Entra,
|
||||
GrantControls,
|
||||
PersistentBrowser,
|
||||
SessionControls,
|
||||
SignInFrequency,
|
||||
SignInFrequencyInterval,
|
||||
SignInFrequencyType,
|
||||
UsersConditions,
|
||||
)
|
||||
from tests.providers.microsoft365.microsoft365_fixtures import (
|
||||
DOMAIN,
|
||||
@@ -28,6 +40,45 @@ async def mock_entra_get_authorization_policy(_):
|
||||
)
|
||||
|
||||
|
||||
async def mock_entra_get_conditional_access_policies(_):
|
||||
return {
|
||||
"id-1": ConditionalAccessPolicy(
|
||||
id="id-1",
|
||||
display_name="Name 1",
|
||||
conditions=Conditions(
|
||||
application_conditions=ApplicationsConditions(
|
||||
included_applications=["app-1", "app-2"],
|
||||
excluded_applications=["app-3", "app-4"],
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=["group-1", "group-2"],
|
||||
excluded_groups=["group-3", "group-4"],
|
||||
included_users=["user-1", "user-2"],
|
||||
excluded_users=["user-3", "user-4"],
|
||||
included_roles=["role-1", "role-2"],
|
||||
excluded_roles=["role-3", "role-4"],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[ConditionalAccessGrantControl.BLOCK]
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=True,
|
||||
mode="always",
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=True,
|
||||
frequency=24,
|
||||
type=SignInFrequencyType.HOURS,
|
||||
interval=SignInFrequencyInterval.TIME_BASED,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED_FOR_REPORTING,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
async def mock_entra_get_groups(_):
|
||||
group1 = {
|
||||
"id": "id-1",
|
||||
@@ -82,6 +133,49 @@ class Test_Entra_Service:
|
||||
)
|
||||
)
|
||||
|
||||
@patch(
|
||||
"prowler.providers.microsoft365.services.entra.entra_service.Entra._get_conditional_access_policies",
|
||||
new=mock_entra_get_conditional_access_policies,
|
||||
)
|
||||
def test_get_conditional_access_policies(self):
|
||||
entra_client = Entra(set_mocked_microsoft365_provider())
|
||||
assert entra_client.conditional_access_policies == {
|
||||
"id-1": ConditionalAccessPolicy(
|
||||
id="id-1",
|
||||
display_name="Name 1",
|
||||
conditions=Conditions(
|
||||
application_conditions=ApplicationsConditions(
|
||||
included_applications=["app-1", "app-2"],
|
||||
excluded_applications=["app-3", "app-4"],
|
||||
),
|
||||
user_conditions=UsersConditions(
|
||||
included_groups=["group-1", "group-2"],
|
||||
excluded_groups=["group-3", "group-4"],
|
||||
included_users=["user-1", "user-2"],
|
||||
excluded_users=["user-3", "user-4"],
|
||||
included_roles=["role-1", "role-2"],
|
||||
excluded_roles=["role-3", "role-4"],
|
||||
),
|
||||
),
|
||||
grant_controls=GrantControls(
|
||||
built_in_controls=[ConditionalAccessGrantControl.BLOCK]
|
||||
),
|
||||
session_controls=SessionControls(
|
||||
persistent_browser=PersistentBrowser(
|
||||
is_enabled=True,
|
||||
mode="always",
|
||||
),
|
||||
sign_in_frequency=SignInFrequency(
|
||||
is_enabled=True,
|
||||
frequency=24,
|
||||
type=SignInFrequencyType.HOURS,
|
||||
interval=SignInFrequencyInterval.TIME_BASED,
|
||||
),
|
||||
),
|
||||
state=ConditionalAccessPolicyState.ENABLED_FOR_REPORTING,
|
||||
)
|
||||
}
|
||||
|
||||
@patch(
|
||||
"prowler.providers.microsoft365.services.entra.entra_service.Entra._get_groups",
|
||||
new=mock_entra_get_groups,
|
||||
|
||||
Reference in New Issue
Block a user