From 0820067b089a1bce8f56c33a38c01c65a4ad1f8d Mon Sep 17 00:00:00 2001 From: Roberto Viani Junior Date: Sat, 5 Feb 2022 23:58:54 -0300 Subject: [PATCH 1/2] JwsJsonProducerTest escaping the string properly - the strings is now properly escaped --- .../json/basic/JsonMapObjectReaderWriter.java | 81 +++++++++++++------ 1 file changed, 55 insertions(+), 26 deletions(-) diff --git a/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java b/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java index 195b3275bde..4931ff5822a 100644 --- a/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java +++ b/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java @@ -22,6 +22,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.UncheckedIOException; +import java.nio.ByteBuffer; import java.util.*; import org.apache.cxf.common.util.StringUtils; @@ -30,7 +31,6 @@ public class JsonMapObjectReaderWriter { - private static final Set ESCAPED_CHARS; private static final char DQUOTE = '"'; private static final char COMMA = ','; private static final char COLON = ':'; @@ -41,18 +41,35 @@ public class JsonMapObjectReaderWriter { private static final char ESCAPE = '\\'; private static final String NULL_VALUE = "null"; private boolean format; + private final static int[] sOutputEscapes128; + private final static char[] HC = "0123456789ABCDEF".toCharArray(); + /** + * Value used for lookup tables to indicate that matching characters + * are to be escaped using standard escaping; for JSON this means + * (for example) using "backslash - u" escape method. + */ + + public final static int ESCAPE_STANDARD = -1; static { - Set chars = new HashSet<>(); - chars.add('"'); - chars.add('\\'); - chars.add('/'); - chars.add('b'); - chars.add('f'); - chars.add('h'); - chars.add('r'); - chars.add('t'); - ESCAPED_CHARS = Collections.unmodifiableSet(chars); + int[] table = new int[128]; + // Control chars need generic escape sequence + for (int i = 0; i < 32; ++i) { + // 04-Mar-2011, tatu: Used to use "-(i + 1)", replaced with constant + table[i] = ESCAPE_STANDARD; + } + /* Others (and some within that range too) have explicit shorter + * sequences + */ + table['"'] = '"'; + table['\\'] = '\\'; + // Escaping of slash is optional, so let's not add it + table[0x08] = 'b'; + table[0x09] = 't'; + table[0x0C] = 'f'; + table[0x0A] = 'n'; + table[0x0D] = 'r'; + sOutputEscapes128 = table; } public JsonMapObjectReaderWriter() { @@ -384,26 +401,38 @@ public Output append(char ch) { } - private String escapeJson(String value) { + private String escapeJson(String content) { StringBuilder sb = new StringBuilder(); - for (int i = 0; i < value.length(); i++) { - char c = value.charAt(i); - // If we have " and the previous char was not \ then escape it - if (c == '"' && (i == 0 || value.charAt(i - 1) != '\\')) { - sb.append('\\').append(c); - // If we have \ and the previous char was not \ and the next char is not an escaped char, then escape it - } else if (c == '\\' && (i == 0 || value.charAt(i - 1) != '\\') - && (i == value.length() - 1 || !isEscapedChar(value.charAt(i + 1)))) { - sb.append('\\').append(c); - } else { + final int[] escCodes = sOutputEscapes128; + int escLen = escCodes.length; + for (int i = 0, len = content.length(); i < len; ++i) { + char c = content.charAt(i); + if (c >= escLen || escCodes[c] == 0) { sb.append(c); + continue; + } + sb.append('\\'); + int escCode = escCodes[c]; + if (escCode < 0) { // generic quoting (hex value) + // The only negative value sOutputEscapes128 returns + // is CharacterEscapes.ESCAPE_STANDARD, which mean + // appendQuotes should encode using the Unicode encoding; + // not sure if this is the right way to encode for + // CharacterEscapes.ESCAPE_CUSTOM or other (future) + // CharacterEscapes.ESCAPE_XXX values. + + // We know that it has to fit in just 2 hex chars + sb.append('u'); + sb.append('0'); + sb.append('0'); + int value = c; // widening + sb.append(HC[value >> 4]); + sb.append(HC[value & 0xF]); + } else { // "named", i.e. prepend with slash + sb.append((char) escCode); } } return sb.toString(); } - private boolean isEscapedChar(char c) { - return ESCAPED_CHARS.contains(Character.valueOf(c)); - } - } From 2b252f1ae3c1699ca49fad9d4d87aeca9dda4367 Mon Sep 17 00:00:00 2001 From: Roberto Viani Junior Date: Mon, 7 Feb 2022 13:46:22 -0300 Subject: [PATCH 2/2] removing unused dependency and adding unit test --- .../json/basic/JsonMapObjectReaderWriter.java | 16 ++++++---------- .../basic/JsonMapObjectReaderWriterTest.java | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java b/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java index 4931ff5822a..b5954611726 100644 --- a/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java +++ b/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java @@ -18,18 +18,15 @@ */ package org.apache.cxf.jaxrs.json.basic; +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.helpers.IOUtils; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UncheckedIOException; -import java.nio.ByteBuffer; import java.util.*; -import org.apache.cxf.common.util.StringUtils; -import org.apache.cxf.helpers.IOUtils; - - - public class JsonMapObjectReaderWriter { private static final char DQUOTE = '"'; private static final char COMMA = ','; @@ -55,7 +52,6 @@ public class JsonMapObjectReaderWriter { int[] table = new int[128]; // Control chars need generic escape sequence for (int i = 0; i < 32; ++i) { - // 04-Mar-2011, tatu: Used to use "-(i + 1)", replaced with constant table[i] = ESCAPE_STANDARD; } /* Others (and some within that range too) have explicit shorter @@ -415,11 +411,11 @@ private String escapeJson(String content) { int escCode = escCodes[c]; if (escCode < 0) { // generic quoting (hex value) // The only negative value sOutputEscapes128 returns - // is CharacterEscapes.ESCAPE_STANDARD, which mean + // is ESCAPE_STANDARD, which mean // appendQuotes should encode using the Unicode encoding; // not sure if this is the right way to encode for - // CharacterEscapes.ESCAPE_CUSTOM or other (future) - // CharacterEscapes.ESCAPE_XXX values. + // ESCAPE_CUSTOM or other (future) + // ESCAPE_XXX values. // We know that it has to fit in just 2 hex chars sb.append('u'); diff --git a/rt/rs/extensions/json-basic/src/test/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriterTest.java b/rt/rs/extensions/json-basic/src/test/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriterTest.java index 424eb72c5dd..091353e600d 100644 --- a/rt/rs/extensions/json-basic/src/test/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriterTest.java +++ b/rt/rs/extensions/json-basic/src/test/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriterTest.java @@ -210,4 +210,19 @@ public void testAlreadyEscapedBackslash() throws Exception { assertEquals("a\\", entry.getValue()); } + @Test + public void testEscapingMetacharacter() throws Exception { + JsonMapObjectReaderWriter jsonMapObjectReaderWriter = new JsonMapObjectReaderWriter(); + Map content = new HashMap<>(); + content.put("userInput", "GET\n\n\n\t\f\b\r \"\\"); + String json = jsonMapObjectReaderWriter.toJson(content); + assertEquals("{\"userInput\":\"GET\\n\\n\\n\\t\\f\\b\\r \\\"\\\\\"}", json); + + Map map = jsonMapObjectReaderWriter.fromJson(json); + assertEquals(1, map.size()); + Map.Entry entry = map.entrySet().iterator().next(); + assertEquals("userInput", entry.getKey()); + assertEquals("GET\\n\\n\\n\\t\\f\\b\\r \"\\", entry.getValue()); + } + }