prowler-cloud/prowler
View on GitHub[New Check]: Apps with Exchange mailbox permissions must be scoped via Application Access Policy
Open
#11065 opened on May 6, 2026
feature-requestgood first issuenew-checkprovider/m365
Description
Existing check search
- I have searched existing issues, Prowler Hub, and the public roadmap, and this check does not already exist.
Provider
Microsoft 365
New provider name
No response
Service or product area
exchange
Suggested check name
exchange_application_access_policy_restricts_mailbox_apps
Context and goal
- Security condition to validate: Every service principal in the tenant that holds any application-level Exchange mailbox permission (
Mail.Read,Mail.ReadBasic,Mail.ReadBasic.All,Mail.ReadWrite,Mail.Send,MailboxSettings.Read,MailboxSettings.ReadWrite,Calendars.Read,Calendars.ReadWrite,Contacts.Read,Contacts.ReadWrite) must be scoped by an Exchange Online Application Access Policy that lists the sameAppId. - Why it matters: Microsoft Graph application permissions like
Mail.Readgrant tenant-wide mailbox access by default — the consenting app can read every mailbox in the organization. Application Access Policies (New-ApplicationAccessPolicy) restrict that access to a defined mail-enabled security group, which is the only supported way to apply least-privilege scoping to those permissions. A service principal withMail.Readand no policy is, by definition, able to read every user's email until restricted. - Resource involved: Microsoft Entra service principals and their
appRoleAssignmentsagainst the Microsoft Graph service principal (appId 00000003-0000-0000-c000-000000000000), plus the Exchange Online Application Access Policy collection.
Expected behavior
- Resource or scope to evaluate:
- Resolve the Microsoft Graph service principal (
appId 00000003-0000-0000-c000-000000000000) to obtain the role IDs of the Exchange-relevant application roles listed above. - Enumerate every service principal in the tenant and read its
appRoleAssignmentsagainst the Microsoft Graph service principal. Collect theAppIdof any service principal that has at least one of those role assignments. - Run Exchange Online PowerShell
Get-ApplicationAccessPolicyand collect theAppIdfield of every returned policy. - Compare: every
AppIdfrom step 2 must appear in step 3.
- Resolve the Microsoft Graph service principal (
- PASS when: every service principal with at least one Exchange application permission has a matching
ApplicationAccessPolicy.AppId. - FAIL when: at least one such service principal has no matching policy. The finding should report the offending service principal's
displayName,appId, and the list of Exchange permissions it holds, so operators know exactly which app to scope. - MANUAL when: Exchange Online PowerShell is unavailable for the run (the operator has not configured PowerShell credentials) — the check cannot complete and should report MANUAL with a status message asking to enable EXO PowerShell.
- Exclusions / edge cases:
- First-party Microsoft service principals (e.g. Microsoft-owned apps) should not be flagged. Filter by
servicePrincipalType = 'Application'and exclude principals owned by Microsoft tenants if theirappOwnerOrganizationIdmatches Microsoft's first-party tenant IDs. - Disabled service principals (
accountEnabled = false) are out of scope. - The check operates only on application permissions (
Applicationrole type), not delegated permissions (delegated access is constrained by the signed-in user).
- First-party Microsoft service principals (e.g. Microsoft-owned apps) should not be flagged. Filter by
References
- Microsoft Graph — Limit application permissions to specific Exchange Online mailboxes: https://learn.microsoft.com/en-us/graph/auth-limit-mailbox-access
- Exchange Online PowerShell —
New-ApplicationAccessPolicy: https://learn.microsoft.com/en-us/powershell/module/exchange/new-applicationaccesspolicy - Exchange Online PowerShell —
Get-ApplicationAccessPolicy: https://learn.microsoft.com/en-us/powershell/module/exchange/get-applicationaccesspolicy - Microsoft Graph v1.0 —
servicePrincipalresource: https://learn.microsoft.com/en-us/graph/api/resources/serviceprincipal?view=graph-rest-1.0 - Microsoft Graph v1.0 — List
appRoleAssignmentsfor a service principal: https://learn.microsoft.com/en-us/graph/api/serviceprincipal-list-approleassignments?view=graph-rest-1.0
Suggested severity
Medium
Additional implementation notes
- Existing patterns to follow: Prowler's
exchangeservice already wires Exchange Online PowerShell (prowler/providers/m365/services/exchange/exchange_service.py) for sibling checks likeexchange_organization_modern_authentication_enabled. Reuse the same PowerShell session to invokeGet-ApplicationAccessPolicy. For the Graph side, theentraservice already enumerates service principals and app role assignments — extend it (or share via the M365 provider) so the new check has both data sets available. - Permissions / scopes: No additional permissions beyond Prowler's M365 baseline (
Directory.Read.Allcovers reading service principals and their app role assignments) plus the Exchange Online PowerShell access theexchangeservice already requires. - PowerShell IS required for the
Get-ApplicationAccessPolicyhalf of the check. Microsoft Graph does not expose Exchange Application Access Policies; this is intentional from Microsoft and the only supported way to read them is the Exchange Online module. - Metadata: follow the Exchange metadata schema used by sibling checks under
exchange/. MarkServiceNameasexchangeand place the check directory underprowler/providers/m365/services/exchange/.