diff --git a/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java b/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java index 9fbc166177..03720fef41 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java @@ -52,6 +52,12 @@ public abstract class JsonStreamContext */ protected int _index; + /** + * The nesting depth is a count of objects and arrays that have not + * been closed, `{` and `[` respectively. + */ + protected int _nestingDepth; + /* /********************************************************** /* Life-cycle @@ -118,6 +124,14 @@ protected JsonStreamContext(int type, int index) { */ public final boolean inObject() { return _type == TYPE_OBJECT; } + /** + * The nesting depth is a count of objects and arrays that have not + * been closed, `{` and `[` respectively. + */ + public final int getNestingDepth() { + return _nestingDepth; + } + /** * @return Type description String * diff --git a/src/main/java/com/fasterxml/jackson/core/StreamReadConstraints.java b/src/main/java/com/fasterxml/jackson/core/StreamReadConstraints.java index e6b2cc5a15..2363c38dd0 100644 --- a/src/main/java/com/fasterxml/jackson/core/StreamReadConstraints.java +++ b/src/main/java/com/fasterxml/jackson/core/StreamReadConstraints.java @@ -25,6 +25,11 @@ public class StreamReadConstraints { private static final long serialVersionUID = 1L; + /** + * Default setting for maximum depth: see {@link Builder#maxNestingDepth(int)} for details. + */ + public static final int DEFAULT_MAX_DEPTH = 1000; + /** * Default setting for maximum number length: see {@link Builder#maxNumberLength(int)} for details. */ @@ -35,16 +40,37 @@ public class StreamReadConstraints */ public static final int DEFAULT_MAX_STRING_LEN = 1_000_000; + protected final int _maxNestingDepth; protected final int _maxNumLen; protected final int _maxStringLen; private static final StreamReadConstraints DEFAULT = - new StreamReadConstraints(DEFAULT_MAX_NUM_LEN, DEFAULT_MAX_STRING_LEN); + new StreamReadConstraints(DEFAULT_MAX_DEPTH, DEFAULT_MAX_NUM_LEN, DEFAULT_MAX_STRING_LEN); public static final class Builder { + private int maxNestingDepth; private int maxNumLen; private int maxStringLen; + /** + * Sets the maximum nesting depth. The depth is a count of objects and arrays that have not + * been closed, `{` and `[` respectively. + * + * @param maxNestingDepth the maximum depth + * + * @return this builder + * @throws IllegalArgumentException if the maxNestingDepth is set to a negative value + * + * @since 2.15 + */ + public Builder maxNestingDepth(final int maxNestingDepth) { + if (maxNestingDepth < 0) { + throw new IllegalArgumentException("Cannot set maxNestingDepth to a negative value"); + } + this.maxNestingDepth = maxNestingDepth; + return this; + } + /** * Sets the maximum number length (in chars or bytes, depending on input context). * The default is 1000. @@ -90,21 +116,23 @@ public Builder maxStringLength(final int maxStringLen) { } Builder() { - this(DEFAULT_MAX_NUM_LEN, DEFAULT_MAX_STRING_LEN); + this(DEFAULT_MAX_DEPTH, DEFAULT_MAX_NUM_LEN, DEFAULT_MAX_STRING_LEN); } - Builder(final int maxNumLen, final int maxStringLen) { + Builder(final int maxNestingDepth, final int maxNumLen, final int maxStringLen) { + this.maxNestingDepth = maxNestingDepth; this.maxNumLen = maxNumLen; this.maxStringLen = maxStringLen; } Builder(StreamReadConstraints src) { + maxNestingDepth = src._maxNestingDepth; maxNumLen = src._maxNumLen; maxStringLen = src._maxStringLen; } public StreamReadConstraints build() { - return new StreamReadConstraints(maxNumLen, maxStringLen); + return new StreamReadConstraints(maxNestingDepth, maxNumLen, maxStringLen); } } @@ -114,7 +142,8 @@ public StreamReadConstraints build() { /********************************************************************** */ - StreamReadConstraints(final int maxNumLen, final int maxStringLen) { + StreamReadConstraints(final int maxNestingDepth, final int maxNumLen, final int maxStringLen) { + _maxNestingDepth = maxNestingDepth; _maxNumLen = maxNumLen; _maxStringLen = maxStringLen; } @@ -141,6 +170,16 @@ public Builder rebuild() { /********************************************************************** */ + /** + * Accessor for maximum depth. + * see {@link Builder#maxNestingDepth(int)} for details. + * + * @return Maximum allowed depth + */ + public int getMaxNestingDepth() { + return _maxNestingDepth; + } + /** * Accessor for maximum length of numbers to decode. * see {@link Builder#maxNumberLength(int)} for details. @@ -169,7 +208,7 @@ public int getMaxStringLength() { /** * Convenience method that can be used to verify that a floating-point - * number of specified length does not exceed maximum specific by this + * number of specified length does not exceed maximum specified by this * constraints object: if it does, a * {@link StreamConstraintsException} * is thrown. @@ -223,4 +262,23 @@ public void validateStringLength(int length) throws StreamConstraintsException length, _maxStringLen)); } } + + /** + * Convenience method that can be used to verify that the + * nesting depth does not exceed the maximum specified by this + * constraints object: if it does, a + * {@link StreamConstraintsException} + * is thrown. + * + * @param depth count of unclosed objects and arrays + * + * @throws StreamConstraintsException If depth exceeds maximum + */ + public void validateNestingDepth(int depth) throws StreamConstraintsException + { + if (depth > _maxNestingDepth) { + throw new StreamConstraintsException(String.format("Depth (%d) exceeds the maximum allowed nesting depth (%d)", + depth, _maxNestingDepth)); + } + } } diff --git a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java index 8af2565793..41ea412f81 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java @@ -1536,4 +1536,14 @@ protected void loadMoreGuaranteed() throws IOException { // Can't declare as deprecated, for now, but shouldn't be needed protected void _finishString() throws IOException { } + + protected final void createChildArrayContext(final int lineNr, final int colNr) throws IOException { + _parsingContext = _parsingContext.createChildArrayContext(lineNr, colNr); + _streamReadConstraints.validateNestingDepth(_parsingContext.getNestingDepth()); + } + + protected final void createChildObjectContext(final int lineNr, final int colNr) throws IOException { + _parsingContext = _parsingContext.createChildObjectContext(lineNr, colNr); + _streamReadConstraints.validateNestingDepth(_parsingContext.getNestingDepth()); + } } diff --git a/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java b/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java index 576fefbf82..e2f95a7acf 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java +++ b/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java @@ -61,6 +61,7 @@ public JsonReadContext(JsonReadContext parent, DupDetector dups, int type, int l _lineNr = lineNr; _columnNr = colNr; _index = -1; + _nestingDepth = parent == null ? 0 : parent._nestingDepth + 1; } /** diff --git a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java index 232843ac81..ccbfa7d2fe 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java @@ -752,13 +752,13 @@ public final JsonToken nextToken() throws IOException break; case '[': if (!inObject) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } t = JsonToken.START_ARRAY; break; case '{': if (!inObject) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } t = JsonToken.START_OBJECT; break; @@ -817,7 +817,7 @@ public final JsonToken nextToken() throws IOException return t; } - private final JsonToken _nextAfterName() + private final JsonToken _nextAfterName() throws IOException { _nameCopied = false; // need to invalidate if it was copied JsonToken t = _nextToken; @@ -827,9 +827,9 @@ private final JsonToken _nextAfterName() // Also: may need to start new context? if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return (_currToken = t); } @@ -1165,10 +1165,10 @@ private final JsonToken _nextTokenNotInObject(int i) throws IOException } switch (i) { case '[': - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); return (_currToken = JsonToken.START_ARRAY); case '{': - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); return (_currToken = JsonToken.START_OBJECT); case 't': _matchToken("true", 1); @@ -1235,9 +1235,9 @@ public final String nextTextValue() throws IOException return _textBuffer.contentsAsString(); } if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return null; } @@ -1258,9 +1258,9 @@ public final int nextIntValue(int defaultValue) throws IOException return getIntValue(); } if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return defaultValue; } @@ -1281,9 +1281,9 @@ public final long nextLongValue(long defaultValue) throws IOException return getLongValue(); } if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return defaultValue; } @@ -1307,9 +1307,9 @@ public final Boolean nextBooleanValue() throws IOException return Boolean.FALSE; } if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return null; } diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java index cee350da3a..6d93fab588 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java @@ -716,10 +716,10 @@ private final JsonToken _nextTokenNotInObject(int i) throws IOException } switch (i) { case '[': - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); return (_currToken = JsonToken.START_ARRAY); case '{': - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); return (_currToken = JsonToken.START_OBJECT); case 't': _matchToken("true", 1); @@ -754,7 +754,7 @@ private final JsonToken _nextTokenNotInObject(int i) throws IOException return (_currToken = _handleUnexpectedValue(i)); } - private final JsonToken _nextAfterName() + private final JsonToken _nextAfterName() throws IOException { _nameCopied = false; // need to invalidate if it was copied JsonToken t = _nextToken; @@ -762,9 +762,9 @@ private final JsonToken _nextAfterName() // Also: may need to start new context? if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return (_currToken = t); } @@ -908,9 +908,9 @@ public String nextTextValue() throws IOException return _textBuffer.contentsAsString(); } if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return null; } @@ -930,9 +930,9 @@ public int nextIntValue(int defaultValue) throws IOException return getIntValue(); } if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return defaultValue; } @@ -952,9 +952,9 @@ public long nextLongValue(long defaultValue) throws IOException return getLongValue(); } if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return defaultValue; } @@ -977,9 +977,9 @@ public Boolean nextBooleanValue() throws IOException return Boolean.FALSE; } if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return null; } diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java index a7c08dae53..2cea491bf5 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java @@ -869,10 +869,10 @@ private final JsonToken _nextTokenNotInObject(int i) throws IOException } switch (i) { case '[': - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); return (_currToken = JsonToken.START_ARRAY); case '{': - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); return (_currToken = JsonToken.START_OBJECT); case 't': _matchTrue(); @@ -907,7 +907,7 @@ private final JsonToken _nextTokenNotInObject(int i) throws IOException return (_currToken = _handleUnexpectedValue(i)); } - private final JsonToken _nextAfterName() + private final JsonToken _nextAfterName() throws IOException { _nameCopied = false; // need to invalidate if it was copied JsonToken t = _nextToken; @@ -917,9 +917,9 @@ private final JsonToken _nextAfterName() // Also: may need to start new context? if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return (_currToken = t); } @@ -1334,9 +1334,9 @@ public String nextTextValue() throws IOException return _textBuffer.contentsAsString(); } if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return null; } @@ -1357,9 +1357,9 @@ public int nextIntValue(int defaultValue) throws IOException return getIntValue(); } if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return defaultValue; } @@ -1380,9 +1380,9 @@ public long nextLongValue(long defaultValue) throws IOException return getLongValue(); } if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return defaultValue; } @@ -1406,9 +1406,9 @@ public Boolean nextBooleanValue() throws IOException return Boolean.FALSE; } if (t == JsonToken.START_ARRAY) { - _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); + createChildArrayContext(_tokenInputRow, _tokenInputCol); } else if (t == JsonToken.START_OBJECT) { - _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); + createChildObjectContext(_tokenInputRow, _tokenInputCol); } return null; } diff --git a/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParserBase.java b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParserBase.java index 66e72abdcf..f37bb3fc7d 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParserBase.java @@ -579,7 +579,7 @@ public Object getEmbeddedObject() throws IOException protected final JsonToken _startArrayScope() throws IOException { - _parsingContext = _parsingContext.createChildArrayContext(-1, -1); + createChildArrayContext(-1, -1); _majorState = MAJOR_ARRAY_ELEMENT_FIRST; _majorStateAfterValue = MAJOR_ARRAY_ELEMENT_NEXT; return (_currToken = JsonToken.START_ARRAY); @@ -587,7 +587,7 @@ protected final JsonToken _startArrayScope() throws IOException protected final JsonToken _startObjectScope() throws IOException { - _parsingContext = _parsingContext.createChildObjectContext(-1, -1); + createChildObjectContext(-1, -1); _majorState = MAJOR_OBJECT_FIELD_FIRST; _majorStateAfterValue = MAJOR_OBJECT_FIELD_NEXT; return (_currToken = JsonToken.START_OBJECT); diff --git a/src/test/java/com/fasterxml/jackson/core/fuzz/Fuzz34435ParseTest.java b/src/test/java/com/fasterxml/jackson/core/fuzz/Fuzz34435ParseTest.java index e83cf3dc4c..d3689a525e 100644 --- a/src/test/java/com/fasterxml/jackson/core/fuzz/Fuzz34435ParseTest.java +++ b/src/test/java/com/fasterxml/jackson/core/fuzz/Fuzz34435ParseTest.java @@ -17,6 +17,7 @@ public void testFuzz34435ViaParser() throws Exception .enable(JsonReadFeature.ALLOW_YAML_COMMENTS) .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) .enable(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES) + .streamReadConstraints(StreamReadConstraints.builder().maxNestingDepth(Integer.MAX_VALUE).build()) .build(); JsonParser p = f.createParser(/*ObjectReadContext.empty(), */ DOC); diff --git a/src/test/java/com/fasterxml/jackson/core/jsonptr/JsonPointerOOME736Test.java b/src/test/java/com/fasterxml/jackson/core/jsonptr/JsonPointerOOME736Test.java index b4547bc5e8..d2f4e0bff0 100644 --- a/src/test/java/com/fasterxml/jackson/core/jsonptr/JsonPointerOOME736Test.java +++ b/src/test/java/com/fasterxml/jackson/core/jsonptr/JsonPointerOOME736Test.java @@ -10,7 +10,10 @@ public void testDeepJsonPointer() throws Exception { int MAX_DEPTH = 120_000; // Create nesting of 120k arrays String INPUT = new String(new char[MAX_DEPTH]).replace("\0", "["); - JsonParser parser = createParser(MODE_READER, INPUT); + final JsonFactory f = JsonFactory.builder() + .streamReadConstraints(StreamReadConstraints.builder().maxNestingDepth(Integer.MAX_VALUE).build()) + .build(); + JsonParser parser = createParser(f, MODE_READER, INPUT); try { while (true) { parser.nextToken(); diff --git a/src/test/java/com/fasterxml/jackson/core/read/ArrayParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/ArrayParsingTest.java index f7067e4198..98fe902349 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/ArrayParsingTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/ArrayParsingTest.java @@ -1,6 +1,7 @@ package com.fasterxml.jackson.core.read; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.exc.StreamConstraintsException; import com.fasterxml.jackson.core.json.JsonReadFeature; /** @@ -106,6 +107,20 @@ public void testNotMissingValueByEnablingFeature() throws Exception _testNotMissingValueByEnablingFeature(false); } + public void testDeepNesting() throws Exception + { + final String DOC = createDeepNestedDoc(1050); + try (JsonParser jp = createParserUsingStream(new JsonFactory(), DOC, "UTF-8")) { + JsonToken jt; + while ((jt = jp.nextToken()) != null) { + + } + fail("expected StreamConstraintsException"); + } catch (StreamConstraintsException e) { + assertEquals("Depth (1001) exceeds the maximum allowed nesting depth (1000)", e.getMessage()); + } + } + private void _testMissingValueByEnablingFeature(boolean useStream) throws Exception { String DOC = "[ \"a\",,,,\"abc\", ] "; @@ -182,4 +197,18 @@ private void _testNotMissingValueByEnablingFeature(boolean useStream) throws Exc jp.close(); } + + private String createDeepNestedDoc(final int depth) { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < depth; i++) { + sb.append("{ \"a\": ["); + } + sb.append(" \"val\" "); + for (int i = 0; i < depth; i++) { + sb.append("]}"); + } + sb.append("]"); + return sb.toString(); + } }