Skip to content

Commit c6c2ce2

Browse files
committed
Merge branch '3.4.x'
Closes gh-45262
2 parents ad64a70 + 80632bd commit c6c2ce2

File tree

2 files changed

+55
-4
lines changed

2 files changed

+55
-4
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java

+24-4
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,12 @@
4747
*/
4848
class JsonValueWriter {
4949

50+
private static final int DEFAULT_MAX_NESTING_DEPTH = 500;
51+
5052
private final Appendable out;
5153

54+
private final int maxNestingDepth;
55+
5256
private MemberPath path = MemberPath.ROOT;
5357

5458
private final Deque<JsonWriterFiltersAndProcessors> filtersAndProcessors = new ArrayDeque<>();
@@ -60,7 +64,18 @@ class JsonValueWriter {
6064
* @param out the {@link Appendable} used to receive the JSON output
6165
*/
6266
JsonValueWriter(Appendable out) {
67+
this(out, DEFAULT_MAX_NESTING_DEPTH);
68+
}
69+
70+
/**
71+
* Create a new {@link JsonValueWriter} instance.
72+
* @param out the {@link Appendable} used to receive the JSON output
73+
* @param maxNestingDepth the maximum allowed nesting depth for JSON objects and
74+
* arrays
75+
*/
76+
JsonValueWriter(Appendable out, int maxNestingDepth) {
6377
this.out = out;
78+
this.maxNestingDepth = maxNestingDepth;
6479
}
6580

6681
void pushProcessors(JsonWriterFiltersAndProcessors jsonProcessors) {
@@ -115,10 +130,7 @@ else if (value instanceof WritableJson writableJson) {
115130
throw new UncheckedIOException(ex);
116131
}
117132
}
118-
else if (value instanceof Path p) {
119-
writeString(p.toString());
120-
}
121-
else if (value instanceof Iterable<?> iterable) {
133+
else if (value instanceof Iterable<?> iterable && canWriteAsArray(iterable)) {
122134
writeArray(iterable::forEach);
123135
}
124136
else if (ObjectUtils.isArray(value)) {
@@ -135,6 +147,10 @@ else if (value instanceof Number || value instanceof Boolean) {
135147
}
136148
}
137149

150+
private <V> boolean canWriteAsArray(Iterable<?> iterable) {
151+
return !(iterable instanceof Path);
152+
}
153+
138154
/**
139155
* Start a new {@link Series} (JSON object or array).
140156
* @param series the series to start
@@ -144,6 +160,10 @@ else if (value instanceof Number || value instanceof Boolean) {
144160
*/
145161
void start(Series series) {
146162
if (series != null) {
163+
int nestingDepth = this.activeSeries.size();
164+
Assert.state(nestingDepth <= this.maxNestingDepth,
165+
() -> "JSON nesting depth (%s) exceeds maximum depth of %s (current path: %s)"
166+
.formatted(nestingDepth, this.maxNestingDepth, this.path));
147167
this.activeSeries.push(new ActiveSeries(series));
148168
append(series.openChar);
149169
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java

+31
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.io.File;
2020
import java.nio.file.Path;
21+
import java.util.ArrayList;
2122
import java.util.LinkedHashMap;
2223
import java.util.LinkedHashSet;
2324
import java.util.List;
@@ -253,6 +254,36 @@ void writeJavaNioPathShouldBeSerializedAsString() {
253254
.isEqualTo(quoted("a\\%1$sb\\%1$sc".formatted(File.separator)));
254255
}
255256

257+
@Test
258+
void illegalStateExceptionShouldBeThrownWhenCollectionExceededNestingDepth() {
259+
JsonValueWriter writer = new JsonValueWriter(new StringBuilder(), 128);
260+
List<Object> list = new ArrayList<>();
261+
list.add(list);
262+
assertThatIllegalStateException().isThrownBy(() -> writer.write(list))
263+
.withMessageStartingWith(
264+
"JSON nesting depth (129) exceeds maximum depth of 128 (current path: [0][0][0][0][0][0][0][0][0][0][0][0]");
265+
}
266+
267+
@Test
268+
void illegalStateExceptionShouldBeThrownWhenMapExceededNestingDepth() {
269+
JsonValueWriter writer = new JsonValueWriter(new StringBuilder(), 128);
270+
Map<String, Object> map = new LinkedHashMap<>();
271+
map.put("foo", Map.of("bar", map));
272+
assertThatIllegalStateException().isThrownBy(() -> writer.write(map))
273+
.withMessageStartingWith(
274+
"JSON nesting depth (129) exceeds maximum depth of 128 (current path: foo.bar.foo.bar.foo.bar.foo");
275+
}
276+
277+
@Test
278+
void illegalStateExceptionShouldBeThrownWhenIterableExceededNestingDepth() {
279+
JsonValueWriter writer = new JsonValueWriter(new StringBuilder(), 128);
280+
List<Object> list = new ArrayList<>();
281+
list.add(list);
282+
assertThatIllegalStateException().isThrownBy(() -> writer.write((Iterable<Object>) list::iterator))
283+
.withMessageStartingWith(
284+
"JSON nesting depth (129) exceeds maximum depth of 128 (current path: [0][0][0][0][0][0][0][0][0][0][0][0]");
285+
}
286+
256287
private <V> String write(V value) {
257288
return doWrite((valueWriter) -> valueWriter.write(value));
258289
}

0 commit comments

Comments
 (0)