SHELL/2.1.11.ps1

$CheckId = "2.1.11"
$Title = "Ensure comprehensive attachment filtering is applied"
$Level = "L2"
$BenchmarkType = "Automated"

try {
    $AttachExts = @(
        "7z", "a3x", "ace", "ade", "adp", "ani", "app", "appinstaller",
        "applescript", "application", "appref-ms", "appx", "appxbundle", "arj",
        "asd", "asx", "bas", "bat", "bgi", "bz2", "cab", "chm", "cmd", "com",
        "cpl", "crt", "cs", "csh", "daa", "dbf", "dcr", "deb",
        "desktopthemepackfile", "dex", "diagcab", "dif", "dir", "dll", "dmg",
        "doc", "docm", "dot", "dotm", "elf", "eml", "exe", "fxp", "gadget", "gz",
        "hlp", "hta", "htc", "htm", "html", "hwpx", "ics", "img",
        "inf", "ins", "iqy", "iso", "isp", "jar", "jnlp", "js", "jse", "kext",
        "ksh", "lha", "lib", "library-ms", "lnk", "lzh", "macho", "mam", "mda",
        "mdb", "mde", "mdt", "mdw", "mdz", "mht", "mhtml", "mof", "msc", "msi",
        "msix", "msp", "msrcincident", "mst", "ocx", "odt", "ops", "oxps", "pcd",
        "pif", "plg", "pot", "potm", "ppa", "ppam", "ppkg", "pps", "ppsm", "ppt",
        "pptm", "prf", "prg", "ps1", "ps11", "ps11xml", "ps1xml", "ps2",
        "ps2xml", "psc1", "psc2", "pub", "py", "pyc", "pyo", "pyw", "pyz",
        "pyzw", "rar", "reg", "rev", "rtf", "scf", "scpt", "scr", "sct",
        "searchConnector-ms", "service", "settingcontent-ms", "sh", "shb", "shs",
        "shtm", "shtml", "sldm", "slk", "so", "spl", "stm", "svg", "swf", "sys",
        "tar", "theme", "themepack", "timer", "uif", "url", "uue", "vb", "vbe",
        "vbs", "vhd", "vhdx", "vxd", "wbk", "website", "wim", "wiz", "ws", "wsc",
        "wsf", "wsh", "xla", "xlam", "xlc", "xll", "xlm", "xls", "xlsb", "xlsm",
        "xlt", "xltm", "xlw", "xnk", "xps", "xsl", "xz", "z"
    )

    $MalwareFilterPolicies = @(Get-MalwareFilterPolicy)
    $MalwareFilterRules = @(Get-MalwareFilterRule)

    $PassingValue = 0.90
    $FailThreshold = [int]($AttachExts.Count * (1 - $PassingValue))

    # CIS text evaluates only policies that define >120 extensions.
    $CompPolicies = @($MalwareFilterPolicies | Where-Object { @($_.FileTypes).Count -gt 120 })

    if (-not $CompPolicies) {
        [pscustomobject]@{
            CheckId   = $CheckId
            Title     = $Title
            Level     = $Level
            Status    = "FAIL"
            Pass      = $false
            Evidence  = [pscustomobject]@{
                AttachExtensionCount = $AttachExts.Count
                PassingCoveragePct   = 90
                MaxMissingAllowed    = $FailThreshold
                ComprehensivePoliciesFound = 0
                PolicyReport = @()
                SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1"
            }
            Error     = "No comprehensive malware filter policy found (requires >120 extensions)."
            Timestamp = Get-Date
        }
        return
    }

    $ExtensionReport = foreach ($Policy in $CompPolicies) {
        $Missing = Compare-Object -ReferenceObject $AttachExts -DifferenceObject @($Policy.FileTypes) -PassThru |
            Where-Object { $_.SideIndicator -eq "<=" }

        $FoundRules = @($MalwareFilterRules | Where-Object {
            $_.MalwareFilterPolicy -eq $Policy.Id -or
            $_.MalwareFilterPolicy -eq $Policy.Name -or
            $_.MalwareFilterPolicy -eq $Policy.Identity
        })

        $EnabledRules = @($FoundRules | Where-Object {
            $_.State -eq "Enabled" -or $_.Enabled -eq $true
        })

        $HasEnabledRule = @($EnabledRules).Count -gt 0
        $PolicyPass = (@($Missing).Count -lt $FailThreshold) -and
            $HasEnabledRule -and
            ($Policy.EnableFileFilter -eq $true)

        [pscustomobject]@{
            PolicyName              = $Policy.Identity
            IsCISCompliant          = $PolicyPass
            EnableFileFilter        = [bool]$Policy.EnableFileFilter
            RuleState               = @($EnabledRules | Select-Object -ExpandProperty State -Unique)
            RuleCount               = @($FoundRules).Count
            EnabledRuleCount        = @($EnabledRules).Count
            MissingCount            = @($Missing).Count
            MissingExtensions       = @($Missing)
            ExtensionCount          = @($Policy.FileTypes).Count
            CoveragePercent         = [math]::Round(((@($AttachExts).Count - @($Missing).Count) * 100.0) / @($AttachExts).Count, 2)
        }
    }

    $CompliantPolicies = @($ExtensionReport | Where-Object { $_.IsCISCompliant -eq $true })
    # CIS pass condition states a single active compliant policy.
    $Pass = @($CompliantPolicies).Count -eq 1

    $FailureReasons = @()
    if (@($CompliantPolicies).Count -eq 0) {
        $FailureReasons += "No compliant active comprehensive policy found."
    }
    elseif (@($CompliantPolicies).Count -gt 1) {
        $FailureReasons += "Multiple compliant active comprehensive policies found; CIS expects a single active policy."
    }

    [pscustomobject]@{
        CheckId   = $CheckId
        Title     = $Title
        Level     = $Level
        BenchmarkType = $BenchmarkType
        Status    = if ($Pass) { "PASS" } else { "FAIL" }
        Pass      = $Pass
        Evidence  = [pscustomobject]@{
            AttachExtensionCount      = $AttachExts.Count
            PassingCoveragePct        = 90
            MaxMissingAllowed         = $FailThreshold
            ComprehensivePoliciesFound = @($CompPolicies).Count
            CompliantPolicyCount      = @($CompliantPolicies).Count
            PolicyReport              = @($ExtensionReport)
            SourceDocument            = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1"
        }
        Error     = if ($Pass) { $null } else { ($FailureReasons -join " ") }
        Timestamp = Get-Date
    }
}
catch {
    [pscustomobject]@{
        CheckId   = $CheckId
        Title     = $Title
        Level     = $Level
        BenchmarkType = $BenchmarkType
        Status    = "ERROR"
        Pass      = $null
        Evidence  = [pscustomobject]@{
            SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1"
        }
        Error     = $_.Exception.Message
        Timestamp = Get-Date
    }
}