From 9b95739dbd2ad9d25e268ad8b51d4563ef05a03f Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 13 Oct 2021 14:12:58 -0700 Subject: [PATCH] Fix #721 --- release-notes/VERSION-2.x | 5 ++ .../core/json/ReaderBasedJsonParser.java | 7 +-- .../core/json/UTF8DataInputJsonParser.java | 5 +- .../core/json/UTF8StreamJsonParser.java | 15 ++--- .../read/NonStandardAposQuotedNamesTest.java | 58 +++++++++++++++++++ 5 files changed, 78 insertions(+), 12 deletions(-) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 1658e3d9b1..a61ba2ac14 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -14,6 +14,11 @@ JSON library. === Releases === ------------------------------------------------------------------------ +2.12.6 (not yet released) + +#713: Incorrect parsing of single-quoted surrounded String values containing double quotes + (reported by wcarmon@github) + 2.12.5 (27-Aug-2021) #712: (partial) Optimize array allocation by `JsonStringEncoder` diff --git a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java index 3427c62662..2218aa4767 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java @@ -1969,10 +1969,9 @@ protected JsonToken _handleApos() throws IOException int i = (int) c; if (i <= '\\') { if (i == '\\') { - /* Although chars outside of BMP are to be escaped as - * an UTF-16 surrogate pair, does that affect decoding? - * For now let's assume it does not. - */ + // Although chars outside of BMP are to be escaped as + // an UTF-16 surrogate pair, does that affect decoding? + // For now let's assume it does not. c = _decodeEscaped(); } else if (i <= '\'') { if (i == '\'') { diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java index a4c744780c..3e1ec00395 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java @@ -2125,7 +2125,10 @@ protected JsonToken _handleApos() throws IOException if (c == '\'') { break main_loop; } - if (codes[c] != 0) { + if ((codes[c] != 0) + // 13-Oct-2021, tatu: [core#721] Alas, regular quote is included as + // special, need to ignore here + && (c != INT_QUOTE)) { break ascii_loop; } outBuf[outPtr++] = (char) c; diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java index 9f4e2dc1bd..910c6cb1db 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java @@ -2139,7 +2139,7 @@ protected String _parseAposName() throws IOException break; } // additional check to skip handling of double-quotes - if ((codes[ch] != 0) && (ch != '"')) { + if ((codes[ch] != 0) && (ch != INT_QUOTE)) { if (ch != '\\') { // Unquoted white space? // As per [JACKSON-208], call can now return: @@ -2724,18 +2724,19 @@ protected JsonToken _handleApos() throws IOException } while (_inputPtr < max) { c = (int) inputBuffer[_inputPtr++] & 0xFF; - if (c == INT_APOS || codes[c] != 0) { + if (c == INT_APOS) { + break main_loop; + } + if ((codes[c] != 0) + // 13-Oct-2021, tatu: [core#721] Alas, regular quote is included as + // special, need to ignore here + && (c != INT_QUOTE)) { break ascii_loop; } outBuf[outPtr++] = (char) c; } } - // Ok: end marker, escape or multi-byte? - if (c == INT_APOS) { - break main_loop; - } - switch (codes[c]) { case 1: // backslash c = _decodeEscaped(); diff --git a/src/test/java/com/fasterxml/jackson/core/read/NonStandardAposQuotedNamesTest.java b/src/test/java/com/fasterxml/jackson/core/read/NonStandardAposQuotedNamesTest.java index 89613a9195..619cd90f57 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/NonStandardAposQuotedNamesTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/NonStandardAposQuotedNamesTest.java @@ -167,4 +167,62 @@ private void _testSingleQuotesEscaped(int mode) throws Exception assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); } + + // [core#721]: specific issue with enclosed unescaped double quotes + public void testSingleQuotedKeys721() throws Exception + { + // empty + _testSingleQuotedKeys721("{ '\"\"': 'value'}", "\"\""); + // non-empty + _testSingleQuotedKeys721("{ '\"key\"': 'value'}", "\"key\""); + } + + private void _testSingleQuotedKeys721(String doc, String expKey) throws Exception + { + _testSingleQuotedKeys721(MODE_READER, doc, expKey); + _testSingleQuotedKeys721(MODE_INPUT_STREAM, doc, expKey); + _testSingleQuotedKeys721(MODE_INPUT_STREAM_THROTTLED, doc, expKey); + _testSingleQuotedKeys721(MODE_DATA_INPUT, doc, expKey); + } + + private void _testSingleQuotedKeys721(int mode, String doc, String expKey) throws Exception + { + JsonParser p = createParser(APOS_F, mode, doc); + + assertToken(JsonToken.START_OBJECT, p.nextToken()); + assertEquals(expKey, p.nextFieldName()); + assertToken(JsonToken.VALUE_STRING, p.nextToken()); + assertEquals("value", p.getText()); + assertToken(JsonToken.END_OBJECT, p.nextToken()); + p.close(); + } + + // [core#721]: specific issue with enclosed unescaped double quotes + public void testSingleQuotedValues721() throws Exception + { + // empty + _testSingleQuotedValues721("{ \"bar\": '\"\"'}", "\"\""); + // non-empty + _testSingleQuotedValues721("{ \"bar\": '\"stuff\"'}", "\"stuff\""); + } + + private void _testSingleQuotedValues721(String doc, String expValue) throws Exception + { + _testSingleQuotedValues721(MODE_READER, doc, expValue); + _testSingleQuotedValues721(MODE_INPUT_STREAM, doc, expValue); + _testSingleQuotedValues721(MODE_INPUT_STREAM_THROTTLED, doc, expValue); + _testSingleQuotedValues721(MODE_DATA_INPUT, doc, expValue); + } + + private void _testSingleQuotedValues721(int mode, String doc, String expValue) throws Exception + { + JsonParser p = createParser(APOS_F, mode, doc); + + assertToken(JsonToken.START_OBJECT, p.nextToken()); + assertEquals("bar", p.nextFieldName()); + assertToken(JsonToken.VALUE_STRING, p.nextToken()); + assertEquals(expValue, p.getText()); + assertToken(JsonToken.END_OBJECT, p.nextToken()); + p.close(); + } }