Skip to content

Commit

Permalink
apply constraint to nesting depth (#943)
Browse files Browse the repository at this point in the history
  • Loading branch information
pjfanning authored Mar 6, 2023
1 parent 566fff4 commit 54f78a9
Show file tree
Hide file tree
Showing 11 changed files with 166 additions and 50 deletions.
14 changes: 14 additions & 0 deletions src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -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.
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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;
}
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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));
}
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/fasterxml/jackson/core/base/ParserBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -754,17 +754,17 @@ 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;
_nextToken = null;

// 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);
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand Down
Loading

0 comments on commit 54f78a9

Please sign in to comment.