Skip to content

Commit 71fa3a8

Browse files
authored
CBOR: support deep nesting constraints (#361)
1 parent d938b4a commit 71fa3a8

File tree

4 files changed

+129
-5
lines changed

4 files changed

+129
-5
lines changed

cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -931,7 +931,7 @@ public JsonToken nextToken() throws IOException
931931
if (!_tagValues.isEmpty()) {
932932
return _handleTaggedArray(_tagValues, len);
933933
}
934-
_streamReadContext = _streamReadContext.createChildArrayContext(len);
934+
createChildArrayContext(len);
935935
}
936936
return (_currToken = JsonToken.START_ARRAY);
937937

@@ -940,7 +940,7 @@ public JsonToken nextToken() throws IOException
940940
_currToken = JsonToken.START_OBJECT;
941941
{
942942
int len = _decodeExplicitLength(lowBits);
943-
_streamReadContext = _streamReadContext.createChildObjectContext(len);
943+
createChildObjectContext(len);
944944
}
945945
return _currToken;
946946

@@ -1128,7 +1128,7 @@ protected JsonToken _handleTaggedArray(TagList tags, int len) throws IOException
11281128
// For simplicity, let's create matching array context -- in perfect
11291129
// world that wouldn't be necessarily, but in this one there are
11301130
// some constraints that make it necessary
1131-
_streamReadContext = _streamReadContext.createChildArrayContext(len);
1131+
createChildArrayContext(len);
11321132

11331133
// BigDecimal is the only thing we know for sure
11341134
if (!tags.contains(CBORConstants.TAG_DECIMAL_FRACTION)) {
@@ -1711,15 +1711,15 @@ public String nextTextValue() throws IOException
17111711
_currToken = JsonToken.START_ARRAY;
17121712
{
17131713
int len = _decodeExplicitLength(lowBits);
1714-
_streamReadContext = _streamReadContext.createChildArrayContext(len);
1714+
createChildArrayContext(len);
17151715
}
17161716
return null;
17171717

17181718
case 5: // Object
17191719
_currToken = JsonToken.START_OBJECT;
17201720
{
17211721
int len = _decodeExplicitLength(lowBits);
1722-
_streamReadContext = _streamReadContext.createChildObjectContext(len);
1722+
createChildObjectContext(len);
17231723
}
17241724
return null;
17251725

@@ -4060,4 +4060,14 @@ private final BigInteger _bigNegative(long l) {
40604060
BigInteger unsignedBase = _bigPositive(l);
40614061
return unsignedBase.negate().subtract(BigInteger.ONE);
40624062
}
4063+
4064+
private void createChildArrayContext(final int len) throws IOException {
4065+
_streamReadContext = _streamReadContext.createChildArrayContext(len);
4066+
streamReadConstraints().validateNestingDepth(_streamReadContext.getNestingDepth());
4067+
}
4068+
4069+
private void createChildObjectContext(final int len) throws IOException {
4070+
_streamReadContext = _streamReadContext.createChildObjectContext(len);
4071+
streamReadConstraints().validateNestingDepth(_streamReadContext.getNestingDepth());
4072+
}
40634073
}

cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORReadContext.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public CBORReadContext(CBORReadContext parent, DupDetector dups,
5858
_type = type;
5959
_expEntryCount = expEntryCount;
6060
_index = -1;
61+
_nestingDepth = parent == null ? 0 : parent._nestingDepth + 1;
6162
}
6263

6364
protected void reset(int type, int expEntryCount)

cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/CBORTestBase.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ public abstract class CBORTestBase
2323
protected CBORParser cborParser(ByteArrayOutputStream bytes) throws IOException {
2424
return cborParser(bytes.toByteArray());
2525
}
26+
protected CBORParser cborParser(CBORFactory cborFactory, ByteArrayOutputStream bytes) throws IOException {
27+
return cborParser(cborFactory, bytes.toByteArray());
28+
}
2629

2730
protected CBORParser cborParser(byte[] input) throws IOException {
2831
return cborParser(cborFactory(), input);
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package com.fasterxml.jackson.dataformat.cbor.parse.dos;
2+
3+
import com.fasterxml.jackson.core.JsonGenerator;
4+
import com.fasterxml.jackson.core.JsonParser;
5+
import com.fasterxml.jackson.core.JsonToken;
6+
import com.fasterxml.jackson.core.StreamReadConstraints;
7+
import com.fasterxml.jackson.core.exc.StreamConstraintsException;
8+
import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
9+
import com.fasterxml.jackson.dataformat.cbor.CBORTestBase;
10+
11+
import java.io.ByteArrayOutputStream;
12+
import java.io.IOException;
13+
14+
/**
15+
* Unit tests for deeply nested JSON
16+
*/
17+
public class DeepNestingParserTest extends CBORTestBase
18+
{
19+
public void testDeeplyNestedObjects() throws Exception
20+
{
21+
final int depth = 1500;
22+
ByteArrayOutputStream out = new ByteArrayOutputStream();
23+
genDeepDoc(out, depth);
24+
try (JsonParser jp = cborParser(out)) {
25+
JsonToken jt;
26+
while ((jt = jp.nextToken()) != null) {
27+
28+
}
29+
fail("expected StreamConstraintsException");
30+
} catch (StreamConstraintsException e) {
31+
assertEquals("Depth (1001) exceeds the maximum allowed nesting depth (1000)", e.getMessage());
32+
}
33+
}
34+
35+
public void testDeeplyNestedObjectsWithUnconstrainedMapper() throws Exception
36+
{
37+
final int depth = 1500;
38+
ByteArrayOutputStream out = new ByteArrayOutputStream();
39+
genDeepDoc(out, depth);
40+
CBORFactory cborFactory = CBORFactory.builder()
41+
.streamReadConstraints(StreamReadConstraints.builder().maxNestingDepth(Integer.MAX_VALUE).build())
42+
.build();
43+
try (JsonParser jp = cborParser(cborFactory, out)) {
44+
JsonToken jt;
45+
while ((jt = jp.nextToken()) != null) {
46+
47+
}
48+
}
49+
}
50+
51+
public void testDeeplyNestedArrays() throws Exception
52+
{
53+
final int depth = 750;
54+
ByteArrayOutputStream out = new ByteArrayOutputStream();
55+
genDeepArrayDoc(out, depth);
56+
try (JsonParser jp = cborParser(out)) {
57+
JsonToken jt;
58+
while ((jt = jp.nextToken()) != null) {
59+
60+
}
61+
fail("expected StreamConstraintsException");
62+
} catch (StreamConstraintsException e) {
63+
assertEquals("Depth (1001) exceeds the maximum allowed nesting depth (1000)", e.getMessage());
64+
}
65+
}
66+
67+
public void testDeeplyNestedArraysWithUnconstrainedMapper() throws Exception
68+
{
69+
final int depth = 750;
70+
ByteArrayOutputStream out = new ByteArrayOutputStream();
71+
genDeepArrayDoc(out, depth);
72+
CBORFactory cborFactory = CBORFactory.builder()
73+
.streamReadConstraints(StreamReadConstraints.builder().maxNestingDepth(Integer.MAX_VALUE).build())
74+
.build();
75+
try (JsonParser jp = cborParser(cborFactory, out)) {
76+
JsonToken jt;
77+
while ((jt = jp.nextToken()) != null) {
78+
79+
}
80+
}
81+
}
82+
83+
private void genDeepDoc(final ByteArrayOutputStream out, final int depth) throws IOException {
84+
try (JsonGenerator gen = cborGenerator(out)) {
85+
for (int i = 0; i < depth; i++) {
86+
gen.writeStartObject();
87+
gen.writeFieldName("a");
88+
}
89+
gen.writeString("val");
90+
for (int i = 0; i < depth; i++) {
91+
gen.writeEndObject();
92+
}
93+
}
94+
}
95+
96+
private void genDeepArrayDoc(final ByteArrayOutputStream out, final int depth) throws IOException {
97+
try (JsonGenerator gen = cborGenerator(out)) {
98+
for (int i = 0; i < depth; i++) {
99+
gen.writeStartObject();
100+
gen.writeFieldName("a");
101+
gen.writeStartArray();
102+
}
103+
gen.writeString("val");
104+
for (int i = 0; i < depth; i++) {
105+
gen.writeEndArray();
106+
gen.writeEndObject();
107+
}
108+
}
109+
}
110+
}

0 commit comments

Comments
 (0)