feat(m365): add entra_app_registration_no_unused_privileged_permissions security check (#10080)

Co-authored-by: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com>
Co-authored-by: Daniel Barranquero <danielbo2001@gmail.com>
This commit is contained in:
Hugo Pereira Brito
2026-02-19 17:12:50 +01:00
committed by GitHub
parent 23e51158e0
commit 20b26bc7d0
11 changed files with 1292 additions and 7 deletions

View File

@@ -0,0 +1,895 @@
from unittest import mock
from uuid import uuid4
from prowler.providers.m365.services.entra.entra_service import (
OAuthApp,
OAuthAppPermission,
)
from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
class Test_entra_app_registration_no_unused_privileged_permissions:
def test_no_oauth_apps(self):
"""No OAuth apps registered in tenant (empty dict): expected PASS."""
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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = {}
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "No OAuth applications are registered in the tenant."
)
assert result[0].resource == {}
assert result[0].resource_name == "OAuth Applications"
assert result[0].resource_id == "oauthApps"
def test_no_oauth_apps_none(self):
"""OAuth apps is None (App Governance not enabled): expected FAIL."""
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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = None
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "OAuth App Governance data is unavailable. Enable App Governance in Microsoft Defender for Cloud Apps and grant ThreatHunting.Read.All to evaluate unused privileged permissions."
)
assert result[0].resource == {}
assert result[0].resource_name == "OAuth Applications"
assert result[0].resource_id == "oauthApps"
def test_app_no_permissions(self):
"""App with no permissions: expected PASS."""
app_id = str(uuid4())
app_name = "Test App No Permissions"
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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = {
app_id: OAuthApp(
id=app_id,
name=app_name,
status="Enabled",
privilege_level="Low",
permissions=[],
service_principal_id=str(uuid4()),
is_admin_consented=False,
last_used_time=None,
app_origin="Internal",
)
}
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"App registration {app_name} has no unused privileged permissions."
)
assert result[0].resource_name == app_name
assert result[0].resource_id == app_id
def test_app_all_permissions_in_use(self):
"""App with all privileged permissions in use: expected PASS."""
app_id = str(uuid4())
app_name = "Test App All In Use"
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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = {
app_id: OAuthApp(
id=app_id,
name=app_name,
status="Enabled",
privilege_level="High",
permissions=[
OAuthAppPermission(
name="Mail.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="InUse",
),
OAuthAppPermission(
name="User.Read.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="InUse",
),
],
service_principal_id=str(uuid4()),
is_admin_consented=True,
last_used_time="2024-01-15T10:30:00Z",
app_origin="Internal",
)
}
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"App registration {app_name} has no unused privileged permissions."
)
assert result[0].resource_name == app_name
assert result[0].resource_id == app_id
def test_app_low_privilege_unused(self):
"""App with unused low privilege permissions (not high): expected PASS."""
app_id = str(uuid4())
app_name = "Test App Low Privilege Unused"
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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = {
app_id: OAuthApp(
id=app_id,
name=app_name,
status="Enabled",
privilege_level="Low",
permissions=[
OAuthAppPermission(
name="User.Read",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Delegated",
privilege_level="Low",
usage_status="NotInUse",
),
OAuthAppPermission(
name="openid",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Delegated",
privilege_level="Low",
usage_status="NotInUse",
),
],
service_principal_id=str(uuid4()),
is_admin_consented=False,
last_used_time=None,
app_origin="External",
)
}
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"App registration {app_name} has no unused privileged permissions."
)
assert result[0].resource_name == app_name
assert result[0].resource_id == app_id
def test_app_medium_privilege_unused(self):
"""App with unused medium privilege permissions (not high): expected PASS."""
app_id = str(uuid4())
app_name = "Test App Medium Privilege Unused"
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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = {
app_id: OAuthApp(
id=app_id,
name=app_name,
status="Enabled",
privilege_level="Medium",
permissions=[
OAuthAppPermission(
name="Files.Read",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Delegated",
privilege_level="Medium",
usage_status="NotInUse",
),
],
service_principal_id=str(uuid4()),
is_admin_consented=False,
last_used_time=None,
app_origin="External",
)
}
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"App registration {app_name} has no unused privileged permissions."
)
assert result[0].resource_name == app_name
assert result[0].resource_id == app_id
def test_app_one_unused_high_privilege_permission(self):
"""App with one unused high privilege permission: expected FAIL."""
app_id = str(uuid4())
app_name = "Test App One Unused High"
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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = {
app_id: OAuthApp(
id=app_id,
name=app_name,
status="Enabled",
privilege_level="High",
permissions=[
OAuthAppPermission(
name="Mail.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
OAuthAppPermission(
name="User.Read",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Delegated",
privilege_level="Low",
usage_status="InUse",
),
],
service_principal_id=str(uuid4()),
is_admin_consented=True,
last_used_time="2024-01-15T10:30:00Z",
app_origin="Internal",
)
}
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"App registration {app_name} has 1 unused privileged permission(s): Mail.ReadWrite.All."
)
assert result[0].resource_name == app_name
assert result[0].resource_id == app_id
def test_app_multiple_unused_high_privilege_permissions(self):
"""App with multiple unused high privilege permissions: expected FAIL."""
app_id = str(uuid4())
app_name = "Test App Multiple Unused High"
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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = {
app_id: OAuthApp(
id=app_id,
name=app_name,
status="Enabled",
privilege_level="High",
permissions=[
OAuthAppPermission(
name="Mail.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
OAuthAppPermission(
name="Directory.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
OAuthAppPermission(
name="User.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
],
service_principal_id=str(uuid4()),
is_admin_consented=True,
last_used_time="2024-01-15T10:30:00Z",
app_origin="External",
)
}
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"App registration {app_name} has 3 unused privileged permission(s): Mail.ReadWrite.All, Directory.ReadWrite.All, User.ReadWrite.All."
)
assert result[0].resource_name == app_name
assert result[0].resource_id == app_id
def test_app_more_than_five_unused_high_privilege_permissions(self):
"""App with more than 5 unused high privilege permissions: expected FAIL with truncated list."""
app_id = str(uuid4())
app_name = "Test App Many Unused High"
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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = {
app_id: OAuthApp(
id=app_id,
name=app_name,
status="Enabled",
privilege_level="High",
permissions=[
OAuthAppPermission(
name="Mail.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
OAuthAppPermission(
name="Directory.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
OAuthAppPermission(
name="User.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
OAuthAppPermission(
name="Group.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
OAuthAppPermission(
name="Sites.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
OAuthAppPermission(
name="RoleManagement.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
OAuthAppPermission(
name="Application.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
],
service_principal_id=str(uuid4()),
is_admin_consented=True,
last_used_time="2024-01-15T10:30:00Z",
app_origin="External",
)
}
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"App registration {app_name} has 7 unused privileged permission(s): Mail.ReadWrite.All, Directory.ReadWrite.All, User.ReadWrite.All, Group.ReadWrite.All, Sites.ReadWrite.All (and 2 more)."
)
assert result[0].resource_name == app_name
assert result[0].resource_id == app_id
def test_app_unused_with_not_in_use_status(self):
"""App with unused permission using 'not_in_use' status variant: expected FAIL."""
app_id = str(uuid4())
app_name = "Test App NotInUse Variant"
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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = {
app_id: OAuthApp(
id=app_id,
name=app_name,
status="Enabled",
privilege_level="High",
permissions=[
OAuthAppPermission(
name="Mail.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="not_in_use",
),
],
service_principal_id=str(uuid4()),
is_admin_consented=True,
last_used_time=None,
app_origin="Internal",
)
}
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"App registration {app_name} has 1 unused privileged permission(s): Mail.ReadWrite.All."
)
assert result[0].resource_name == app_name
assert result[0].resource_id == app_id
def test_multiple_apps_mixed_results(self):
"""Multiple apps with mixed results: one PASS and one FAIL."""
app_id_pass = str(uuid4())
app_name_pass = "Test App Pass"
app_id_fail = str(uuid4())
app_name_fail = "Test App Fail"
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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = {
app_id_pass: OAuthApp(
id=app_id_pass,
name=app_name_pass,
status="Enabled",
privilege_level="High",
permissions=[
OAuthAppPermission(
name="Mail.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="InUse",
),
],
service_principal_id=str(uuid4()),
is_admin_consented=True,
last_used_time="2024-01-15T10:30:00Z",
app_origin="Internal",
),
app_id_fail: OAuthApp(
id=app_id_fail,
name=app_name_fail,
status="Enabled",
privilege_level="High",
permissions=[
OAuthAppPermission(
name="Directory.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
],
service_principal_id=str(uuid4()),
is_admin_consented=True,
last_used_time="2024-01-15T10:30:00Z",
app_origin="External",
),
}
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 2
# Find results by app ID
result_pass = next(r for r in result if r.resource_id == app_id_pass)
result_fail = next(r for r in result if r.resource_id == app_id_fail)
assert result_pass.status == "PASS"
assert (
result_pass.status_extended
== f"App registration {app_name_pass} has no unused privileged permissions."
)
assert result_pass.resource_name == app_name_pass
assert result_fail.status == "FAIL"
assert (
result_fail.status_extended
== f"App registration {app_name_fail} has 1 unused privileged permission(s): Directory.ReadWrite.All."
)
assert result_fail.resource_name == app_name_fail
def test_app_mixed_privilege_levels_unused(self):
"""App with mixed privilege levels (High and Low) unused: only High triggers FAIL."""
app_id = str(uuid4())
app_name = "Test App Mixed Privileges"
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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = {
app_id: OAuthApp(
id=app_id,
name=app_name,
status="Enabled",
privilege_level="High",
permissions=[
OAuthAppPermission(
name="Mail.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
OAuthAppPermission(
name="User.Read",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Delegated",
privilege_level="Low",
usage_status="NotInUse",
),
OAuthAppPermission(
name="Files.Read",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Delegated",
privilege_level="Medium",
usage_status="NotInUse",
),
],
service_principal_id=str(uuid4()),
is_admin_consented=True,
last_used_time="2024-01-15T10:30:00Z",
app_origin="Internal",
)
}
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
# Only the High privilege permission should be reported
assert (
result[0].status_extended
== f"App registration {app_name} has 1 unused privileged permission(s): Mail.ReadWrite.All."
)
assert result[0].resource_name == app_name
assert result[0].resource_id == app_id
def test_app_high_privilege_in_use_and_unused(self):
"""App with some high privilege permissions in use and some unused: expected FAIL."""
app_id = str(uuid4())
app_name = "Test App Partial Usage"
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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = {
app_id: OAuthApp(
id=app_id,
name=app_name,
status="Enabled",
privilege_level="High",
permissions=[
OAuthAppPermission(
name="Mail.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="InUse",
),
OAuthAppPermission(
name="Directory.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="NotInUse",
),
OAuthAppPermission(
name="User.ReadWrite.All",
target_app_id="00000003-0000-0000-c000-000000000000",
target_app_name="Microsoft Graph",
permission_type="Application",
privilege_level="High",
usage_status="InUse",
),
],
service_principal_id=str(uuid4()),
is_admin_consented=True,
last_used_time="2024-01-15T10:30:00Z",
app_origin="Internal",
)
}
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"App registration {app_name} has 1 unused privileged permission(s): Directory.ReadWrite.All."
)
assert result[0].resource_name == app_name
assert result[0].resource_id == app_id
def test_app_without_name_uses_id(self):
"""App without a name should use app_id as resource_name."""
app_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_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions.entra_client",
new=entra_client,
),
):
from prowler.providers.m365.services.entra.entra_app_registration_no_unused_privileged_permissions.entra_app_registration_no_unused_privileged_permissions import (
entra_app_registration_no_unused_privileged_permissions,
)
entra_client.oauth_apps = {
app_id: OAuthApp(
id=app_id,
name="",
status="Enabled",
privilege_level="Low",
permissions=[],
service_principal_id=str(uuid4()),
is_admin_consented=False,
last_used_time=None,
app_origin="Internal",
)
}
check = entra_app_registration_no_unused_privileged_permissions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].resource_name == ""
assert result[0].resource_id == app_id