Merge pull request #44627 from nosan
Build and Deploy Snapshot / Build and Deploy Snapshot (push) Waiting to run Details
Build and Deploy Snapshot / Trigger Docs Build (push) Blocked by required conditions Details
Build and Deploy Snapshot / Verify (push) Blocked by required conditions Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[early-access:true toolchain:true version:24], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[early-access:true toolchain:true version:24], map[id:windows-latest name:Windows]) (push) Waiting to run Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:17], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:17], map[id:windows-latest name:Windows]) (push) Waiting to run Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:21], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:21], map[id:windows-latest name:Windows]) (push) Waiting to run Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:22], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:22], map[id:windows-latest name:Windows]) (push) Waiting to run Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:23], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:23], map[id:windows-latest name:Windows]) (push) Waiting to run Details
Run System Tests / Java ${{ matrix.java.version}} (map[toolchain:false version:17]) (push) Waiting to run Details
Run System Tests / Java ${{ matrix.java.version}} (map[toolchain:true version:21]) (push) Waiting to run Details

* pr/44627:
  Polish 'Protected against JsonValueWriter stack overflow'
  Protected against JsonValueWriter stack overflow

Closes gh-44627
This commit is contained in:
Phillip Webb 2025-04-22 20:00:36 -07:00
commit 80632bd41c
2 changed files with 50 additions and 0 deletions

View File

@ -47,8 +47,12 @@ import org.springframework.util.function.ThrowingConsumer;
*/
class JsonValueWriter {
private static final int DEFAULT_MAX_NESTING_DEPTH = 500;
private final Appendable out;
private final int maxNestingDepth;
private MemberPath path = MemberPath.ROOT;
private final Deque<JsonWriterFiltersAndProcessors> filtersAndProcessors = new ArrayDeque<>();
@ -60,7 +64,18 @@ class JsonValueWriter {
* @param out the {@link Appendable} used to receive the JSON output
*/
JsonValueWriter(Appendable out) {
this(out, DEFAULT_MAX_NESTING_DEPTH);
}
/**
* Create a new {@link JsonValueWriter} instance.
* @param out the {@link Appendable} used to receive the JSON output
* @param maxNestingDepth the maximum allowed nesting depth for JSON objects and
* arrays
*/
JsonValueWriter(Appendable out, int maxNestingDepth) {
this.out = out;
this.maxNestingDepth = maxNestingDepth;
}
void pushProcessors(JsonWriterFiltersAndProcessors jsonProcessors) {
@ -145,6 +160,10 @@ class JsonValueWriter {
*/
void start(Series series) {
if (series != null) {
int nestingDepth = this.activeSeries.size();
Assert.state(nestingDepth <= this.maxNestingDepth,
() -> "JSON nesting depth (%s) exceeds maximum depth of %s (current path: %s)"
.formatted(nestingDepth, this.maxNestingDepth, this.path));
this.activeSeries.push(new ActiveSeries(series));
append(series.openChar);
}

View File

@ -18,6 +18,7 @@ package org.springframework.boot.json;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@ -253,6 +254,36 @@ class JsonValueWriterTests {
.isEqualTo(quoted("a\\%1$sb\\%1$sc".formatted(File.separator)));
}
@Test
void illegalStateExceptionShouldBeThrownWhenCollectionExceededNestingDepth() {
JsonValueWriter writer = new JsonValueWriter(new StringBuilder(), 128);
List<Object> list = new ArrayList<>();
list.add(list);
assertThatIllegalStateException().isThrownBy(() -> writer.write(list))
.withMessageStartingWith(
"JSON nesting depth (129) exceeds maximum depth of 128 (current path: [0][0][0][0][0][0][0][0][0][0][0][0]");
}
@Test
void illegalStateExceptionShouldBeThrownWhenMapExceededNestingDepth() {
JsonValueWriter writer = new JsonValueWriter(new StringBuilder(), 128);
Map<String, Object> map = new LinkedHashMap<>();
map.put("foo", Map.of("bar", map));
assertThatIllegalStateException().isThrownBy(() -> writer.write(map))
.withMessageStartingWith(
"JSON nesting depth (129) exceeds maximum depth of 128 (current path: foo.bar.foo.bar.foo.bar.foo");
}
@Test
void illegalStateExceptionShouldBeThrownWhenIterableExceededNestingDepth() {
JsonValueWriter writer = new JsonValueWriter(new StringBuilder(), 128);
List<Object> list = new ArrayList<>();
list.add(list);
assertThatIllegalStateException().isThrownBy(() -> writer.write((Iterable<Object>) list::iterator))
.withMessageStartingWith(
"JSON nesting depth (129) exceeds maximum depth of 128 (current path: [0][0][0][0][0][0][0][0][0][0][0][0]");
}
private <V> String write(V value) {
return doWrite((valueWriter) -> valueWriter.write(value));
}