checks/library/risky-oauth2-consent-grants.ps1

# Tenant-wide (AllPrincipals) delegated consent grants for sensitive Microsoft Graph scopes.
$graph = @(Get-GraphAll "https://graph.microsoft.com/v1.0/servicePrincipals?`$filter=appId eq '00000003-0000-0000-c000-000000000000'&`$select=id,displayName")[0]
$risky = @('Mail.ReadWrite', 'Mail.Send', 'Calendars.ReadWrite', 'Contacts.ReadWrite',
    'Files.ReadWrite.All', 'Sites.ReadWrite.All', 'Directory.ReadWrite.All', 'User.ReadWrite.All',
    'MailboxSettings.ReadWrite', 'Group.ReadWrite.All')
$items = @()
foreach ($g in Get-GraphAll "https://graph.microsoft.com/v1.0/oauth2PermissionGrants?`$top=999") {
    if ($g.consentType -ne 'AllPrincipals') { continue }
    if ($g.resourceId -ne $graph.id) { continue }   # Microsoft Graph resource only
    $scopes = @(($g.scope -split '\s+') | Where-Object { $_ })
    $hit = @($scopes | Where-Object { $risky -contains $_ })
    if ($hit.Count -gt 0) {
        $client = try { (Invoke-MgGraphRequest -Method GET -OutputType PSObject -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$($g.clientId)?`$select=displayName").displayName } catch { $g.clientId }
        $items += [pscustomobject]@{ Title = $client; Detail = "Tenant-wide Graph delegated grant of: $($hit -join ', ')" }
    }
}
[pscustomobject]@{
    Name     = 'Risky tenant-wide OAuth2 consent grants'
    Severity = 'high'
    Fix      = 'Revoke broad admin consent; prefer per-user / incremental consent and least-privilege scopes.'
    Items    = $items
}