Merge branch '2.6.x' into 2.7.x

Closes gh-31871
This commit is contained in:
Phillip Webb 2022-07-26 15:37:41 +01:00
commit 466724f77a
5 changed files with 42 additions and 6 deletions

View File

@ -37,6 +37,8 @@ import org.springframework.util.StringUtils;
*/ */
public class BasicJsonParser extends AbstractJsonParser { public class BasicJsonParser extends AbstractJsonParser {
private static final int MAX_DEPTH = 1000;
@Override @Override
public Map<String, Object> parseMap(String json) { public Map<String, Object> parseMap(String json) {
return tryParse(() -> parseMap(json, this::parseMapInternal), Exception.class); return tryParse(() -> parseMap(json, this::parseMapInternal), Exception.class);
@ -44,21 +46,24 @@ public class BasicJsonParser extends AbstractJsonParser {
@Override @Override
public List<Object> parseList(String json) { public List<Object> parseList(String json) {
return tryParse(() -> parseList(json, this::parseListInternal), Exception.class); return tryParse(() -> parseList(json, (jsonToParse) -> parseListInternal(0, jsonToParse)), Exception.class);
} }
private List<Object> parseListInternal(String json) { private List<Object> parseListInternal(int nesting, String json) {
List<Object> list = new ArrayList<>(); List<Object> list = new ArrayList<>();
json = trimLeadingCharacter(trimTrailingCharacter(json, ']'), '[').trim(); json = trimLeadingCharacter(trimTrailingCharacter(json, ']'), '[').trim();
for (String value : tokenize(json)) { for (String value : tokenize(json)) {
list.add(parseInternal(value)); list.add(parseInternal(nesting + 1, value));
} }
return list; return list;
} }
private Object parseInternal(String json) { private Object parseInternal(int nesting, String json) {
if (nesting > MAX_DEPTH) {
throw new IllegalStateException("JSON is too deeply nested");
}
if (json.startsWith("[")) { if (json.startsWith("[")) {
return parseListInternal(json); return parseListInternal(nesting + 1, json);
} }
if (json.startsWith("{")) { if (json.startsWith("{")) {
return parseMapInternal(json); return parseMapInternal(json);
@ -101,7 +106,7 @@ public class BasicJsonParser extends AbstractJsonParser {
for (String pair : tokenize(json)) { for (String pair : tokenize(json)) {
String[] values = StringUtils.trimArrayElements(StringUtils.split(pair, ":")); String[] values = StringUtils.trimArrayElements(StringUtils.split(pair, ":"));
String key = trimLeadingCharacter(trimTrailingCharacter(values[0], '"'), '"'); String key = trimLeadingCharacter(trimTrailingCharacter(values[0], '"'), '"');
Object value = parseInternal(values[1]); Object value = parseInternal(0, values[1]);
map.put(key, value); map.put(key, value);
} }
return map; return map;

View File

@ -16,11 +16,15 @@
package org.springframework.boot.json; package org.springframework.boot.json;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.util.StreamUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@ -186,4 +190,12 @@ abstract class AbstractJsonParserTests {
assertThatExceptionOfType(JsonParseException.class).isThrownBy(() -> this.parser.parseMap("{\"foo\"}")); assertThatExceptionOfType(JsonParseException.class).isThrownBy(() -> this.parser.parseMap("{\"foo\"}"));
} }
@Test // gh-31868
void listWithRepeatedOpenArray() throws IOException {
String input = StreamUtils.copyToString(
AbstractJsonParserTests.class.getResourceAsStream("repeated-open-array.txt"), StandardCharsets.UTF_8);
assertThatExceptionOfType(JsonParseException.class).isThrownBy(() -> this.parser.parseList(input)).havingCause()
.withMessageContaining("too deeply nested");
}
} }

View File

@ -16,6 +16,10 @@
package org.springframework.boot.json; package org.springframework.boot.json;
import java.io.IOException;
import org.junit.jupiter.api.Disabled;
/** /**
* Tests for {@link GsonJsonParser}. * Tests for {@link GsonJsonParser}.
* *
@ -28,4 +32,10 @@ class GsonJsonParserTests extends AbstractJsonParserTests {
return new GsonJsonParser(); return new GsonJsonParser();
} }
@Override
@Disabled("Gson does not protect against deeply nested JSON")
void listWithRepeatedOpenArray() throws IOException {
super.listWithRepeatedOpenArray();
}
} }

View File

@ -16,6 +16,8 @@
package org.springframework.boot.json; package org.springframework.boot.json;
import java.io.IOException;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.yaml.snakeyaml.constructor.ConstructorException; import org.yaml.snakeyaml.constructor.ConstructorException;
@ -53,4 +55,10 @@ class YamlJsonParserTests extends AbstractJsonParserTests {
void mapWithKeyAndNoValue() { void mapWithKeyAndNoValue() {
} }
@Override
@Disabled("SnakeYaml does not protect against deeply nested JSON")
void listWithRepeatedOpenArray() throws IOException {
super.listWithRepeatedOpenArray();
}
} }

File diff suppressed because one or more lines are too long