Security/DefenderSafeAttLinksChecks.ps1

# -------------------------------------------------------------------
# Defender -- Safe Links & Safe Attachments Checks
# Extracted from Get-DefenderSecurityConfig.ps1 (#257)
# Runs in shared scope: $settings, $checkIdCounter, Add-Setting,
# Test-PresetPolicy, $script:presetPolicyNames
# -------------------------------------------------------------------
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
param()

# ------------------------------------------------------------------
# 4. Safe Links Policies (Defender P1+)
# ------------------------------------------------------------------
try {
    $slAvailable = Get-Command -Name Get-SafeLinksPolicy -ErrorAction SilentlyContinue
    if ($slAvailable) {
        Write-Verbose "Checking Safe Links policies..."
        $safeLinks = Get-SafeLinksPolicy -ErrorAction Stop

        if (@($safeLinks).Count -eq 0) {
            $settingParams = @{
                Category         = 'Safe Links'
                Setting          = 'Safe Links Policies'
                CurrentValue     = 'None configured'
                RecommendedValue = 'At least 1 policy'
                Status           = 'Warning'
                CheckId          = 'DEFENDER-SAFELINKS-001'
                Remediation      = 'Run: New-SafeLinksPolicy -Name "Safe Links" -IsEnabled $true; New-SafeLinksRule -Name "Safe Links" -SafeLinksPolicy "Safe Links" -RecipientDomainIs (Get-AcceptedDomain).Name. Security admin center > Safe Links > Create a policy covering all users.'
            }
            Add-Setting @settingParams
        }
        else {
            foreach ($policy in @($safeLinks)) {
                $policyLabel = $policy.Name
                $presetTier = Test-PresetPolicy -PolicyName $policy.Name
                if ($presetTier) {
                    $settingParams = @{
                        Category         = 'Safe Links'
                        Setting          = "Policy ($policyLabel)"
                        CurrentValue     = "Managed by $presetTier preset security policy"
                        RecommendedValue = 'Preset security policy active'
                        Status           = 'Pass'
                        CheckId          = 'DEFENDER-SAFELINKS-001'
                        Remediation      = 'No action needed -- settings enforced by preset security policy.'
                    }
                    Add-Setting @settingParams
                    continue
                }

                # URL scanning
                $scanUrls = $policy.ScanUrls
                $settingParams = @{
                    Category         = 'Safe Links'
                    Setting          = "Real-time URL Scanning ($policyLabel)"
                    CurrentValue     = "$scanUrls"
                    RecommendedValue = 'True'
                    Status           = if ($scanUrls) { 'Pass' } else { 'Warning' }
                    CheckId          = 'DEFENDER-SAFELINKS-001'
                    Remediation      = 'Run: Set-SafeLinksPolicy -Identity <PolicyName> -ScanUrls $true. Security admin center > Safe Links policy > URL & click protection > Enable real-time URL scanning.'
                }
                Add-Setting @settingParams

                # Click tracking
                $trackClicks = -not $policy.DoNotTrackUserClicks
                $settingParams = @{
                    Category         = 'Safe Links'
                    Setting          = "Track User Clicks ($policyLabel)"
                    CurrentValue     = "$trackClicks"
                    RecommendedValue = 'True'
                    Status           = if ($trackClicks) { 'Pass' } else { 'Warning' }
                    CheckId          = 'DEFENDER-SAFELINKS-001'
                    Remediation      = 'Run: Set-SafeLinksPolicy -Identity <PolicyName> -DoNotTrackUserClicks $false. Security admin center > Safe Links policy > Ensure "Do not track when users click protected links" is disabled.'
                }
                Add-Setting @settingParams

                # Internal senders
                if ($null -ne $policy.EnableForInternalSenders) {
                    $internalSenders = $policy.EnableForInternalSenders
                    $settingParams = @{
                        Category         = 'Safe Links'
                        Setting          = "Enable for Internal Senders ($policyLabel)"
                        CurrentValue     = "$internalSenders"
                        RecommendedValue = 'True'
                        Status           = if ($internalSenders) { 'Pass' } else { 'Warning' }
                        CheckId          = 'DEFENDER-SAFELINKS-001'
                        Remediation      = 'Run: Set-SafeLinksPolicy -Identity <PolicyName> -EnableForInternalSenders $true. Security admin center > Safe Links policy > Enable for messages sent within the organization.'
                    }
                    Add-Setting @settingParams
                }

                # Wait for URL scanning
                if ($null -ne $policy.DeliverMessageAfterScan) {
                    $waitScan = $policy.DeliverMessageAfterScan
                    $settingParams = @{
                        Category         = 'Safe Links'
                        Setting          = "Wait for URL Scan ($policyLabel)"
                        CurrentValue     = "$waitScan"
                        RecommendedValue = 'True'
                        Status           = if ($waitScan) { 'Pass' } else { 'Warning' }
                        CheckId          = 'DEFENDER-SAFELINKS-001'
                        Remediation      = 'Run: Set-SafeLinksPolicy -Identity <PolicyName> -DeliverMessageAfterScan $true. Security admin center > Safe Links policy > Wait for URL scanning to complete before delivering the message.'
                    }
                    Add-Setting @settingParams
                }
            }
        }
    }
    else {
        $settingParams = @{
            Category         = 'Safe Links'
            Setting          = 'Safe Links Availability'
            CurrentValue     = 'Not licensed'
            RecommendedValue = 'Defender for Office 365 P1+'
            Status           = 'Review'
            CheckId          = 'DEFENDER-SAFELINKS-001'
            Remediation      = 'Safe Links requires Defender for Office 365 Plan 1 or higher.'
        }
        Add-Setting @settingParams
    }
}
catch {
    Write-Warning "Could not retrieve Safe Links policies: $_"
}

# ------------------------------------------------------------------
# 5. Safe Attachments Policies (Defender P1+)
# ------------------------------------------------------------------
try {
    $saAvailable = Get-Command -Name Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue
    if ($saAvailable) {
        Write-Verbose "Checking Safe Attachments policies..."
        $safeAttachments = Get-SafeAttachmentPolicy -ErrorAction Stop

        if (@($safeAttachments).Count -eq 0) {
            $settingParams = @{
                Category         = 'Safe Attachments'
                Setting          = 'Safe Attachments Policies'
                CurrentValue     = 'None configured'
                RecommendedValue = 'At least 1 policy'
                Status           = 'Warning'
                CheckId          = 'DEFENDER-SAFEATTACH-001'
                Remediation      = 'Run: New-SafeAttachmentPolicy -Name "Safe Attachments" -Enable $true -Action Block; New-SafeAttachmentRule -Name "Safe Attachments" -SafeAttachmentPolicy "Safe Attachments" -RecipientDomainIs (Get-AcceptedDomain).Name. Security admin center > Safe Attachments > Create a policy covering all users.'
            }
            Add-Setting @settingParams
        }
        else {
            foreach ($policy in @($safeAttachments)) {
                $policyLabel = $policy.Name
                $presetTier = Test-PresetPolicy -PolicyName $policy.Name
                if ($presetTier) {
                    $settingParams = @{
                        Category         = 'Safe Attachments'
                        Setting          = "Policy ($policyLabel)"
                        CurrentValue     = "Managed by $presetTier preset security policy"
                        RecommendedValue = 'Preset security policy active'
                        Status           = 'Pass'
                        CheckId          = 'DEFENDER-SAFEATTACH-001'
                        Remediation      = 'No action needed -- settings enforced by preset security policy.'
                    }
                    Add-Setting @settingParams
                    continue
                }

                # Enabled
                $enabled = $policy.Enable
                $settingParams = @{
                    Category         = 'Safe Attachments'
                    Setting          = "Policy Enabled ($policyLabel)"
                    CurrentValue     = "$enabled"
                    RecommendedValue = 'True'
                    Status           = if ($enabled) { 'Pass' } else { 'Warning' }
                    CheckId          = 'DEFENDER-SAFEATTACH-001'
                    Remediation      = 'Run: Set-SafeAttachmentPolicy -Identity <PolicyName> -Enable $true. Security admin center > Safe Attachments policy > Enable the policy.'
                }
                Add-Setting @settingParams

                # Action type
                $action = $policy.Action
                $actionDisplay = switch ($action) {
                    'Allow'            { 'Allow (no scanning)' }
                    'Block'            { 'Block' }
                    'Replace'          { 'Replace attachment' }
                    'DynamicDelivery'  { 'Dynamic Delivery' }
                    default { $action }
                }

                $actionStatus = switch ($action) {
                    'Allow'           { 'Fail' }
                    'Block'           { 'Pass' }
                    'Replace'         { 'Pass' }
                    'DynamicDelivery' { 'Pass' }
                    default { 'Review' }
                }

                $settingParams = @{
                    Category         = 'Safe Attachments'
                    Setting          = "Action ($policyLabel)"
                    CurrentValue     = $actionDisplay
                    RecommendedValue = 'Block or Dynamic Delivery'
                    Status           = $actionStatus
                    CheckId          = 'DEFENDER-SAFEATTACH-001'
                    Remediation      = 'Run: Set-SafeAttachmentPolicy -Identity <PolicyName> -Action Block. Security admin center > Safe Attachments policy > Action > Block (or DynamicDelivery for user experience).'
                }
                Add-Setting @settingParams

                # Redirect
                $redirect = $policy.Redirect
                $settingParams = @{
                    Category         = 'Safe Attachments'
                    Setting          = "Redirect to Admin ($policyLabel)"
                    CurrentValue     = "$redirect"
                    RecommendedValue = 'True'
                    Status           = if ($redirect) { 'Pass' } else { 'Warning' }
                    CheckId          = 'DEFENDER-SAFEATTACH-001'
                    Remediation      = 'Run: Set-SafeAttachmentPolicy -Identity <PolicyName> -Redirect $true -RedirectAddress admin@domain.com. Security admin center > Safe Attachments policy > Enable redirect and specify an admin email.'
                }
                Add-Setting @settingParams
            }
        }
    }
    else {
        $settingParams = @{
            Category         = 'Safe Attachments'
            Setting          = 'Safe Attachments Availability'
            CurrentValue     = 'Not licensed'
            RecommendedValue = 'Defender for Office 365 P1+'
            Status           = 'Review'
            CheckId          = 'DEFENDER-SAFEATTACH-001'
            Remediation      = 'Safe Attachments requires Defender for Office 365 Plan 1 or higher.'
        }
        Add-Setting @settingParams
    }
}
catch {
    Write-Warning "Could not retrieve Safe Attachments policies: $_"
}

# ------------------------------------------------------------------
# 5b. Safe Attachments for SPO/OneDrive/Teams (CIS 2.1.5)
# ------------------------------------------------------------------
try {
    $atpO365Available = Get-Command -Name Get-AtpPolicyForO365 -ErrorAction SilentlyContinue
    if ($atpO365Available) {
        Write-Verbose "Checking Safe Attachments for SPO/OneDrive/Teams..."
        $atpPolicy = Get-AtpPolicyForO365 -ErrorAction Stop

        $atpEnabled = $atpPolicy.EnableATPForSPOTeamsODB
        $settingParams = @{
            Category         = 'Safe Attachments'
            Setting          = 'Safe Attachments for SPO/OneDrive/Teams'
            CurrentValue     = "$atpEnabled"
            RecommendedValue = 'True'
            Status           = if ($atpEnabled) { 'Pass' } else { 'Fail' }
            CheckId          = 'DEFENDER-SAFEATTACH-002'
            Remediation      = 'Run: Set-AtpPolicyForO365 -EnableATPForSPOTeamsODB $true. Security admin center > Safe Attachments > Global settings > Turn on Defender for Office 365 for SharePoint, OneDrive, and Microsoft Teams.'
        }
        Add-Setting @settingParams
    }
    else {
        $settingParams = @{
            Category         = 'Safe Attachments'
            Setting          = 'Safe Attachments for SPO/OneDrive/Teams'
            CurrentValue     = 'Not licensed'
            RecommendedValue = 'Defender for Office 365 P1+'
            Status           = 'Review'
            CheckId          = 'DEFENDER-SAFEATTACH-002'
            Remediation      = 'Safe Attachments for SPO/OneDrive/Teams requires Defender for Office 365 Plan 1 or higher.'
        }
        Add-Setting @settingParams
    }
}
catch {
    Write-Warning "Could not check Safe Attachments for SPO/OneDrive/Teams: $_"
}