From 4cf2207d586ef99e935608a206fcdec72c38e6ef Mon Sep 17 00:00:00 2001 From: "Hugo P.Brito" Date: Mon, 11 May 2026 11:43:42 +0100 Subject: [PATCH] fix(m365): emit a single MANUAL finding when PIM stale alert is unavailable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous behavior fanned out a FAIL finding for every Organization the tenant returned whenever the stale sign-in alert was missing from the API response. That conflates three distinct conditions — no Microsoft Entra ID P2 license, alert disabled, or insufficient permission — into the same verdict, penalizes tenants without PIM, and emits N near-duplicate findings for what is logically a single tenant-level state. Emit a single MANUAL finding pinned to the first organization instead, with a status_extended that lists the actionable causes so the operator can choose what to remediate. MANUAL is the right verdict because the cause may be legitimate (no P2) rather than misconfiguration. --- .../entra_pim_stale_sign_in_alert.py | 28 +++++++++++-------- .../entra_pim_stale_sign_in_alert_test.py | 14 +++++++--- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/prowler/providers/m365/services/entra/entra_pim_stale_sign_in_alert/entra_pim_stale_sign_in_alert.py b/prowler/providers/m365/services/entra/entra_pim_stale_sign_in_alert/entra_pim_stale_sign_in_alert.py index 0ed47d0c50..38c23284b2 100644 --- a/prowler/providers/m365/services/entra/entra_pim_stale_sign_in_alert/entra_pim_stale_sign_in_alert.py +++ b/prowler/providers/m365/services/entra/entra_pim_stale_sign_in_alert/entra_pim_stale_sign_in_alert.py @@ -60,16 +60,22 @@ class entra_pim_stale_sign_in_alert(Check): report.status_extended = "PIM stale sign-in alert reports no stale accounts in privileged roles." findings.append(report) - else: - for organization in entra_client.organizations: - report = CheckReportM365( - self.metadata(), - resource=organization, - resource_id=organization.id, - resource_name=organization.name, - ) - report.status = "FAIL" - report.status_extended = "PIM stale sign-in alert is not configured or not available for the tenant." - findings.append(report) + elif entra_client.organizations: + organization = entra_client.organizations[0] + report = CheckReportM365( + self.metadata(), + resource=organization, + resource_id=organization.id, + resource_name=organization.name, + ) + report.status = "MANUAL" + report.status_extended = ( + "PIM stale sign-in alert is not available. This can happen when " + "the tenant lacks Microsoft Entra ID P2, the alert is disabled, " + "or the running credentials cannot read PIM alerts. Review the " + "alert configuration in the Entra admin center under Identity " + "Governance > Privileged Identity Management > Alerts." + ) + findings.append(report) return findings diff --git a/tests/providers/m365/services/entra/entra_pim_stale_sign_in_alert/entra_pim_stale_sign_in_alert_test.py b/tests/providers/m365/services/entra/entra_pim_stale_sign_in_alert/entra_pim_stale_sign_in_alert_test.py index a5f8e38663..a48af4b3c1 100644 --- a/tests/providers/m365/services/entra/entra_pim_stale_sign_in_alert/entra_pim_stale_sign_in_alert_test.py +++ b/tests/providers/m365/services/entra/entra_pim_stale_sign_in_alert/entra_pim_stale_sign_in_alert_test.py @@ -160,7 +160,7 @@ class Test_entra_pim_stale_sign_in_alert: assert result[0].resource_id == "alert-001" def test_alert_not_configured(self): - """FAIL: PIM stale sign-in alert is not configured.""" + """MANUAL: PIM stale sign-in alert is not available.""" entra_client = mock.MagicMock() with ( @@ -183,15 +183,21 @@ class Test_entra_pim_stale_sign_in_alert: id="org-001", name="Contoso", on_premises_sync_enabled=False, - ) + ), + Organization( + id="org-002", + name="Contoso Two", + on_premises_sync_enabled=False, + ), ] check = entra_pim_stale_sign_in_alert() result = check.execute() assert len(result) == 1 - assert result[0].status == "FAIL" - assert "not configured or not available" in result[0].status_extended + assert result[0].status == "MANUAL" + assert "not available" in result[0].status_extended + assert "P2" in result[0].status_extended assert result[0].resource_id == "org-001" assert result[0].resource_name == "Contoso" assert result[0].location == "global"