Private/ConvertFrom-InforcerBase64Text.ps1

function ConvertFrom-InforcerBase64Text {
    <#
    .SYNOPSIS
        Decodes a base64 string, returning it only when the result is real text.
    .DESCRIPTION
        Intune base64-encodes both text (scriptContent, rulesContent, the macOS/iOS
        .mobileconfig payload plist) and binary (hashedScriptContent digests, signed
        profiles) — so the property name alone is not a safe test. This decodes and
        verifies the bytes were text, returning $null otherwise so callers keep the
        base64 as-is rather than rendering mojibake.
    .PARAMETER Value
        The candidate base64 string.
    .OUTPUTS
        [string] The decoded text, or $null when the input isn't base64-encoded text.
    .EXAMPLE
        ConvertFrom-InforcerBase64Text -Value $policy.payload
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter()]
        [string]$Value
    )

    if ([string]::IsNullOrWhiteSpace($Value) -or $Value.Length -le 20 -or $Value -match '\s') { return $null }

    try { $bytes = [System.Convert]::FromBase64String($Value) } catch { return $null }
    if ($bytes.Length -eq 0) { return $null }

    # Strict decode: invalid UTF-8 throws instead of silently yielding U+FFFD.
    try { $text = [System.Text.UTF8Encoding]::new($false, $true).GetString($bytes) } catch { return $null }

    $text = $text.TrimStart([char]0xFEFF)

    # Strict decoding is necessary but not sufficient: EF BF BD is *valid* UTF-8 for U+FFFD,
    # and C2 80..C2 9F are valid encodings of the C1 controls, so a digest can survive it.
    if ($text.Contains([char]0xFFFD) -or $text -match '[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]') { return $null }

    $text
}