Skip to content

Commit 00d61b9

Browse files
authored
feat: GsonFactory to have read leniency option (#1819)
* feat: GsonFactory to have read leniency option
1 parent 0664e17 commit 00d61b9

File tree

3 files changed

+76
-1
lines changed

3 files changed

+76
-1
lines changed

google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonFactory.java

+43
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,23 @@ public static GsonFactory getDefaultInstance() {
5252
return InstanceHolder.INSTANCE;
5353
}
5454

55+
/** Controls the behavior of leniency in reading JSON value */
56+
private boolean readLeniency = false;
57+
5558
/** Holder for the result of {@link #getDefaultInstance()}. */
5659
@Beta
5760
static class InstanceHolder {
5861
static final GsonFactory INSTANCE = new GsonFactory();
5962
}
6063

64+
// Keeping the default, non-arg constructor for backward compatibility. Users should use
65+
// getDefaultInstance() or builder() instead.
66+
public GsonFactory() {}
67+
68+
private GsonFactory(Builder builder) {
69+
readLeniency = builder.readLeniency;
70+
}
71+
6172
@Override
6273
public JsonParser createJsonParser(InputStream in) {
6374
return createJsonParser(new InputStreamReader(in, StandardCharsets.UTF_8));
@@ -90,4 +101,36 @@ public JsonGenerator createJsonGenerator(OutputStream out, Charset enc) {
90101
public JsonGenerator createJsonGenerator(Writer writer) {
91102
return new GsonGenerator(this, new JsonWriter(writer));
92103
}
104+
105+
/** Returns true if it is lenient to input JSON value. */
106+
boolean getReadLeniency() {
107+
return readLeniency;
108+
}
109+
110+
/** Returns the builder * */
111+
public static Builder builder() {
112+
return new Builder();
113+
}
114+
115+
/** Builder for GsonFactory. */
116+
public static final class Builder {
117+
// Do not directly call this constructor
118+
private Builder() {}
119+
120+
private boolean readLeniency = false;
121+
122+
/**
123+
* Set to {@code true} when you want to the JSON parser to be lenient to reading JSON value. By
124+
* default, it is {@code false}.
125+
*/
126+
public Builder setReadLeniency(boolean readLeniency) {
127+
this.readLeniency = readLeniency;
128+
return this;
129+
}
130+
131+
/** Builds GsonFactory instance. */
132+
public GsonFactory build() {
133+
return new GsonFactory(this);
134+
}
135+
}
93136
}

google-http-client-gson/src/main/java/com/google/api/client/json/gson/GsonParser.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class GsonParser extends JsonParser {
4343
GsonParser(GsonFactory factory, JsonReader reader) {
4444
this.factory = factory;
4545
this.reader = reader;
46-
reader.setLenient(false);
46+
reader.setLenient(factory.getReadLeniency());
4747
}
4848

4949
@Override

google-http-client-gson/src/test/java/com/google/api/client/json/gson/GsonFactoryTest.java

+32
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,16 @@
1414

1515
package com.google.api.client.json.gson;
1616

17+
import com.google.api.client.json.GenericJson;
1718
import com.google.api.client.json.JsonFactory;
19+
import com.google.api.client.json.JsonObjectParser;
1820
import com.google.api.client.json.JsonParser;
1921
import com.google.api.client.test.json.AbstractJsonFactoryTest;
22+
import com.google.gson.stream.MalformedJsonException;
23+
import java.io.ByteArrayInputStream;
2024
import java.io.IOException;
25+
import java.io.InputStream;
26+
import java.nio.charset.StandardCharsets;
2127
import java.util.ArrayList;
2228

2329
/**
@@ -94,4 +100,30 @@ public final void testGetByteValue() throws IOException {
94100
assertNotNull(ex.getMessage());
95101
}
96102
}
103+
104+
public final void testReaderLeniency_lenient() throws IOException {
105+
JsonObjectParser parser =
106+
new JsonObjectParser(GsonFactory.builder().setReadLeniency(true).build());
107+
108+
// This prefix in JSON body is used to prevent Cross-site script inclusion (XSSI).
109+
InputStream inputStream =
110+
new ByteArrayInputStream((")]}'\n" + JSON_ENTRY_PRETTY).getBytes(StandardCharsets.UTF_8));
111+
GenericJson json = parser.parseAndClose(inputStream, StandardCharsets.UTF_8, GenericJson.class);
112+
113+
assertEquals("foo", json.get("title"));
114+
}
115+
116+
public final void testReaderLeniency_not_lenient_by_default() throws IOException {
117+
JsonObjectParser parser = new JsonObjectParser(GsonFactory.getDefaultInstance());
118+
119+
try {
120+
// This prefix in JSON body is used to prevent Cross-site script inclusion (XSSI).
121+
InputStream inputStream =
122+
new ByteArrayInputStream((")]}'\n" + JSON_ENTRY_PRETTY).getBytes(StandardCharsets.UTF_8));
123+
parser.parseAndClose(inputStream, StandardCharsets.UTF_8, GenericJson.class);
124+
fail("The read leniency should fail the JSON input with XSSI prefix.");
125+
} catch (MalformedJsonException ex) {
126+
assertNotNull(ex.getMessage());
127+
}
128+
}
97129
}

0 commit comments

Comments
 (0)