Src/Private/Get-AbrExoSafeAttachmentsSafeLinks.ps1
|
function Get-AbrExoSafeAttachmentsSafeLinks { <# .SYNOPSIS Documents Microsoft Defender for Office 365 Safe Attachments and Safe Links policies with ACSC E8 and CIS compliance assessments. .NOTES Version: 0.1.0 Author: Pai Wei Sing #> [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory)] [string]$TenantId ) begin { Write-PScriboMessage -Message "Collecting Safe Attachments and Safe Links configuration for $TenantId." Show-AbrDebugExecutionTime -Start -TitleMessage 'SafeAttachmentsSafeLinks' } process { Section -Style Heading2 'Safe Attachments & Safe Links' { Paragraph "The following section documents Safe Attachments and Safe Links policies in tenant $TenantId. These features require a Microsoft Defender for Office 365 Plan 1 or higher licence." BlankLine # Compliance variable defaults $SafeAttachPolicyCount = 0 $SafeAttachBlockActionCount = 0 $SafeAttachForSPOEnabled = $false $SafeLinksPolicyCount = 0 $SafeLinksDoNotRewriteUrlCount = 0 $SafeLinksRealTimeCount = 0 #region Safe Attachments try { Write-Host " - Retrieving Safe Attachments policies..." $SafeAttachPolicies = Get-SafeAttachmentPolicy -ErrorAction Stop | Sort-Object IsDefault -Descending $SafeAttachPolicyCount = @($SafeAttachPolicies | Where-Object { $_.Enable -eq $true }).Count $SafeAttachBlockActionCount = @($SafeAttachPolicies | Where-Object { $_.Enable -eq $true -and $_.Action -in @('Block', 'DynamicDelivery', 'Replace') }).Count # Check ATP policy for SPO/OneDrive/Teams try { $AtpPolicy = Get-AtpPolicyForO365 -ErrorAction SilentlyContinue $SafeAttachForSPOEnabled = ($AtpPolicy.EnableATPForSPOTeamsODB -eq $true) } catch { } Section -Style Heading3 'Safe Attachments Policies' { if ($SafeAttachPolicies) { Paragraph "$(@($SafeAttachPolicies).Count) Safe Attachments policy/policies found. $SafeAttachPolicyCount are enabled." BlankLine $SAObj = [System.Collections.ArrayList]::new() foreach ($Policy in $SafeAttachPolicies) { $saInObj = [ordered] @{ 'Policy Name' = $Policy.Name 'Enabled' = $Policy.Enable 'Default Policy' = $Policy.IsDefault 'Action' = $Policy.Action 'Dynamic Delivery' = $Policy.Action -eq 'DynamicDelivery' 'Quarantine Tag' = if ($Policy.QuarantineTag) { $Policy.QuarantineTag } else { 'Default' } 'Redirect on Detect' = $Policy.Redirect 'Redirect Address' = if ($Policy.RedirectAddress) { $Policy.RedirectAddress } else { 'Not Set' } } $SAObj.Add([pscustomobject](ConvertTo-HashToYN $saInObj)) | Out-Null } $null = (& { if ($HealthCheck.ExchangeOnline.SafeAttachments) { $null = ($SAObj | Where-Object { $_.'Enabled' -eq 'No' } | Set-Style -Style Warning | Out-Null) $null = ($SAObj | Where-Object { $_.'Action' -eq 'Allow' } | Set-Style -Style Critical | Out-Null) } }) $SATableParams = @{ Name = "Safe Attachments Policies - $TenantId"; List = $false; ColumnWidths = 18, 8, 8, 14, 12, 14, 12, 14 } if ($Report.ShowTableCaptions) { $SATableParams['Caption'] = "- $($SATableParams.Name)" } $SAObj | Table @SATableParams $script:ExcelSheets['Safe Attachments'] = $SAObj # SPO/ODB ATP Status BlankLine $ATPObj = [pscustomobject]@{ 'Safe Attachments for SharePoint/OneDrive/Teams' = if ($SafeAttachForSPOEnabled) { 'Enabled' } else { 'Disabled' } } if ($HealthCheck.ExchangeOnline.SafeAttachments -and -not $SafeAttachForSPOEnabled) { $ATPObj | Set-Style -Style Warning | Out-Null } Paragraph "Safe Attachments for SharePoint, OneDrive, and Microsoft Teams: $(if ($SafeAttachForSPOEnabled) { 'ENABLED' } else { 'DISABLED -- remediation recommended.' })" } else { Paragraph "No Safe Attachments policies found. Defender for Office 365 Plan 1 or higher is required." } # Policy Rules $SAFRules = Get-SafeAttachmentRule -ErrorAction SilentlyContinue | Sort-Object Priority if ($InfoLevel.SafeAttachments -ge 2 -and $SAFRules) { BlankLine Section -Style Heading3 'Safe Attachments Policy Assignments' { Paragraph "The following assignments determine which users are protected by each Safe Attachments policy." BlankLine $SARuleObj = [System.Collections.ArrayList]::new() foreach ($Rule in $SAFRules) { $saRuleInObj = [ordered] @{ 'Rule Name' = $Rule.Name 'Priority' = $Rule.Priority 'State' = $Rule.State 'Policy' = $Rule.SafeAttachmentPolicy 'Applied To' = (@($Rule.SentTo) + @($Rule.SentToMemberOf) + @($Rule.RecipientDomainIs)) -join ', ' } $SARuleObj.Add([pscustomobject]$saRuleInObj) | Out-Null } $null = (& { if ($HealthCheck.ExchangeOnline.SafeAttachments) { $null = ($SARuleObj | Where-Object { $_.State -eq 'Disabled' } | Set-Style -Style Warning | Out-Null) } }) $SARuleTableParams = @{ Name = "Safe Attachments Assignments - $TenantId"; List = $false; ColumnWidths = 22, 8, 8, 22, 40 } if ($Report.ShowTableCaptions) { $SARuleTableParams['Caption'] = "- $($SARuleTableParams.Name)" } $SARuleObj | Table @SARuleTableParams } } } } catch { Write-ExoError 'SafeAttachments' "Unable to retrieve Safe Attachments policies: $($_.Exception.Message)" Paragraph "Safe Attachments policies could not be retrieved. This feature requires Defender for Office 365 Plan 1." } #endregion #region Safe Links try { Write-Host " - Retrieving Safe Links policies..." $SafeLinksPolicies = Get-SafeLinksPolicy -ErrorAction Stop | Sort-Object IsDefault -Descending $SafeLinksPolicyCount = @($SafeLinksPolicies | Where-Object { $_.IsEnabled -eq $true -or $_.State -eq 'Enabled' }).Count $SafeLinksDoNotRewriteUrlCount = ($SafeLinksPolicies.DoNotRewriteUrls | Measure-Object -Sum).Count $SafeLinksRealTimeCount = @($SafeLinksPolicies | Where-Object { ($_.IsEnabled -eq $true -or $_.State -eq 'Enabled') -and $_.ScanUrls -eq $true }).Count Section -Style Heading3 'Safe Links Policies' { if ($SafeLinksPolicies) { Paragraph "$(@($SafeLinksPolicies).Count) Safe Links policy/policies found. $SafeLinksPolicyCount are enabled." BlankLine $SLObj = [System.Collections.ArrayList]::new() foreach ($Policy in $SafeLinksPolicies) { $DoNotRewriteCount = if ($Policy.DoNotRewriteUrls) { @($Policy.DoNotRewriteUrls).Count } else { 0 } $slInObj = [ordered] @{ 'Policy Name' = $Policy.Name 'Enabled' = ($Policy.IsEnabled -eq $true -or $Policy.State -eq 'Enabled') 'Default Policy' = $Policy.IsDefault 'Scan URLs' = $Policy.ScanUrls 'Real-time Scan' = $Policy.DeliverMessageAfterScan 'Scan Internal Senders' = $Policy.EnableForInternalSenders 'Track Clicks' = $Policy.TrackClicks 'Allow Click-through' = $Policy.AllowClickThrough 'Disable URL Rewrite' = $Policy.DisableUrlRewrite 'Safe Links for Teams' = $Policy.EnableSafeLinksForTeams 'Safe Links for Office' = $Policy.EnableSafeLinksForO365Clients 'DoNotRewrite URL Count' = $DoNotRewriteCount } $SLObj.Add([pscustomobject](ConvertTo-HashToYN $slInObj)) | Out-Null } $null = (& { if ($HealthCheck.ExchangeOnline.SafeLinks) { $null = ($SLObj | Where-Object { $_.'Enabled' -eq 'No' } | Set-Style -Style Warning | Out-Null) $null = ($SLObj | Where-Object { $_.'Scan URLs' -eq 'No' } | Set-Style -Style Warning | Out-Null) $null = ($SLObj | Where-Object { $_.'DoNotRewrite URL Count' -ne '--' -and [int]$_.'DoNotRewrite URL Count' -gt 10 } | Set-Style -Style Warning | Out-Null) $null = ($SLObj | Where-Object { $_.'Allow Click-through' -eq 'Yes' } | Set-Style -Style Warning | Out-Null) } }) $SLTableParams = @{ Name = "Safe Links Policies - $TenantId"; List = $false; ColumnWidths = 14, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8 } if ($Report.ShowTableCaptions) { $SLTableParams['Caption'] = "- $($SLTableParams.Name)" } $SLObj | Table @SLTableParams $script:ExcelSheets['Safe Links'] = $SLObj # DoNotRewrite URLs detail if ($InfoLevel.SafeLinks -ge 2) { $AllDoNotRewrite = [System.Collections.ArrayList]::new() foreach ($Policy in $SafeLinksPolicies) { if ($Policy.DoNotRewriteUrls -and $Policy.DoNotRewriteUrls.Count -gt 0) { foreach ($Url in $Policy.DoNotRewriteUrls) { $null = $AllDoNotRewrite.Add([pscustomobject]@{ 'Policy' = $Policy.Name 'Excluded URL' = $Url }) } } } if ($AllDoNotRewrite.Count -gt 0) { Section -Style Heading3 'Safe Links URL Exclusions (DoNotRewriteUrls)' { Paragraph "WARNING: The following $($AllDoNotRewrite.Count) URL(s) are excluded from Safe Links scanning. Each exclusion represents a potential phishing risk." BlankLine $null = (& { if ($HealthCheck.ExchangeOnline.SafeLinks) { $null = ($AllDoNotRewrite | Set-Style -Style Warning | Out-Null) } }) $DnrTableParams = @{ Name = "Safe Links URL Exclusions - $TenantId"; List = $false; ColumnWidths = 35, 65 } if ($Report.ShowTableCaptions) { $DnrTableParams['Caption'] = "- $($DnrTableParams.Name)" } $AllDoNotRewrite | Table @DnrTableParams $script:ExcelSheets['Safe Links Exclusions'] = $AllDoNotRewrite } } } } else { Paragraph "No Safe Links policies found. Defender for Office 365 Plan 1 or higher is required." } } } catch { Write-ExoError 'SafeLinks' "Unable to retrieve Safe Links policies: $($_.Exception.Message)" Paragraph "Safe Links policies could not be retrieved. This feature requires Defender for Office 365 Plan 1." } #endregion #region ACSC E8 - Safe Attachments BlankLine Paragraph "ACSC Essential Eight Assessment" BlankLine $e8VarsSA = @{ SafeAttachPolicyCount = $SafeAttachPolicyCount SafeAttachBlockActionCount = $SafeAttachBlockActionCount SafeAttachForSPOEnabled = $SafeAttachForSPOEnabled } $e8ChecksSA = Build-AbrExoComplianceChecks -Definitions (Get-AbrExoE8Checks 'SafeAttachments') -Framework 'E8' -CallerVariables $e8VarsSA New-AbrExoE8AssessmentTable -Checks $e8ChecksSA -Name 'Safe Attachments' -TenantId $TenantId if ($e8ChecksSA) { foreach ($row in $e8ChecksSA) { $null = $script:E8AllChecks.Add([pscustomobject]@{ Section = 'Safe Attachments' ML = $row.ML Control = $row.Control Status = $row.Status Detail = $row.Detail }) } } #endregion #region ACSC E8 - Safe Links BlankLine Paragraph "ACSC Essential Eight Assessment" BlankLine $e8VarsSL = @{ SafeLinksPolicyCount = $SafeLinksPolicyCount SafeLinksDoNotRewriteUrlCount = $SafeLinksDoNotRewriteUrlCount SafeLinksRealTimeCount = $SafeLinksRealTimeCount } $e8ChecksSL = Build-AbrExoComplianceChecks -Definitions (Get-AbrExoE8Checks 'SafeLinks') -Framework 'E8' -CallerVariables $e8VarsSL New-AbrExoE8AssessmentTable -Checks $e8ChecksSL -Name 'Safe Links' -TenantId $TenantId if ($e8ChecksSL) { foreach ($row in $e8ChecksSL) { $null = $script:E8AllChecks.Add([pscustomobject]@{ Section = 'Safe Links' ML = $row.ML Control = $row.Control Status = $row.Status Detail = $row.Detail }) } } #endregion #region CIS - Safe Attachments BlankLine Paragraph "CIS Microsoft 365 Foundations Benchmark Assessment" BlankLine $cisVarsSA = @{ SafeAttachPolicyCount = $SafeAttachPolicyCount SafeAttachForSPOEnabled = $SafeAttachForSPOEnabled } $cisChecksSA = Build-AbrExoComplianceChecks -Definitions (Get-AbrExoCISChecks 'SafeAttachments') -Framework 'CIS' -CallerVariables $cisVarsSA New-AbrExoCISAssessmentTable -Checks $cisChecksSA -Name 'Safe Attachments' -TenantId $TenantId if ($cisChecksSA) { foreach ($row in $cisChecksSA) { $null = $script:CISAllChecks.Add([pscustomobject]@{ Section = 'Safe Attachments' CISControl = $row.CISControl Level = $row.Level Status = $row.Status Detail = $row.Detail }) } } #endregion #region CIS - Safe Links BlankLine Paragraph "CIS Microsoft 365 Foundations Benchmark Assessment" BlankLine $cisVarsSL = @{ SafeLinksPolicyCount = $SafeLinksPolicyCount SafeLinksDoNotRewriteUrlCount = $SafeLinksDoNotRewriteUrlCount } $cisChecksSL = Build-AbrExoComplianceChecks -Definitions (Get-AbrExoCISChecks 'SafeLinks') -Framework 'CIS' -CallerVariables $cisVarsSL New-AbrExoCISAssessmentTable -Checks $cisChecksSL -Name 'Safe Links' -TenantId $TenantId if ($cisChecksSL) { foreach ($row in $cisChecksSL) { $null = $script:CISAllChecks.Add([pscustomobject]@{ Section = 'Safe Links' CISControl = $row.CISControl Level = $row.Level Status = $row.Status Detail = $row.Detail }) } } #endregion } } end { Show-AbrDebugExecutionTime -End -TitleMessage 'SafeAttachmentsSafeLinks' } } |