Private/Value.ps1

function Test-HLStrictEquality {
    [CmdletBinding()]
    param(
        [AllowNull()]
        [object]$Actual,

        [AllowNull()]
        [object]$Expected
    )

    if ($null -eq $Actual -and $null -eq $Expected) { return $true }
    if ($null -eq $Actual -or $null -eq $Expected) { return $false }

    if ($Actual -is [bool] -or $Expected -is [bool]) {
        $actualBool = ConvertTo-HLBoolean -Value $Actual
        $expectedBool = ConvertTo-HLBoolean -Value $Expected
        return ($null -ne $actualBool) -and ($null -ne $expectedBool) -and ($actualBool -eq $expectedBool)
    }

    $numericTypes = @([byte], [sbyte], [int16], [uint16], [int32], [uint32], [int64], [uint64], [single], [double], [decimal])
    $actualIsNumeric = @($numericTypes | Where-Object { $Actual -is $_ }).Count -gt 0
    $expectedIsNumeric = @($numericTypes | Where-Object { $Expected -is $_ }).Count -gt 0
    if ($actualIsNumeric -and $expectedIsNumeric) {
        return [double]$Actual -eq [double]$Expected
    }
    if ($actualIsNumeric -or $expectedIsNumeric) {
        # Registry policies occasionally store numbers as strings; accept a
        # culture-invariant numeric string, but no other cross-type coercion.
        $text = if ($actualIsNumeric) { $Expected } else { $Actual }
        $number = if ($actualIsNumeric) { $Actual } else { $Expected }
        $parsed = 0.0
        if ([double]::TryParse([string]$text, [Globalization.NumberStyles]::Number, [Globalization.CultureInfo]::InvariantCulture, [ref]$parsed)) {
            return $parsed -eq [double]$number
        }
        return $false
    }

    if ($Actual -is [string] -and $Expected -is [string]) {
        return [string]::Equals([string]$Actual, [string]$Expected, [StringComparison]::OrdinalIgnoreCase)
    }

    return $Actual.GetType() -eq $Expected.GetType() -and $Actual -eq $Expected
}

function Test-HLValue {
    [CmdletBinding()]
    param(
        [AllowNull()]
        [object]$Actual,

        [AllowNull()]
        [object]$Expected,

        [Parameter(Mandatory)]
        [ValidateSet('Equals', 'NotEquals', 'In', 'NotIn', 'GreaterOrEqual', 'LessOrEqual', 'ContainsAll')]
        [string]$Operator
    )

    switch ($Operator) {
        'Equals' {
            return Test-HLStrictEquality -Actual $Actual -Expected $Expected
        }
        'NotEquals' {
            return -not (Test-HLStrictEquality -Actual $Actual -Expected $Expected)
        }
        'In' {
            return @(@($Expected) | Where-Object { Test-HLStrictEquality -Actual $Actual -Expected $_ }).Count -gt 0
        }
        'NotIn' {
            return @(@($Expected) | Where-Object { Test-HLStrictEquality -Actual $Actual -Expected $_ }).Count -eq 0
        }
        'GreaterOrEqual' {
            if ($null -eq $Actual) { return $false }
            return [double]$Actual -ge [double]$Expected
        }
        'LessOrEqual' {
            if ($null -eq $Actual) { return $false }
            return [double]$Actual -le [double]$Expected
        }
        'ContainsAll' {
            $actualItems = @($Actual | ForEach-Object { [string]$_ })
            foreach ($item in @($Expected)) {
                if ([string]$item -notin $actualItems) {
                    return $false
                }
            }
            return $true
        }
    }
}

function Get-HLValueProbeResult {
    [CmdletBinding()]
    param(
        [AllowNull()]
        [object]$Actual,

        [AllowNull()]
        [object]$Expected,

        [Parameter(Mandatory)]
        [string]$Operator,

        [AllowNull()]
        [object[]]$WarningValues,

        [string]$SuccessMessage = 'The effective value matches the baseline.',

        [string]$FailureMessage = 'The effective value does not match the baseline.',

        [AllowNull()]
        [object]$Evidence = $null
    )

    if (Test-HLValue -Actual $Actual -Expected $Expected -Operator $Operator) {
        return Get-HLProbeResult -Status Pass -Expected $Expected -Actual $Actual -Message $SuccessMessage -Evidence $Evidence
    }

    if ($null -ne $WarningValues -and $Actual -in @($WarningValues)) {
        return Get-HLProbeResult -Status Warning -Expected $Expected -Actual $Actual -Message 'The control is in audit or warning mode and is not fully enforced.' -Evidence $Evidence
    }

    return Get-HLProbeResult -Status Fail -Expected $Expected -Actual $Actual -Message $FailureMessage -Evidence $Evidence
}