Rules/APP-006.risky-delegated-grants.psd1

@{
    Id          = 'APP-006'
    Title       = 'No risky delegated consent grants'
    Area        = 'ConsentGrants'
    Severity    = 'Medium'
    References  = @(
        'https://learn.microsoft.com/defender-cloud-apps/investigate-risky-oauth'
    )
    Rationale   = 'Delegated grants with write access to mail or files plus offline_access are the classic footprint of illicit consent-grant attacks - durable API access in user context, invisible to password resets.'
    Remediation = 'Review the flagged grants (who consented, is the app known?) and revoke unexpected ones; restrict future user consent (see ID-005).'
    Test        = {
        param($Snapshot)
        $riskyScopes = @('Mail.ReadWrite', 'Mail.Send', 'MailboxSettings.ReadWrite', 'Files.ReadWrite.All',
            'Sites.ReadWrite.All', 'Directory.ReadWrite.All', 'Contacts.ReadWrite', 'Notes.ReadWrite.All')
        $hits = [System.Collections.Generic.List[object]]::new()
        foreach ($grant in @($Snapshot.ConsentGrants.delegatedGrants)) {
            $scopeTokens = @(([string]$grant.scope) -split '\s+' | Where-Object { $_ })
            $matched = @($scopeTokens | Where-Object { $riskyScopes -contains $_ })
            if ($matched.Count -gt 0) {
                $clientLabel = $(if ($grant.PSObject.Properties['_tlClientName'] -and $grant._tlClientName) { $grant._tlClientName } else { $grant.clientId })
                $hits.Add(("{0}: {1} ({2})" -f $clientLabel, ($matched -join ', '), $grant.consentType))
            }
        }
        if ($hits.Count -gt 0) {
            New-TLRuleResult -Status Fail -Evidence @($hits | Select-Object -First 15)
        }
        else {
            New-TLRuleResult -Status Pass -Evidence ("No risky delegated scopes across {0} consent grants." -f @($Snapshot.ConsentGrants.delegatedGrants).Count)
        }
    }
}