Skip to content

[New Check]: Apps with Exchange mailbox permissions must be scoped via Application Access Policy #11065

@HugoPBrito

Description

@HugoPBrito

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 same AppId.
  • Why it matters: Microsoft Graph application permissions like Mail.Read grant 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 with Mail.Read and no policy is, by definition, able to read every user's email until restricted.
  • Resource involved: Microsoft Entra service principals and their appRoleAssignments against 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:
    1. 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.
    2. Enumerate every service principal in the tenant and read its appRoleAssignments against the Microsoft Graph service principal. Collect the AppId of any service principal that has at least one of those role assignments.
    3. Run Exchange Online PowerShell Get-ApplicationAccessPolicy and collect the AppId field of every returned policy.
    4. Compare: every AppId from step 2 must appear in step 3.
  • 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 their appOwnerOrganizationId matches Microsoft's first-party tenant IDs.
    • Disabled service principals (accountEnabled = false) are out of scope.
    • The check operates only on application permissions (Application role type), not delegated permissions (delegated access is constrained by the signed-in user).

References

Suggested severity

Medium

Additional implementation notes

  • Existing patterns to follow: Prowler's exchange service already wires Exchange Online PowerShell (prowler/providers/m365/services/exchange/exchange_service.py) for sibling checks like exchange_organization_modern_authentication_enabled. Reuse the same PowerShell session to invoke Get-ApplicationAccessPolicy. For the Graph side, the entra service 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.All covers reading service principals and their app role assignments) plus the Exchange Online PowerShell access the exchange service already requires.
  • PowerShell IS required for the Get-ApplicationAccessPolicy half 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/. Mark ServiceName as exchange and place the check directory under prowler/providers/m365/services/exchange/.

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature-requestNew feature request for Prowler.good first issueIndicates a good issue for first-time contributorsprovider/m365Issues/PRs related with the M365 provider

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions