Private/Suppression.ps1

function Get-SuppressionMap {
    <#
    .SYNOPSIS
        Reads suppression comments from the token stream.
    .DESCRIPTION
        Supported comments (rule ids are optional and comma separated):
          # m365ds-ignore-file[: ID1, ID2] whole file
          # m365ds-ignore-next-line[: ID1, ID2] the line after the comment
          # m365ds-ignore[: ID1, ID2] the line the comment is on
        Without ids, every rule is suppressed.
    #>

    [CmdletBinding()]
    param(
        [AllowNull()]
        [System.Management.Automation.Language.Token[]]$Tokens
    )

    $map = @{
        File  = New-Object System.Collections.Generic.List[string]
        Lines = @{}
    }
    if (-not $Tokens) {
        return $map
    }

    # The scope must be followed by a non-word character, so a typo such as 'm365ds-ignore-nextline'
    # does not silently fall back to 'ignore this line'.
    $pattern = 'm365ds-ignore(?<scope>-next-line|-file)?(?![\w-])(?:\s*:\s*(?<ids>[\w.-]+(?:\s*,\s*[\w.-]+)*))?'
    foreach ($token in $Tokens) {
        if ($token.Kind -ne [System.Management.Automation.Language.TokenKind]::Comment) {
            continue
        }
        foreach ($match in [regex]::Matches($token.Text, $pattern, 'IgnoreCase')) {
            $ids = @('*')
            if ($match.Groups['ids'].Success) {
                $ids = @($match.Groups['ids'].Value -split '\s*,\s*' | ForEach-Object { $_.Trim().ToUpperInvariant() } | Where-Object { $_ })
            }

            switch ($match.Groups['scope'].Value.ToLowerInvariant()) {
                '-file' {
                    $map.File.AddRange([string[]]$ids)
                }
                '-next-line' {
                    Add-SuppressedLine -Map $map -Line ($token.Extent.EndLineNumber + 1) -Ids $ids
                }
                default {
                    Add-SuppressedLine -Map $map -Line $token.Extent.StartLineNumber -Ids $ids
                }
            }
        }
    }
    return $map
}

function Add-SuppressedLine {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [hashtable]$Map,

        [Parameter(Mandatory)]
        [int]$Line,

        [Parameter(Mandatory)]
        [string[]]$Ids
    )

    if (-not $Map.Lines.ContainsKey($Line)) {
        $Map.Lines[$Line] = New-Object System.Collections.Generic.List[string]
    }
    $Map.Lines[$Line].AddRange($Ids)
}

function Test-Suppressed {
    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter(Mandatory)]
        [hashtable]$SuppressionMap,

        [Parameter(Mandatory)]
        [int]$Line,

        [Parameter(Mandatory)]
        [string]$RuleId
    )

    $id = $RuleId.ToUpperInvariant()
    if ($SuppressionMap.File.Contains('*') -or $SuppressionMap.File.Contains($id)) {
        return $true
    }
    $lineIds = $SuppressionMap.Lines[$Line]
    return ($null -ne $lineIds) -and ($lineIds.Contains('*') -or $lineIds.Contains($id))
}