Skip to content

Commit c2b6942

Browse files
committed
Fixed #477
1 parent b9c7604 commit c2b6942

File tree

6 files changed

+90
-16
lines changed

6 files changed

+90
-16
lines changed

release-notes/VERSION-2.x

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ JSON library.
1414
=== Releases ===
1515
------------------------------------------------------------------------
1616

17+
2.9.7 (not yet released)
18+
19+
#477: Exception while decoding Base64 value with escaped `=` character
20+
1721
2.9.6 (12-Jun-2018)
1822

1923
#400: Add mechanism for forcing `BufferRecycler` released (to call on shutdown)

src/main/java/com/fasterxml/jackson/core/base/ParserBase.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -1029,7 +1029,9 @@ protected final int _decodeBase64Escape(Base64Variant b64variant, int ch, int in
10291029
// otherwise try to find actual triplet value
10301030
int bits = b64variant.decodeBase64Char(unescaped);
10311031
if (bits < 0) {
1032-
throw reportInvalidBase64Char(b64variant, unescaped, index);
1032+
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
1033+
throw reportInvalidBase64Char(b64variant, unescaped, index);
1034+
}
10331035
}
10341036
return bits;
10351037
}
@@ -1049,7 +1051,10 @@ protected final int _decodeBase64Escape(Base64Variant b64variant, char ch, int i
10491051
// otherwise try to find actual triplet value
10501052
int bits = b64variant.decodeBase64Char(unescaped);
10511053
if (bits < 0) {
1052-
throw reportInvalidBase64Char(b64variant, unescaped, index);
1054+
// second check since padding can only be 3rd or 4th byte (index #2 or #3)
1055+
if ((bits != Base64Variant.BASE64_VALUE_PADDING) || (index < 2)) {
1056+
throw reportInvalidBase64Char(b64variant, unescaped, index);
1057+
}
10531058
}
10541059
return bits;
10551060
}

src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,9 @@ protected int _readBinary(Base64Variant b64variant, OutputStream out, byte[] buf
563563
}
564564
ch = _inputBuffer[_inputPtr++];
565565
if (!b64variant.usesPaddingChar(ch)) {
566-
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
566+
if (_decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) {
567+
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
568+
}
567569
}
568570
// Got 12 bits, only need 8, need to shift
569571
decodedData >>= 4;
@@ -2714,7 +2716,9 @@ protected byte[] _decodeBase64(Base64Variant b64variant) throws IOException
27142716
}
27152717
ch = _inputBuffer[_inputPtr++];
27162718
if (!b64variant.usesPaddingChar(ch)) {
2717-
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
2719+
if (_decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) {
2720+
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
2721+
}
27182722
}
27192723
// Got 12 bits, only need 8, need to shift
27202724
decodedData >>= 4;

src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,10 @@ protected int _readBinary(Base64Variant b64variant, OutputStream out,
492492
// Ok, must get padding
493493
ch = _inputData.readUnsignedByte();
494494
if (!b64variant.usesPaddingChar(ch)) {
495-
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
495+
if ((ch != INT_BACKSLASH)
496+
|| _decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) {
497+
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
498+
}
496499
}
497500
// Got 12 bits, only need 8, need to shift
498501
decodedData >>= 4;
@@ -2763,7 +2766,10 @@ protected final byte[] _decodeBase64(Base64Variant b64variant) throws IOExceptio
27632766
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
27642767
ch = _inputData.readUnsignedByte();
27652768
if (!b64variant.usesPaddingChar(ch)) {
2766-
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
2769+
if ((ch != INT_BACKSLASH)
2770+
|| _decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) {
2771+
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
2772+
}
27672773
}
27682774
// Got 12 bits, only need 8, need to shift
27692775
decodedData >>= 4;

src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java

+10-10
Original file line numberDiff line numberDiff line change
@@ -487,18 +487,14 @@ public byte[] getBinaryValue(Base64Variant b64variant) throws IOException
487487
(_currToken != JsonToken.VALUE_EMBEDDED_OBJECT || _binaryValue == null)) {
488488
_reportError("Current token ("+_currToken+") not VALUE_STRING or VALUE_EMBEDDED_OBJECT, can not access as binary");
489489
}
490-
/* To ensure that we won't see inconsistent data, better clear up
491-
* state...
492-
*/
490+
// To ensure that we won't see inconsistent data, better clear up state...
493491
if (_tokenIncomplete) {
494492
try {
495493
_binaryValue = _decodeBase64(b64variant);
496494
} catch (IllegalArgumentException iae) {
497495
throw _constructError("Failed to decode VALUE_STRING as base64 ("+b64variant+"): "+iae.getMessage());
498496
}
499-
/* let's clear incomplete only now; allows for accessing other
500-
* textual content in error cases
501-
*/
497+
// let's clear incomplete only now; allows for accessing other textual content in error cases
502498
_tokenIncomplete = false;
503499
} else { // may actually require conversion...
504500
if (_binaryValue == null) {
@@ -602,7 +598,9 @@ protected int _readBinary(Base64Variant b64variant, OutputStream out,
602598
}
603599
ch = _inputBuffer[_inputPtr++] & 0xFF;
604600
if (!b64variant.usesPaddingChar(ch)) {
605-
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
601+
if (_decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) {
602+
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
603+
}
606604
}
607605
// Got 12 bits, only need 8, need to shift
608606
decodedData >>= 4;
@@ -3608,7 +3606,7 @@ protected final byte[] _decodeBase64(Base64Variant b64variant) throws IOExceptio
36083606
// First branch: can get padding (-> 1 byte)
36093607
if (bits < 0) {
36103608
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
3611-
// as per [JACKSON-631], could also just be 'missing' padding
3609+
// could also just be 'missing' padding
36123610
if (ch == '"' && !b64variant.usesPadding()) {
36133611
decodedData >>= 4;
36143612
builder.append(decodedData);
@@ -3623,7 +3621,9 @@ protected final byte[] _decodeBase64(Base64Variant b64variant) throws IOExceptio
36233621
}
36243622
ch = _inputBuffer[_inputPtr++] & 0xFF;
36253623
if (!b64variant.usesPaddingChar(ch)) {
3626-
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
3624+
if (_decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) {
3625+
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
3626+
}
36273627
}
36283628
// Got 12 bits, only need 8, need to shift
36293629
decodedData >>= 4;
@@ -3641,7 +3641,7 @@ protected final byte[] _decodeBase64(Base64Variant b64variant) throws IOExceptio
36413641
bits = b64variant.decodeBase64Char(ch);
36423642
if (bits < 0) {
36433643
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
3644-
// as per [JACKSON-631], could also just be 'missing' padding
3644+
// could also just be 'missing' padding
36453645
if (ch == '"' && !b64variant.usesPadding()) {
36463646
decodedData >>= 2;
36473647
builder.appendTwoBytes(decodedData);

src/test/java/com/fasterxml/jackson/core/base64/Base64BinaryParsingTest.java

+55
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ public void testWithEscaped() throws IOException {
5353
}
5454
}
5555

56+
public void testWithEscapedPadding() throws IOException {
57+
for (int mode : ALL_MODES) {
58+
_testEscapedPadding(mode);
59+
}
60+
}
61+
5662
public void testInvalidTokenForBase64() throws IOException
5763
{
5864
for (int mode : ALL_MODES) {
@@ -333,4 +339,53 @@ private void _testEscaped(int mode) throws IOException
333339
}
334340
p.close();
335341
}
342+
343+
private void _testEscapedPadding(int mode) throws IOException
344+
{
345+
// Input: "Test!" -> "VGVzdCE="
346+
final String DOC = quote("VGVzdCE\\u003d");
347+
348+
// 06-Sep-2018, tatu: actually one more, test escaping of padding
349+
JsonParser p = createParser(mode, DOC);
350+
assertToken(JsonToken.VALUE_STRING, p.nextToken());
351+
assertEquals("Test!", new String(p.getBinaryValue(), "US-ASCII"));
352+
if (mode != MODE_DATA_INPUT) {
353+
assertNull(p.nextToken());
354+
}
355+
p.close();
356+
357+
// also, try out alternate access method
358+
p = createParser(mode, DOC);
359+
assertToken(JsonToken.VALUE_STRING, p.nextToken());
360+
assertEquals("Test!", new String(_readBinary(p), "US-ASCII"));
361+
if (mode != MODE_DATA_INPUT) {
362+
assertNull(p.nextToken());
363+
}
364+
p.close();
365+
366+
// and then different padding; "X" -> "WA=="
367+
final String DOC2 = quote("WA\\u003D\\u003D");
368+
p = createParser(mode, DOC2);
369+
assertToken(JsonToken.VALUE_STRING, p.nextToken());
370+
assertEquals("X", new String(p.getBinaryValue(), "US-ASCII"));
371+
if (mode != MODE_DATA_INPUT) {
372+
assertNull(p.nextToken());
373+
}
374+
p.close();
375+
376+
p = createParser(mode, DOC2);
377+
assertToken(JsonToken.VALUE_STRING, p.nextToken());
378+
assertEquals("X", new String(_readBinary(p), "US-ASCII"));
379+
if (mode != MODE_DATA_INPUT) {
380+
assertNull(p.nextToken());
381+
}
382+
p.close();
383+
}
384+
385+
private byte[] _readBinary(JsonParser p) throws IOException
386+
{
387+
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
388+
p.readBinaryValue(bytes);
389+
return bytes.toByteArray();
390+
}
336391
}

0 commit comments

Comments
 (0)