Rules/MAIL-001.spf-configured.psd1

@{
    Id          = 'MAIL-001'
    Title       = 'Every custom mail domain publishes a strict SPF record'
    Area        = 'EmailAuthDns'
    Severity    = 'High'
    References  = @(
        'CIS Microsoft 365 Foundations Benchmark (SPF records for all domains)'
        'https://learn.microsoft.com/defender-office-365/email-authentication-spf-configure'
    )
    Rationale   = 'Without SPF (or with +all), anyone can spoof mail from the domain. SPF must end in -all (hardfail) or at least ~all (softfail).'
    Remediation = 'Publish a TXT record "v=spf1 include:spf.protection.outlook.com -all" (plus any legitimate senders) on every custom mail domain.'
    Test        = {
        param($Snapshot)
        $customDomains = @($Snapshot.EmailAuthDns | Where-Object { -not $_.isOnMicrosoft })
        # Older snapshots have no isMailDomain field - treat those as mail domains.
        $mailDomains = @($customDomains | Where-Object {
                -not $_.PSObject.Properties['isMailDomain'] -or [bool]$_.isMailDomain
            })
        $nonMailDomains = @($customDomains | Where-Object {
                $_.PSObject.Properties['isMailDomain'] -and -not [bool]$_.isMailDomain
            })
        $notes = @()
        if ($nonMailDomains.Count -gt 0) {
            $notes = @('Not evaluated (no Email service): ' + (@($nonMailDomains | ForEach-Object { $_.domain }) -join ', ') +
                '. Consider publishing "v=spf1 -all" and DMARC p=reject on non-mail domains as spoofing protection.')
        }
        if ($mailDomains.Count -eq 0) {
            return New-TLRuleResult -Status Pass -Evidence (@('No custom mail domains to check.') + $notes)
        }
        $bad = @($mailDomains | Where-Object {
                -not ($_.spf.present -and (@('hardfail', 'softfail') -contains [string]$_.spf.qualifier))
            })
        if ($bad.Count -gt 0) {
            New-TLRuleResult -Status Fail -Evidence (@($bad | ForEach-Object {
                        $detail = 'no SPF record'
                        if ($_.spf.present) { $detail = "qualifier '$($_.spf.qualifier)'" }
                        "$($_.domain): $detail"
                    }) + $notes)
        }
        else {
            New-TLRuleResult -Status Pass -Evidence (@($mailDomains | ForEach-Object { "$($_.domain): $($_.spf.qualifier)" }) + $notes)
        }
    }
}