diff --git a/src/Convert/Public/ConvertFrom-Base64ToString.ps1 b/src/Convert/Public/ConvertFrom-Base64ToString.ps1 index 56bfb5f..49e3d69 100644 --- a/src/Convert/Public/ConvertFrom-Base64ToString.ps1 +++ b/src/Convert/Public/ConvertFrom-Base64ToString.ps1 @@ -85,11 +85,19 @@ function ConvertFrom-Base64ToString { $ptr = [ConvertCoreInterop]::base64_to_string($s, $Encoding) if ($ptr -eq $nullPtr) { - $errorMsg = GetRustError -DefaultMessage "Base64 decoding failed for encoding '$Encoding'" - throw $errorMsg + $rustError = GetRustError -DefaultMessage '' + if ($rustError -match 'Invalid UTF-8|Invalid ASCII|Invalid UTF-16|Invalid UTF-32') { + # Binary data - fall back to Latin-1 which can represent any byte + $bytes = [System.Convert]::FromBase64String($s) + [System.Text.Encoding]::GetEncoding('ISO-8859-1').GetString($bytes) + } elseif ($rustError) { + throw $rustError + } else { + throw "Base64 decoding failed for encoding '$Encoding'" + } + } else { + ConvertPtrToString -Ptr $ptr } - - ConvertPtrToString -Ptr $ptr } finally { if ($ptr -ne $nullPtr) { [ConvertCoreInterop]::free_string($ptr) diff --git a/src/Convert/Public/ConvertTo-String.ps1 b/src/Convert/Public/ConvertTo-String.ps1 index 9d8e18b..2718f6d 100644 --- a/src/Convert/Public/ConvertTo-String.ps1 +++ b/src/Convert/Public/ConvertTo-String.ps1 @@ -133,33 +133,38 @@ function ConvertTo-String { } process { - $splat = @{} switch ($PSCmdlet.ParameterSetName) { 'Base64String' { - $InputObject = $Base64EncodedString - $Function = 'ConvertFrom-Base64ToString' - $splat.Add('Encoding', $Encoding) - if ($Decompress) { - $splat.Add('Decompress', $true) + foreach ($b64 in $Base64EncodedString) { + try { + if ($Decompress) { + $b64 | ConvertFrom-Base64ToString -Encoding $Encoding -Decompress -ErrorAction Stop + } else { + try { + ConvertFrom-Base64ToString -String $b64 -Encoding $Encoding -ErrorAction Stop + } catch { + if ($_.Exception.Message -match 'does not represent valid .+ text') { + # Binary data - fall back to Latin-1 which can represent any byte + $bytes = [System.Convert]::FromBase64String($b64) + [System.Text.Encoding]::GetEncoding('ISO-8859-1').GetString($bytes) + } else { + throw + } + } + } + } catch { + Write-Error -ErrorRecord $_ -ErrorAction $userErrorActionPreference + } } - break } 'MemoryStream' { - $InputObject = $MemoryStream - $Function = 'ConvertFrom-MemoryStreamToString' - break + $MemoryStream | ConvertFrom-MemoryStreamToString -ErrorAction $userErrorActionPreference } 'Stream' { - $InputObject = $Stream - $Function = 'ConvertFrom-MemoryStreamToString' - break + $Stream | ConvertFrom-MemoryStreamToString -ErrorAction $userErrorActionPreference } } - - if ($InputObject) { - $InputObject | & $Function @splat -ErrorAction $userErrorActionPreference - } } } diff --git a/src/Tests/Unit/ConvertFrom-Base64ToString.Tests.ps1 b/src/Tests/Unit/ConvertFrom-Base64ToString.Tests.ps1 index 224143e..daa1eea 100644 --- a/src/Tests/Unit/ConvertFrom-Base64ToString.Tests.ps1 +++ b/src/Tests/Unit/ConvertFrom-Base64ToString.Tests.ps1 @@ -96,6 +96,26 @@ Describe -Name $function -Fixture { $base64 = 'SGVsbG8' { ConvertFrom-Base64ToString -String $base64 -Encoding 'UTF8' -ErrorAction Stop } | Should -Throw } + + It -Name 'Converts binary data (non-UTF8) without error' -Test { + $binaryBytes = [byte[]](0xA1, 0x59, 0xC0, 0xA5, 0xE4, 0x94, 0xFF, 0x00, 0x80) + $base64 = [System.Convert]::ToBase64String($binaryBytes) + + $result = ConvertFrom-Base64ToString -String $base64 + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType [string] + } + + It -Name 'Round-trips binary data through Latin-1 fallback' -Test { + $binaryBytes = [byte[]](0xA1, 0x59, 0xC0, 0xA5, 0xE4, 0x94, 0xFF, 0x00, 0x80) + $base64 = [System.Convert]::ToBase64String($binaryBytes) + + $resultString = ConvertFrom-Base64ToString -String $base64 + $resultBytes = [System.Text.Encoding]::GetEncoding('ISO-8859-1').GetBytes($resultString) + + $resultBytes | Should -Be $binaryBytes + } } Context -Name 'Error Handling' -Fixture { diff --git a/src/Tests/Unit/ConvertTo-String.Tests.ps1 b/src/Tests/Unit/ConvertTo-String.Tests.ps1 index 17867e0..098132e 100644 --- a/src/Tests/Unit/ConvertTo-String.Tests.ps1 +++ b/src/Tests/Unit/ConvertTo-String.Tests.ps1 @@ -36,6 +36,27 @@ Describe -Name $function -Fixture { $assertion | Should -BeExactly $String } + + It -Name 'Converts binary data (non-UTF8) without error' -Test { + # Binary data that is not valid UTF-8 (e.g., certificate/image data) + $binaryBytes = [byte[]](0xA1, 0x59, 0xC0, 0xA5, 0xE4, 0x94, 0xFF, 0x00, 0x80) + $base64 = [System.Convert]::ToBase64String($binaryBytes) + + $assertion = ConvertTo-String -Base64EncodedString $base64 + + $assertion | Should -Not -BeNullOrEmpty + $assertion | Should -BeOfType [string] + } + + It -Name 'Round-trips binary data through Latin-1 fallback' -Test { + $binaryBytes = [byte[]](0xA1, 0x59, 0xC0, 0xA5, 0xE4, 0x94, 0xFF, 0x00, 0x80) + $base64 = [System.Convert]::ToBase64String($binaryBytes) + + $resultString = ConvertTo-String -Base64EncodedString $base64 + $resultBytes = [System.Text.Encoding]::GetEncoding('ISO-8859-1').GetBytes($resultString) + + $resultBytes | Should -Be $binaryBytes + } } Context -Name 'MemoryStream input' -Fixture {