Skip to content

Commit 983265e

Browse files
committed
More compliant JSON escaping of characters
1 parent 93ea6dc commit 983265e

File tree

2 files changed

+40
-15
lines changed

2 files changed

+40
-15
lines changed

java/client/src/org/openqa/selenium/json/JsonOutput.java

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,38 @@ public class JsonOutput implements Closeable {
4848
private static final Logger LOG = Logger.getLogger(JsonOutput.class.getName());
4949
private static final int MAX_DEPTH = 5;
5050

51+
// https://www.json.org has some helpful comments on characters to escape
52+
// See also https://tools.ietf.org/html/rfc8259#section-7 and
53+
// https://github.com/google/gson/issues/341 so we escape those as well.
54+
// It's legal to escape any character, so to be nice to HTML parsers,
55+
// we'll also escape "<" and "&"
56+
private static final Map<Integer, String> ESCAPES;
57+
static {
58+
ImmutableMap.Builder<Integer, String> builder = ImmutableMap.builder();
59+
60+
for (int i = 0; i <= 0x1f; i++) {
61+
// We want nice looking escapes for these, which are called out
62+
// by json.org
63+
if (!(i == '\b' || i == '\f' || i == '\n' || i == '\r' || i == '\t')) {
64+
builder.put(i, String.format("\\u%04x", i));
65+
}
66+
}
67+
68+
builder.put((int) '"', "\\\"");
69+
builder.put((int) '\\', "\\\\");
70+
builder.put((int) '/', "\\u002f");
71+
builder.put((int) '\b', "\\b");
72+
builder.put((int) '\f', "\\f");
73+
builder.put((int) '\n', "\\n");
74+
builder.put((int) '\r', "\\r");
75+
builder.put((int) '\t', "\\t");
76+
77+
builder.put((int) '\u2028', "\\u2028");
78+
builder.put((int) '<', String.format("\\u%04x", (int) '<'));
79+
builder.put((int) '&', String.format("\\u%04x", (int) '&'));
80+
ESCAPES = builder.build();
81+
}
82+
5183
private final Map<Predicate<Class<?>>, SafeBiConsumer<Object, Integer>> converters;
5284
private final Appendable appendable;
5385
private final Consumer<String> appender;
@@ -238,24 +270,11 @@ private JsonOutput append(String text) {
238270
}
239271

240272
private String asString(Object obj) {
241-
// https://www.json.org has some helpful comments on characters to escape
242273
StringBuilder toReturn = new StringBuilder("\"");
243274

244275
String.valueOf(obj)
245276
.chars()
246-
.mapToObj(
247-
i -> {
248-
switch (i) {
249-
case '"': return "\\\"";
250-
case '\\': return "\\\\";
251-
case '\b': return "\\b";
252-
case '\f': return "\\f";
253-
case '\n': return "\\n";
254-
case '\r': return "\\r";
255-
case '\t': return "\\t";
256-
default: return "" + (char) i;
257-
}
258-
})
277+
.mapToObj(i -> ESCAPES.getOrDefault(i, "" + (char) i))
259278
.forEach(toReturn::append);
260279

261280
toReturn.append('"');

java/client/test/org/openqa/selenium/json/JsonOutputTest.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,13 @@ public void testConvertsToJsonMethodResultToPrimitiveIfItIsNotJson() {
337337
}
338338

339339
String json = convert(new JsonAware(raw));
340-
assertEquals("\"gnu/linux\"", json);
340+
341+
// The JSON spec says that we should encode the forward stroke ("solidus"). Decode the string
342+
assertTrue(json.startsWith("\""));
343+
assertTrue(json.endsWith("\""));
344+
json = new JsonParser().parse(json).getAsString();
345+
346+
assertEquals("gnu/linux", json);
341347
}
342348

343349
private void verifyStackTraceInJson(String json, StackTraceElement[] stackTrace) {

0 commit comments

Comments
 (0)