Private/M365Monitor/Detections/Test-M365DefenderAlertChange.ps1

# PSGuerrilla - Jim Tyler, Microsoft MVP - CC BY 4.0
# https://github.com/jimrtyler/PSGuerrilla | https://creativecommons.org/licenses/by/4.0/
# AI/LLM use: see AI-USAGE.md for required attribution
function Test-M365DefenderAlertChange {
    [CmdletBinding()]
    param(
        [PSCustomObject[]]$Events = @()
    )

    $results = [System.Collections.Generic.List[PSCustomObject]]::new()

    # High-risk operations
    $disablePatterns = @(
        'Disable-ProtectionAlert'
        'Remove-ProtectionAlert'
        'Disable-AntiPhishPolicy'
        'Remove-AntiPhishPolicy'
        'Disable-SafeAttachmentPolicy'
        'Remove-SafeAttachmentPolicy'
        'Disable-SafeLinksPolicy'
        'Remove-SafeLinksPolicy'
        'Disable-AlertPolicy'
        'Remove-AlertPolicy'
    )

    foreach ($event in $Events) {
        $operationType = $event.OperationType ?? $event.Activity ?? ''
        $policyName = $event.TargetName ?? ''
        $isDisabled = $false
        $isRemoved = $false
        $severityDowngraded = $false
        $notificationsDisabled = $false
        $changeDetails = [System.Collections.Generic.List[string]]::new()

        # Check for disable/remove operations
        foreach ($pattern in $disablePatterns) {
            if ($operationType -match [regex]::Escape($pattern) -or $event.Activity -match [regex]::Escape($pattern)) {
                if ($pattern -match 'Remove') {
                    $isRemoved = $true
                    $changeDetails.Add("Alert policy removed: $operationType")
                } else {
                    $isDisabled = $true
                    $changeDetails.Add("Alert policy disabled: $operationType")
                }
                break
            }
        }

        # Analyze property changes
        if (-not $isDisabled -and -not $isRemoved) {
            foreach ($prop in $event.ModifiedProps) {
                $propName = $prop.Name ?? ''
                $newVal = $prop.NewValue ?? ''
                $oldVal = $prop.OldValue ?? ''

                # Policy enabled/disabled state
                if ($propName -match 'IsEnabled|Enabled|IsActive|State') {
                    $cleanNew = ($newVal -replace '"', '').Trim()
                    $cleanOld = ($oldVal -replace '"', '').Trim()

                    if ($cleanNew -match 'false|False|disabled|Disabled|inactive' -and
                        $cleanOld -match 'true|True|enabled|Enabled|active') {
                        $isDisabled = $true
                        $changeDetails.Add("Policy disabled: $propName changed from '$cleanOld' to '$cleanNew'")
                    }
                }

                # Severity downgrade
                if ($propName -match 'Severity|ThreatSeverity|AlertSeverity') {
                    $severityOrder = @{ 'Informational' = 0; 'Low' = 1; 'Medium' = 2; 'High' = 3; 'Critical' = 4 }
                    $cleanNew = ($newVal -replace '"', '').Trim()
                    $cleanOld = ($oldVal -replace '"', '').Trim()

                    $oldSev = 0; $newSev = 0
                    foreach ($sev in $severityOrder.Keys) {
                        if ($cleanOld -match $sev) { $oldSev = $severityOrder[$sev] }
                        if ($cleanNew -match $sev) { $newSev = $severityOrder[$sev] }
                    }

                    if ($newSev -lt $oldSev) {
                        $severityDowngraded = $true
                        $changeDetails.Add("Severity downgraded: $propName from '$cleanOld' to '$cleanNew'")
                    }
                }

                # Notification settings
                if ($propName -match 'NotifyUser|EmailNotification|NotificationEnabled|AggregationType') {
                    $cleanNew = ($newVal -replace '"', '').Trim()
                    $cleanOld = ($oldVal -replace '"', '').Trim()

                    if ($cleanNew -match 'false|False|disabled|None' -and
                        $cleanOld -match 'true|True|enabled') {
                        $notificationsDisabled = $true
                        $changeDetails.Add("Notifications disabled: $propName = '$cleanNew'")
                    }
                }

                # Scope/filter reduction
                if ($propName -match 'Filter|Scope|TargetedUsers|TargetedDomains|ExcludedUsers|ExcludedDomains') {
                    $changeDetails.Add("Scope modified: $propName changed")
                }

                # Protection action weakened
                if ($propName -match 'Action|PhishThresholdLevel|BulkThreshold|SpamAction') {
                    $cleanNew = ($newVal -replace '"', '').Trim()
                    if ($cleanNew -match 'Allow|NoAction|Deliver|MoveToJunk' -and
                        $cleanOld -match 'Block|Quarantine|Delete|Reject') {
                        $changeDetails.Add("Protection action weakened: $propName from '$cleanOld' to '$cleanNew'")
                    }
                }
            }
        }

        $isDisabling = $isRemoved -or $isDisabled

        # Severity assessment
        $severity = if ($isRemoved) { 'Critical' }
                    elseif ($isDisabled) { 'High' }
                    elseif ($severityDowngraded -and $notificationsDisabled) { 'High' }
                    elseif ($severityDowngraded -or $notificationsDisabled) { 'Medium' }
                    else { 'Low' }

        $description = if ($isRemoved) {
            "Defender alert policy '$policyName' REMOVED by $($event.Actor)"
        } elseif ($isDisabled) {
            "Defender alert policy '$policyName' DISABLED by $($event.Actor)"
        } elseif ($severityDowngraded) {
            "Defender alert policy '$policyName' severity downgraded by $($event.Actor)"
        } else {
            "Defender alert policy '$policyName' modified by $($event.Actor)"
        }

        $results.Add([PSCustomObject]@{
            Timestamp     = $event.Timestamp
            Actor         = $event.Actor
            DetectionType = 'm365DefenderAlertChange'
            Description   = $description
            Details       = @{
                PolicyName            = $policyName
                OperationType         = $operationType
                IsDisabling           = $isDisabling
                IsRemoved             = $isRemoved
                SeverityDowngraded    = $severityDowngraded
                NotificationsDisabled = $notificationsDisabled
                ChangeNotes           = @($changeDetails)
                ModifiedProps         = $event.ModifiedProps
            }
            Severity      = $severity
        })
    }

    return @($results)
}