Escape current character output
ci/woodpecker/push/woodpecker Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2024-03-12 13:19:48 +01:00
parent ff4b1a7656
commit 57b2e54b5a
Signed by: Johannes
GPG Key ID: E76429612C2929F4
7 changed files with 110 additions and 65 deletions

View File

@ -22,6 +22,8 @@ import com.google.gson.Strictness;
import com.google.gson.internal.DefaultConfig;
import com.google.gson.internal.JsonReaderInternalAccess;
import com.google.gson.internal.TroubleshootingGuide;
import com.google.gson.util.StringEscapeUtil;
import java.io.Closeable;
import java.io.EOFException;
import java.io.IOException;
@ -1611,7 +1613,11 @@ public class JsonReader implements Closeable {
private String locationString() {
int line = lineNumber + 1;
int column = pos - lineStart + 1;
String charInterjection = pos < buffer.length ? " (char '" + buffer[pos] + "')" : "";
String replacement = StringEscapeUtil.getReplacement(buffer[pos]);
if (replacement == null) {
replacement = String.valueOf(buffer[pos]);
}
String charInterjection = pos < buffer.length ? " (char '" + replacement + "')" : "";
return " at line " + line + " column " + column + charInterjection + " path " + getPath();
}

View File

@ -30,6 +30,8 @@ import com.google.gson.FormattingStyle;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.Strictness;
import com.google.gson.util.StringEscapeUtil;
import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;
@ -166,39 +168,6 @@ public class JsonWriter implements Closeable, Flushable {
private static final Pattern VALID_JSON_NUMBER_PATTERN =
Pattern.compile("-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][-+]?[0-9]+)?");
/*
* From RFC 8259, "All Unicode characters may be placed within the
* quotation marks except for the characters that must be escaped:
* quotation mark, reverse solidus, and the control characters
* (U+0000 through U+001F)."
*
* We also escape '\u2028' and '\u2029', which JavaScript interprets as
* newline characters. This prevents eval() from failing with a syntax
* error. http://code.google.com/p/google-gson/issues/detail?id=341
*/
private static final String[] REPLACEMENT_CHARS;
private static final String[] HTML_SAFE_REPLACEMENT_CHARS;
static {
REPLACEMENT_CHARS = new String[128];
for (int i = 0; i <= 0x1f; i++) {
REPLACEMENT_CHARS[i] = String.format("\\u%04x", i);
}
REPLACEMENT_CHARS['"'] = "\\\"";
REPLACEMENT_CHARS['\\'] = "\\\\";
REPLACEMENT_CHARS['\t'] = "\\t";
REPLACEMENT_CHARS['\b'] = "\\b";
REPLACEMENT_CHARS['\n'] = "\\n";
REPLACEMENT_CHARS['\r'] = "\\r";
REPLACEMENT_CHARS['\f'] = "\\f";
HTML_SAFE_REPLACEMENT_CHARS = REPLACEMENT_CHARS.clone();
HTML_SAFE_REPLACEMENT_CHARS['<'] = "\\u003c";
HTML_SAFE_REPLACEMENT_CHARS['>'] = "\\u003e";
HTML_SAFE_REPLACEMENT_CHARS['&'] = "\\u0026";
HTML_SAFE_REPLACEMENT_CHARS['='] = "\\u003d";
HTML_SAFE_REPLACEMENT_CHARS['\''] = "\\u0027";
}
/** The JSON output destination */
private final Writer out;
@ -794,23 +763,13 @@ public class JsonWriter implements Closeable, Flushable {
}
private void string(String value) throws IOException {
String[] replacements = htmlSafe ? HTML_SAFE_REPLACEMENT_CHARS : REPLACEMENT_CHARS;
out.write('\"');
int last = 0;
int length = value.length();
for (int i = 0; i < length; i++) {
char c = value.charAt(i);
String replacement;
if (c < 128) {
replacement = replacements[c];
if (replacement == null) {
continue;
}
} else if (c == '\u2028') {
replacement = "\\u2028";
} else if (c == '\u2029') {
replacement = "\\u2029";
} else {
String replacement = htmlSafe ? StringEscapeUtil.getHtmlSafeReplacement(c) : StringEscapeUtil.getReplacement(c);
if (replacement == null) {
continue;
}
if (last < i) {

View File

@ -0,0 +1,80 @@
package com.google.gson.util;
/**
* Utilities methods for escaping strings, extracted from gsons JsonWriter.
* @author JFronny
*/
public class StringEscapeUtil {
private StringEscapeUtil() {}
/*
* From RFC 8259, "All Unicode characters may be placed within the
* quotation marks except for the characters that must be escaped:
* quotation mark, reverse solidus, and the control characters
* (U+0000 through U+001F)."
*
* We also escape '\u2028' and '\u2029', which JavaScript interprets as
* newline characters. This prevents eval() from failing with a syntax
* error. http://code.google.com/p/google-gson/issues/detail?id=341
*/
private static final String[] REPLACEMENT_CHARS;
private static final String[] HTML_SAFE_REPLACEMENT_CHARS;
static {
REPLACEMENT_CHARS = new String[128];
for (int i = 0; i <= 0x1f; i++) {
REPLACEMENT_CHARS[i] = String.format("\\u%04x", i);
}
REPLACEMENT_CHARS['"'] = "\\\"";
REPLACEMENT_CHARS['\\'] = "\\\\";
REPLACEMENT_CHARS['\t'] = "\\t";
REPLACEMENT_CHARS['\b'] = "\\b";
REPLACEMENT_CHARS['\n'] = "\\n";
REPLACEMENT_CHARS['\r'] = "\\r";
REPLACEMENT_CHARS['\f'] = "\\f";
REPLACEMENT_CHARS['\0'] = "\\0";
HTML_SAFE_REPLACEMENT_CHARS = REPLACEMENT_CHARS.clone();
HTML_SAFE_REPLACEMENT_CHARS['<'] = "\\u003c";
HTML_SAFE_REPLACEMENT_CHARS['>'] = "\\u003e";
HTML_SAFE_REPLACEMENT_CHARS['&'] = "\\u0026";
HTML_SAFE_REPLACEMENT_CHARS['='] = "\\u003d";
HTML_SAFE_REPLACEMENT_CHARS['\''] = "\\u0027";
}
private static String getReplacement(char c, String[] replacements) {
String replacement;
if (c < 128) {
replacement = replacements[c];
if (replacement == null) {
return null;
}
} else if (c == '\u2028') {
replacement = "\\u2028";
} else if (c == '\u2029') {
replacement = "\\u2029";
} else {
return null;
}
return replacement;
}
/**
* Returns the replacement for the character, or null if the character does not need to be escaped.
* @param c the character to escape
* @return the replacement for the character, or null if the character does not need to be escaped
* @see #getHtmlSafeReplacement(char)
*/
public static String getReplacement(char c) {
return getReplacement(c, REPLACEMENT_CHARS);
}
/**
* Returns the replacement for the character, or null if the character does not need to be escaped.
* @param c the character to escape
* @return the replacement for the character, or null if the character does not need to be escaped
* @see #getReplacement(char)
*/
public static String getHtmlSafeReplacement(char c) {
return getReplacement(c, HTML_SAFE_REPLACEMENT_CHARS);
}
}

View File

@ -40,7 +40,7 @@ public class ToNumberPolicyTest {
assertThat(e)
.hasMessageThat()
.isEqualTo(
"JSON forbids NaN and infinities: Infinity at line 1 column 6 (char '\0') path $\n"
"JSON forbids NaN and infinities: Infinity at line 1 column 6 (char '\\0') path $\n"
+ "See https://github.com/google/gson/blob/main/Troubleshooting.md#malformed-json");
assertThrows(
@ -133,7 +133,7 @@ public class ToNumberPolicyTest {
assertThat(e)
.hasMessageThat()
.isEqualTo(
"Expected a double but was NULL at line 1 column 5 (char '\0') path $\n"
"Expected a double but was NULL at line 1 column 5 (char '\\0') path $\n"
+ "See https://github.com/google/gson/blob/main/Troubleshooting.md#adapter-not-null-safe");
e =
@ -143,7 +143,7 @@ public class ToNumberPolicyTest {
assertThat(e)
.hasMessageThat()
.isEqualTo(
"Expected a string but was NULL at line 1 column 5 (char '\0') path $\n"
"Expected a string but was NULL at line 1 column 5 (char '\\0') path $\n"
+ "See https://github.com/google/gson/blob/main/Troubleshooting.md#adapter-not-null-safe");
e =
@ -153,7 +153,7 @@ public class ToNumberPolicyTest {
assertThat(e)
.hasMessageThat()
.isEqualTo(
"Expected a string but was NULL at line 1 column 5 (char '\0') path $\n"
"Expected a string but was NULL at line 1 column 5 (char '\\0') path $\n"
+ "See https://github.com/google/gson/blob/main/Troubleshooting.md#adapter-not-null-safe");
e =
@ -163,7 +163,7 @@ public class ToNumberPolicyTest {
assertThat(e)
.hasMessageThat()
.isEqualTo(
"Expected a string but was NULL at line 1 column 5 (char '\0') path $\n"
"Expected a string but was NULL at line 1 column 5 (char '\\0') path $\n"
+ "See https://github.com/google/gson/blob/main/Troubleshooting.md#adapter-not-null-safe");
}

View File

@ -97,7 +97,7 @@ public class PrimitiveTest {
.hasMessageThat()
.isEqualTo(
"java.lang.NumberFormatException: Expected an int but was 2147483648"
+ " at line 1 column 11 (char '\0') path $");
+ " at line 1 column 11 (char '\\0') path $");
}
@Test
@ -140,7 +140,7 @@ public class PrimitiveTest {
.hasMessageThat()
.isEqualTo(
"java.lang.NumberFormatException: Expected an int but was 2147483648"
+ " at line 1 column 11 (char '\0') path $");
+ " at line 1 column 11 (char '\\0') path $");
}
@Test
@ -915,7 +915,7 @@ public class PrimitiveTest {
assertThat(e)
.hasCauseThat()
.hasMessageThat()
.isEqualTo("Expected an int but was -122.08e-213 at line 1 column 13 (char '\0') path $");
.isEqualTo("Expected an int but was -122.08e-213 at line 1 column 13 (char '\\0') path $");
}
@Test
@ -926,7 +926,7 @@ public class PrimitiveTest {
assertThat(e)
.hasCauseThat()
.hasMessageThat()
.isEqualTo("Expected an int but was " + number + " at line 1 column 57 (char '\0') path $");
.isEqualTo("Expected an int but was " + number + " at line 1 column 57 (char '\\0') path $");
}
@Test
@ -937,7 +937,7 @@ public class PrimitiveTest {
assertThat(e)
.hasCauseThat()
.hasMessageThat()
.isEqualTo("Expected a long but was " + number + " at line 1 column 57 (char '\0') path $");
.isEqualTo("Expected a long but was " + number + " at line 1 column 57 (char '\\0') path $");
}
@Test

View File

@ -1175,27 +1175,27 @@ public final class JsonReaderTest {
fail();
} catch (IllegalStateException expected) {
assertUnexpectedStructureError(
expected, "a string", "END_OBJECT", "line 1 column 11 (char '\0') path $.a");
expected, "a string", "END_OBJECT", "line 1 column 11 (char '\\0') path $.a");
}
try {
reader.nextName();
fail();
} catch (IllegalStateException expected) {
assertUnexpectedStructureError(expected, "a name", "END_OBJECT", "line 1 column 11 (char '\0') path $.a");
assertUnexpectedStructureError(expected, "a name", "END_OBJECT", "line 1 column 11 (char '\\0') path $.a");
}
try {
reader.beginArray();
fail();
} catch (IllegalStateException expected) {
assertUnexpectedStructureError(
expected, "BEGIN_ARRAY", "END_OBJECT", "line 1 column 11 (char '\0') path $.a");
expected, "BEGIN_ARRAY", "END_OBJECT", "line 1 column 11 (char '\\0') path $.a");
}
try {
reader.endArray();
fail();
} catch (IllegalStateException expected) {
assertUnexpectedStructureError(
expected, "END_ARRAY", "END_OBJECT", "line 1 column 11 (char '\0') path $.a");
expected, "END_ARRAY", "END_OBJECT", "line 1 column 11 (char '\\0') path $.a");
}
reader.endObject();
assertThat(reader.peek()).isEqualTo(JsonToken.END_DOCUMENT);
@ -1623,7 +1623,7 @@ public final class JsonReaderTest {
reader.nextNull();
fail();
} catch (IllegalStateException expected) {
assertThat(expected).hasMessageThat().startsWith("Expected null but was END_ARRAY at line 1 column 8 (char '\0') path $[1]");
assertThat(expected).hasMessageThat().startsWith("Expected null but was END_ARRAY at line 1 column 8 (char '\\0') path $[1]");
}
reader = new JsonReader(reader("[,]"));
@ -2109,7 +2109,7 @@ public final class JsonReaderTest {
assertThat(expected)
.hasMessageThat()
.isEqualTo(
"Unterminated object at x at line 1 column 16 (char '\0') path $.a\n"
"Unterminated object at x at line 1 column 16 (char '\\0') path $.a\n"
+ "See https://github.com/google/gson/blob/main/Troubleshooting.md#malformed-json");
}
}
@ -2241,7 +2241,7 @@ public final class JsonReaderTest {
assertThat(expected)
.hasMessageThat()
.isEqualTo(
"Expected name at line 1 column 11 (char '\0') path $.a\n"
"Expected name at line 1 column 11 (char '\\0') path $.a\n"
+ "See https://github.com/google/gson/blob/main/Troubleshooting.md#malformed-json");
}
}
@ -2260,7 +2260,7 @@ public final class JsonReaderTest {
assertThat(expected)
.hasMessageThat()
.isEqualTo(
"Expected name at line 1 column 11 (char '\0') path $.a\n"
"Expected name at line 1 column 11 (char '\\0') path $.a\n"
+ "See https://github.com/google/gson/blob/main/Troubleshooting.md#malformed-json");
}
}

View File

@ -698,7 +698,7 @@ public final class JsonWriterTest {
+ "\"}\","
+ "\"[\","
+ "\"]\","
+ "\"\\u0000\","
+ "\"\\0\","
+ "\"\\u0019\"]");
}