public/cis/Test-MtCisSafeLink.ps1

<#
.SYNOPSIS
    Checks if safe links for office applications are Enabled

.DESCRIPTION
    Safe links should be enabled for office applications (Exchange Teams Office 365 Apps)
    CIS Microsoft 365 Foundations Benchmark v5.0.0

.EXAMPLE
    Test-MtCisSafeLink

    Returns true safe links are enabled

.LINK
    https://maester.dev/docs/commands/Test-MtCisSafeLink
#>

function Test-MtCisSafeLink {
    [CmdletBinding()]
    [OutputType([bool])]
    param()

    if (!(Test-MtConnection ExchangeOnline)) {
        Add-MtTestResultDetail -SkippedBecause NotConnectedExchange
        return $null
    } elseif (!(Test-MtConnection SecurityCompliance)) {
        Add-MtTestResultDetail -SkippedBecause NotConnectedSecurityCompliance
        return $null
    } elseif ('P1' -notin (Get-MtLicenseInformation -Product MdoV2)) {
        Add-MtTestResultDetail -SkippedBecause NotLicensedMdoP1
        return $null
    }

    try {
        Write-Verbose 'Getting Safe Links Policy...'

        # Get the name of highest priority policy
        $priority0Policy = Get-MtExo -Request SafeLinksRule | Where-Object { $_.Priority -eq '0' }

        # Get policy highest priority policy
        $policy = Get-MtExo -Request SafeLinksPolicy | Where-Object { $_.Name -eq $priority0Policy }

        $safeLinkCheckList = @()

        #EnableSafeLinksForEmail
        $safeLinkCheckList += [pscustomobject] @{
            'CheckName' = 'EnableSafeLinksForEmail'
            'Value'     = 'True'
        }

        #EnableSafeLinksForTeams
        $safeLinkCheckList += [pscustomobject] @{
            'CheckName' = 'EnableSafeLinksForTeams'
            'Value'     = 'True'
        }

        #EnableSafeLinksForOffice
        $safeLinkCheckList += [pscustomobject] @{
            'CheckName' = 'EnableSafeLinksForOffice'
            'Value'     = 'True'
        }

        #TrackClicks
        $safeLinkCheckList += [pscustomobject] @{
            'CheckName' = 'TrackClicks'
            'Value'     = 'True'
        }

        #AllowClickThrough
        $safeLinkCheckList += [pscustomobject] @{
            'CheckName' = 'AllowClickThrough'
            'Value'     = 'False'
        }

        #ScanUrls
        $safeLinkCheckList += [pscustomobject] @{
            'CheckName' = 'ScanUrls'
            'Value'     = 'True'
        }

        #EnableForInternalSenders
        $safeLinkCheckList += [pscustomobject] @{
            'CheckName' = 'EnableForInternalSenders'
            'Value'     = 'True'
        }

        #DeliverMessageAfterScan
        $safeLinkCheckList += [pscustomobject] @{
            'CheckName' = 'DeliverMessageAfterScan'
            'Value'     = 'True'
        }

        #DisableUrlRewrite
        $safeLinkCheckList += [pscustomobject] @{
            'CheckName' = 'DisableUrlRewrite'
            'Value'     = 'True'
        }

        Write-Verbose 'Executing checks'
        $failedCheckList = @()
        foreach ($check in $safeLinkCheckList) {
            $checkResult = $policy | Where-Object { $_.($check.CheckName) -notmatch $check.Value }
            if ($checkResult) {
                #If the check fails, add it to the list so we can report on it later
                $failedCheckList += $check.CheckName
            }
        }

        $testResult = ($failedCheckList | Measure-Object).Count -eq 0

        $portalLink = 'https://security.microsoft.com/presetSecurityPolicies'

        if ($testResult) {
            $testResultMarkdown = 'Well done. Safe link policy' + $priority0Policy.Name + " (Priority 0 policy) matches CIS recommendations ($portalLink).`n`n%TestResult%"
        } else {
            $testResultMarkdown = 'Safe link policy' + $priority0Policy.Name + " (Priority 0 policy) does not match CIS recommendations ($portalLink).`n`n%TestResult%"
        }

        $resultMd = "| Check Name | Result |`n"
        $resultMd += "| --- | --- |`n"
        foreach ($item in $safeLinkCheckList) {
            $itemResult = '❌ Fail'
            if ($item.CheckName -notin $failedCheckList) {
                $itemResult = '✅ Pass'
            }
            $resultMd += "| $($item.CheckName) | $($itemResult) |`n"
        }

        $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $resultMd

        Add-MtTestResultDetail -Result $testResultMarkdown
        return $testResult
    } catch {
        Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_
        return $null
    }
}