Skip to content

Commit 36622ec

Browse files
committed
Fix base64Binary decoding for unpadded and whitespace-containing input
The PSL Base64 decoder is stricter than what XSD base64Binary requires: - It rejects whitespace (valid per RFC 2045 / XSD lexical space) - It requires explicit padding (many SOAP services omit trailing '=') Strip whitespace before decoding and disable explicit padding requirement. Uses positional arguments for PSL cross-version compatibility. Fixes #46
1 parent 14f369f commit 36622ec

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

src/Encoder/SimpleType/Base64BinaryTypeEncoder.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33

44
namespace Soap\Encoding\Encoder\SimpleType;
55

6+
use Psl\Encoding\Base64\Variant;
7+
use Psl\Regex;
68
use Soap\Encoding\Encoder\Context;
79
use Soap\Encoding\Encoder\XmlEncoder;
8-
use Soap\Encoding\Restriction\WhitespaceRestriction;
910
use VeeWee\Reflecta\Iso\Iso;
1011
use function Psl\Encoding\Base64\decode;
1112
use function Psl\Encoding\Base64\encode;
@@ -22,7 +23,7 @@ public function iso(Context $context): Iso
2223
{
2324
return (new Iso(
2425
static fn (string $value): string => encode($value),
25-
static fn (string $value): string => WhitespaceRestriction::collapse(decode($value)),
26+
static fn (string $value): string => decode(Regex\replace($value, '/\s+/', ''), Variant::Standard, false),
2627
));
2728
}
2829
}

tests/Unit/Encoder/SimpleType/Base64BinaryTypeEncoderTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
namespace Soap\Encoding\Test\Unit\Encoder\SimpleType;
55

66
use PHPUnit\Framework\Attributes\CoversClass;
7+
use PHPUnit\Framework\Attributes\DataProvider;
78
use Soap\Encoding\Encoder\SimpleType\Base64BinaryTypeEncoder;
89
use Soap\Encoding\Test\Unit\Encoder\AbstractEncoderTests;
910
use Soap\Engine\Metadata\Model\XsdType;
@@ -24,4 +25,43 @@ public static function provideIsomorphicCases(): iterable
2425
'data' => 'hello',
2526
];
2627
}
28+
29+
/**
30+
* @return iterable<string, array{xml: string, data: string}>
31+
*/
32+
public static function provideBase64WithWhitespaceCases(): iterable
33+
{
34+
$data = 'hello world, this is a longer string for base64 wrapping';
35+
$encoded = base64_encode($data);
36+
37+
yield 'with-line-breaks' => [
38+
'xml' => chunk_split($encoded, 76, "\r\n"),
39+
'data' => $data,
40+
];
41+
42+
yield 'with-spaces' => [
43+
'xml' => chunk_split($encoded, 20, " "),
44+
'data' => $data,
45+
];
46+
47+
yield 'with-mixed-whitespace' => [
48+
'xml' => chunk_split($encoded, 30, "\n\t"),
49+
'data' => $data,
50+
];
51+
52+
yield 'without-padding' => [
53+
'xml' => rtrim(base64_encode('hello world'), '='),
54+
'data' => 'hello world',
55+
];
56+
}
57+
58+
#[DataProvider('provideBase64WithWhitespaceCases')]
59+
public function test_it_can_decode_base64_with_whitespace(string $xml, string $data): void
60+
{
61+
$encoder = new Base64BinaryTypeEncoder();
62+
$context = self::createContext(XsdType::guess('base64Binary'));
63+
$iso = $encoder->iso($context);
64+
65+
static::assertEquals($data, $iso->from($xml));
66+
}
2767
}

0 commit comments

Comments
 (0)