Checks/MDO/MET-MDO001-SafeLinks.ps1
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'METCheckInfo', Justification = 'Check metadata. Read from the AST by Get-METCheck and never executed.')] param() $METCheckInfo = @{ Name = 'Safe Links Effective Coverage' Severity = 'High' Description = 'Resolves the precedence-winning Safe Links policy per mailbox and flags recipients whose effective policy disables Safe Links for email or Office apps, or has TrackClicks/EnableForInternalSenders/real-time scanning misconfigured.' RequiresModule = @('ExchangeOnlineManagement') } $allMailboxes = $null if ($METContext -and $METContext.AllMailboxes) { $allMailboxes = @($METContext.AllMailboxes) } else { try { $allMailboxes = @(Get-METAssessableMailboxes) if ($METContext) { $METContext.AllMailboxes = $allMailboxes } } catch { New-METCheckResult -CheckId 'MET-MDO001' -Category MDO -Name 'Safe Links Effective Coverage' ` -Result Warning -Severity High -AffectedObject 'All Mailboxes' ` -Finding 'Unable to determine effective Safe Links coverage because the mailbox list could not be retrieved.' ` -Recommendation 'Ensure the account has Exchange View-Only Recipients permission and rerun the assessment.' ` -ReferenceUrl 'https://aka.ms/mdo-safelinks' -ErrorMessage $_.ToString() return } } if ($allMailboxes.Count -eq 0) { New-METCheckResult -CheckId 'MET-MDO001' -Category MDO -Name 'Safe Links Effective Coverage' ` -Result NotApplicable -Severity High -AffectedObject 'Tenant (0 mailboxes)' ` -Finding 'No assessable mailboxes were found in the tenant.' ` -ReferenceUrl 'https://aka.ms/mdo-safelinks' return } function Add-METSafeLinksPropertyIssue { param( [Parameter(Mandatory)] [AllowEmptyCollection()] [System.Collections.Generic.List[string]] $Issues, [Parameter(Mandatory)] [object] $Policy, [Parameter(Mandatory)] [string] $PropertyName, [Parameter(Mandatory)] [string] $NotEstablishedText, [Parameter(Mandatory)] [string] $FalseText, [switch] $InsecureWhenTrue ) # A property that is absent, or present but $null, was never observed. Reading # it as $false (or, for the insecure-when-true properties, as $true) would # fabricate a verdict this check did not establish. $property = $Policy.PSObject.Properties[$PropertyName] if (-not $property -or $null -eq $property.Value) { $Issues.Add($NotEstablishedText) return } $isIssue = if ($InsecureWhenTrue) { [bool]$property.Value } else { -not $property.Value } if ($isIssue) { $Issues.Add($FalseText) } } $evaluate = { param($Policy, $PolicyType) $issues = [System.Collections.Generic.List[string]]::new() if (-not $Policy) { $issues.Add('Policy settings could not be retrieved') return $issues.ToArray() } Add-METSafeLinksPropertyIssue -Issues $issues -Policy $Policy -PropertyName 'EnableSafeLinksForEmail' ` -NotEstablishedText 'EnableSafeLinksForEmail was not returned for this policy, so whether Safe Links is enabled for email was not established' ` -FalseText 'Safe Links for email is disabled' Add-METSafeLinksPropertyIssue -Issues $issues -Policy $Policy -PropertyName 'EnableSafeLinksForOffice' ` -NotEstablishedText 'EnableSafeLinksForOffice was not returned for this policy, so whether Safe Links is enabled for Office apps was not established' ` -FalseText 'Safe Links for Office apps is disabled' Add-METSafeLinksPropertyIssue -Issues $issues -Policy $Policy -PropertyName 'TrackClicks' ` -NotEstablishedText 'TrackClicks was not returned for this policy, so whether click tracking is enabled was not established' ` -FalseText 'Click tracking is disabled' Add-METSafeLinksPropertyIssue -Issues $issues -Policy $Policy -PropertyName 'EnableForInternalSenders' ` -NotEstablishedText 'EnableForInternalSenders was not returned for this policy, so whether the policy applies to internal senders was not established' ` -FalseText 'Not applied to internal senders' Add-METSafeLinksPropertyIssue -Issues $issues -Policy $Policy -PropertyName 'ScanUrls' ` -NotEstablishedText 'ScanUrls was not returned for this policy, so whether real-time URL scanning is enabled was not established' ` -FalseText 'Real-time URL scanning is disabled' Add-METSafeLinksPropertyIssue -Issues $issues -Policy $Policy -PropertyName 'DeliverMessageAfterScan' ` -NotEstablishedText 'DeliverMessageAfterScan was not returned for this policy, so whether messages are held until the URL scan completes was not established' ` -FalseText 'Messages delivered before URL scan completes' Add-METSafeLinksPropertyIssue -Issues $issues -Policy $Policy -PropertyName 'AllowClickThrough' ` -NotEstablishedText 'AllowClickThrough was not returned for this policy, so whether users can click through to blocked URLs was not established' ` -FalseText 'Users can click through to blocked URLs' -InsecureWhenTrue if ($PolicyType -ne 'BuiltIn') { Add-METSafeLinksPropertyIssue -Issues $issues -Policy $Policy -PropertyName 'DisableURLRewrite' ` -NotEstablishedText 'DisableURLRewrite was not returned for this policy, so whether URL rewriting is disabled was not established' ` -FalseText 'URL rewriting is disabled' -InsecureWhenTrue } $issues.ToArray() } $groupCache = if ($METContext -and $METContext.GroupMembers) { $METContext.GroupMembers } else { @{} } $retrievalErrors = [System.Collections.Generic.List[string]]::new() if ($METContext -and $METContext.SafeLinksResolution) { $resolution = $METContext.SafeLinksResolution $retrievalErrors.AddRange([string[]]@($METContext.SafeLinksRetrievalErrors)) } else { $resolution = Resolve-METSafeLinksEffectivePolicy -AllMailboxes $allMailboxes ` -GroupCache $groupCache -RetrievalErrors $retrievalErrors if ($METContext) { $METContext.SafeLinksResolution = $resolution $METContext.SafeLinksRetrievalErrors = @($retrievalErrors) } } New-METEffectivePolicyCoverageResult -CheckId 'MET-MDO001' -Name 'Safe Links Effective Coverage' ` -ProtectionType 'Safe Links' -Severity High -Subjects $allMailboxes -Resolution $resolution ` -GetPolicyIssues $evaluate -RetrievalErrors $retrievalErrors -ReferenceUrl 'https://aka.ms/mdo-safelinks' ` -Recommendation 'Assign recipients to a Standard/Strict preset or a compliant custom Safe Links policy. Fix the effective policy for each affected recipient; unused shadowed policies do not affect this result.' |