package tools.jackson.core; import tools.jackson.core.exc.StreamConstraintsException; import tools.jackson.core.exc.StreamReadException; /** * The constraints to use for streaming reads: used to guard against malicious * input by preventing processing of "too big" input constructs (values, * structures). * Constraints are registered with {@code TokenStreamFactory} (such as * {@code JsonFactory}); if nothing explicitly specified, default * constraints are used. *
* Currently constrained aspects, with default settings, are: *
* Note: was {@code 1000} in Jackson 2.x; lowered in 3.0. */ public static final int DEFAULT_MAX_DEPTH = 500; /** * Default setting for maximum document length: * see {@link Builder#maxDocumentLength} for details. */ public static final long DEFAULT_MAX_DOC_LEN = -1L; /** * Default setting for maximum token count: * see {@link Builder#maxTokenCount(long)} for details. */ public static final long DEFAULT_MAX_TOKEN_COUNT = -1L; /** * Default setting for maximum number length: see {@link Builder#maxNumberLength(int)} * for details. */ public static final int DEFAULT_MAX_NUM_LEN = 1000; /** * Default setting for maximum string length: see {@link Builder#maxStringLength(int)} * for details. */ public static final int DEFAULT_MAX_STRING_LEN = 100_000_000; /** * Default setting for maximum name length: see {@link Builder#maxNameLength(int)} * for details. * * @since 2.16 */ public static final int DEFAULT_MAX_NAME_LEN = 50_000; /** * Limit for the maximum magnitude of Scale of {@link java.math.BigDecimal} that can be * converted to {@link java.math.BigInteger}. *
* "100k digits ought to be enough for anybody!" */ private static final int MAX_BIGINT_SCALE_MAGNITUDE = 100_000; protected final int _maxNestingDepth; protected final long _maxDocLen; protected final long _maxTokenCount; protected final int _maxNumLen; protected final int _maxStringLen; protected final int _maxNameLen; private static StreamReadConstraints DEFAULT = new StreamReadConstraints(DEFAULT_MAX_DEPTH, DEFAULT_MAX_DOC_LEN, DEFAULT_MAX_TOKEN_COUNT, DEFAULT_MAX_NUM_LEN, DEFAULT_MAX_STRING_LEN, DEFAULT_MAX_NAME_LEN); /** * Override the default StreamReadConstraints. These defaults are only used when {@link TokenStreamFactory} * instances are not configured with their own StreamReadConstraints. *
* Library maintainers should not set this as it will affect other code that uses Jackson.
* Library maintainers who want to configure StreamReadConstraints for the Jackson usage within their
* lib should create ObjectMapper instances that have a {@link TokenStreamFactory} instance with
* the required StreamReadConstraints.
*
* This method is meant for users delivering applications. If they use this, they set it when they start * their application to avoid having other code initialize their mappers before the defaults are overridden. * * @param streamReadConstraints new default for StreamReadConstraints (a null value will reset to built-in default) * @see #defaults() * @see #builder() * @since v2.15.2 */ public static void overrideDefaultStreamReadConstraints(final StreamReadConstraints streamReadConstraints) { if (streamReadConstraints == null) { DEFAULT = new StreamReadConstraints(DEFAULT_MAX_DEPTH, DEFAULT_MAX_DOC_LEN, DEFAULT_MAX_TOKEN_COUNT, DEFAULT_MAX_NUM_LEN, DEFAULT_MAX_STRING_LEN, DEFAULT_MAX_NAME_LEN); } else { DEFAULT = streamReadConstraints; } } public static final class Builder { private int maxNestingDepth; private long maxDocLen; private long maxTokenCount; private int maxNumLen; private int maxStringLen; private int maxNameLen; /** * 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 */ 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 allowed document length (for positive values over 0) or * indicate that any length is acceptable ({@code 0} or negative number). * The length is in input units of the input source, that is, in * {@code byte}s or {@code char}s. *
* The check is not done after every byte or character but checked when a * new chunk of data is read. This means that the check is not likely to be exact * but that documents smaller than this limit will be treated as valid. *
* @param maxDocLen the maximum allowed document length if positive number above 0; otherwise * ({@code 0} or negative number) means "unlimited". * * @return this builder */ public Builder maxDocumentLength(long maxDocLen) { // Negative values and 0 mean "unlimited", mark with -1L if (maxDocLen <= 0L) { maxDocLen = -1L; } this.maxDocLen = maxDocLen; return this; } /** * Sets the maximum allowed token count (for positive values over 0) or * indicate that any count is acceptable ({@code 0} or negative number). * ** A token is a single unit of input, such as a number, a string, an object * start or end, or an array start or end. *
* * @param maxTokenCount the maximum allowed token count if positive number above 0; otherwise * ({@code 0} or negative number) means "unlimited". * * @return this builder * * @since 2.18 */ public Builder maxTokenCount(long maxTokenCount) { // Negative values and 0 mean "unlimited", mark with -1L if (maxTokenCount <= 0L) { maxTokenCount = -1L; } this.maxTokenCount = maxTokenCount; return this; } /** * Sets the maximum number length (in chars or bytes, depending on input context). * The default is 1000. * * @param maxNumLen the maximum number length (in chars or bytes, depending on input context) * * @return this builder * @throws IllegalArgumentException if the maxNumLen is set to a negative value */ public Builder maxNumberLength(final int maxNumLen) { if (maxNumLen < 0) { throw new IllegalArgumentException("Cannot set maxNumberLength to a negative value"); } this.maxNumLen = maxNumLen; return this; } /** * Sets the maximum string length (in chars or bytes, depending on input context). * The default is {@link #DEFAULT_MAX_STRING_LEN}. This limit is not exact, the limit * is applied when we increase * internal buffer sizes and an exception will happen at sizes greater than this limit. Some * text values that are a little bigger than the limit may be treated as valid but no text * values with sizes less than or equal to this limit will be treated as invalid. ** Setting this value to lower than the {@link #maxNumberLength(int)} is not recommended. *
** NOTE: Jackson 2.15.0 initially used a lower setting ({@code 5,000,000}); and versions * up to 3.0 {@code 20,000,000}. * Jackson 3.1 further raised this to {@code 100,000,000}. *
* NOTE: Setting this to {@link Integer#MAX_VALUE} effectively DISABLES limit. * * @param maxStringLen the maximum string length (in chars or bytes, depending on input context) * * @return this builder * @throws IllegalArgumentException if the maxStringLen is set to a negative value */ public Builder maxStringLength(final int maxStringLen) { if (maxStringLen < 0) { throw new IllegalArgumentException("Cannot set maxStringLen to a negative value"); } this.maxStringLen = maxStringLen; return this; } /** * Sets the maximum name length (in chars or bytes, depending on input context). * The default is 50,000. This limit is not exact, the limit is applied when we increase * internal buffer sizes and an exception will happen at sizes greater than this limit. Some * text values that are a little bigger than the limit may be treated as valid but no text * values with sizes less than or equal to this limit will be treated as invalid. * * @param maxNameLen the maximum string length (in chars or bytes, depending on input context) * * @return this builder * @throws IllegalArgumentException if the maxStringLen is set to a negative value */ public Builder maxNameLength(final int maxNameLen) { if (maxNameLen < 0) { throw new IllegalArgumentException("Cannot set maxNameLen to a negative value"); } this.maxNameLen = maxNameLen; return this; } Builder() { this(DEFAULT_MAX_DEPTH, DEFAULT_MAX_DOC_LEN, DEFAULT_MAX_TOKEN_COUNT, DEFAULT_MAX_NUM_LEN, DEFAULT_MAX_STRING_LEN, DEFAULT_MAX_NAME_LEN); } Builder(final int maxNestingDepth, final long maxDocLen, final long maxTokenCount, final int maxNumLen, final int maxStringLen, final int maxNameLen) { this.maxNestingDepth = maxNestingDepth; this.maxDocLen = maxDocLen; this.maxTokenCount = maxTokenCount; this.maxNumLen = maxNumLen; this.maxStringLen = maxStringLen; this.maxNameLen = maxNameLen; } Builder(StreamReadConstraints src) { maxNestingDepth = src._maxNestingDepth; maxDocLen = src._maxDocLen; maxTokenCount = src._maxTokenCount; maxNumLen = src._maxNumLen; maxStringLen = src._maxStringLen; maxNameLen = src._maxNameLen; } public StreamReadConstraints build() { return new StreamReadConstraints(maxNestingDepth, maxDocLen, maxTokenCount, maxNumLen, maxStringLen, maxNameLen); } } /* /********************************************************************** /* Life-cycle /********************************************************************** */ /** * @param maxNestingDepth Maximum input document nesting to allow * @param maxDocLen Maximum input document length to allow * @param maxNumLen Maximum number representation length to allow * @param maxStringLen Maximum String value length to allow * @param maxNameLen Maximum Object property name length to allow * @param maxTokenCount Maximum number of tokens to allow */ protected StreamReadConstraints(int maxNestingDepth, long maxDocLen, long maxTokenCount, int maxNumLen, int maxStringLen, int maxNameLen) { _maxNestingDepth = maxNestingDepth; _maxDocLen = maxDocLen; _maxTokenCount = maxTokenCount; _maxNumLen = maxNumLen; _maxStringLen = maxStringLen; _maxNameLen = maxNameLen; } public static Builder builder() { return new Builder(); } /** * @return the default {@link StreamReadConstraints} (when none is set on the {@link TokenStreamFactory} explicitly) * @see #overrideDefaultStreamReadConstraints */ public static StreamReadConstraints defaults() { return DEFAULT; } /** * @return New {@link Builder} initialized with settings of this constraints * instance */ public Builder rebuild() { return new Builder(this); } /* /********************************************************************** /* Accessors /********************************************************************** */ /** * Accessor for maximum depth. * see {@link Builder#maxNestingDepth(int)} for details. * * @return Maximum allowed depth */ public int getMaxNestingDepth() { return _maxNestingDepth; } /** * Accessor for maximum document length. * see {@link Builder#maxDocumentLength(long)} for details. * * @return Maximum allowed depth */ public long getMaxDocumentLength() { return _maxDocLen; } /** * Convenience method, basically same as: *
* getMaxDocumentLength() > 0L
*
*
* @return {@code True} if this constraints instance has a limit for maximum
* document length to enforce; {@code false} otherwise.
*/
public boolean hasMaxDocumentLength() {
return _maxDocLen > 0L;
}
/**
* Accessor for maximum token count.
* see {@link Builder#maxTokenCount(long)} for details.
*
* @return Maximum allowed token count
* @since 2.18
*/
public long getMaxTokenCount() {
return _maxTokenCount;
}
/**
* Convenience method, basically same as:
*
* getMaxTokenCount() > 0L
*
*
* @return {@code True} if this constraints instance has a limit for maximum
* token count to enforce; {@code false} otherwise.
* @since 2.18
*/
public boolean hasMaxTokenCount() {
return _maxTokenCount > 0L;
}
/**
* Accessor for maximum length of numbers to decode.
* see {@link Builder#maxNumberLength(int)} for details.
*
* @return Maximum allowed number length
*/
public int getMaxNumberLength() {
return _maxNumLen;
}
/**
* Accessor for maximum length of strings to decode.
* see {@link Builder#maxStringLength(int)} for details.
*
* @return Maximum allowed string length
*/
public int getMaxStringLength() {
return _maxStringLen;
}
/**
* Accessor for maximum length of names to decode.
* see {@link Builder#maxNameLength(int)} for details.
*
* @return Maximum allowed name length
*/
public int getMaxNameLength() {
return _maxNameLen;
}
/**
* Accessor for maximum magnitude (absolute value) of {@link java.math.BigDecimal}
* scale that may be converted to {@link java.math.BigInteger}:
* see {@link #validateBigIntegerScale(int)} for details.
** Note: unlike other limits, this one is not configurable (yet). * * @return Maximum allowed scale magnitude * * @since 3.3 */ public int getMaxBigIntegerScale() { return MAX_BIGINT_SCALE_MAGNITUDE; } /* /********************************************************************** /* Convenience methods for validation, document limits /********************************************************************** */ /** * 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 _constructException( "Document nesting depth (%d) exceeds the maximum allowed (%d, from %s)", depth, _maxNestingDepth, _constrainRef("getMaxNestingDepth")); } } /** * Convenience method that can be used to verify that the * document length does not exceed the maximum specified by this * constraints object (if any): if it does, a * {@link StreamConstraintsException} * is thrown. * * @param len Current length of processed document content * * @throws StreamConstraintsException If length exceeds maximum * * @since 2.16 */ public void validateDocumentLength(long len) throws StreamConstraintsException { if ((len > _maxDocLen) // Note: -1L used as marker for "unlimited" && (_maxDocLen > 0L)) { throw _constructException( "Document length (%d) exceeds the maximum allowed (%d, from %s)", len, _maxDocLen, _constrainRef("getMaxDocumentLength")); } } /** * Convenience method that can be used to verify that the * token count does not exceed the maximum specified by this * constraints object (if any): if it does, a * {@link StreamConstraintsException} * is thrown. * * @param count Current token count for processed document content * * @throws StreamConstraintsException If length exceeds maximum * * @since 2.18 */ public void validateTokenCount(long count) throws StreamConstraintsException { // for performance reasons, it is assumed that users check hasMaxTokenCount() // before calling this method - this method will not work properly if hasMaxTokenCount() is false if (count > _maxTokenCount) { throw _constructException( "Token count (%d) exceeds the maximum allowed (%d, from %s)", count, _maxTokenCount, _constrainRef("getMaxTokenCount")); } } /* /********************************************************************** /* Convenience methods for validation, token lengths /********************************************************************** */ /** * Convenience method that can be used to verify that a floating-point * number of specified length does not exceed maximum specified by this * constraints object: if it does, a * {@link StreamConstraintsException} * is thrown. * * @param length Length of number in input units * * @throws StreamConstraintsException If length exceeds maximum */ public void validateFPLength(int length) throws StreamConstraintsException { if (length > _maxNumLen) { throw _constructException( "Number value length (%d) exceeds the maximum allowed (%d, from %s)", length, _maxNumLen, _constrainRef("getMaxNumberLength")); } } /** * Convenience method that can be used to verify that an integer * number of specified length does not exceed maximum specific by this * constraints object: if it does, a * {@link StreamConstraintsException} * is thrown. * * @param length Length of number in input units * * @throws StreamConstraintsException If length exceeds maximum */ public void validateIntegerLength(int length) throws StreamConstraintsException { if (length > _maxNumLen) { throw _constructException( "Number value length (%d) exceeds the maximum allowed (%d, from %s)", length, _maxNumLen, _constrainRef("getMaxNumberLength")); } } /** * Convenience method that can be used to verify that a String * of specified length does not exceed maximum specific by this * constraints object: if it does, a * {@link StreamConstraintsException} * is thrown. * * @param length Length of string in input units * * @throws StreamReadException If length exceeds maximum */ public void validateStringLength(int length) throws StreamConstraintsException { if (length > _maxStringLen) { throw _constructException( "String value length (%d) exceeds the maximum allowed (%d, from %s)", length, _maxStringLen, _constrainRef("getMaxStringLength")); } } /** * Variant of {@link #validateStringLength} but takes {@code long} instead * {@code int}. *
* NOTE: value of {@link Integer#MAX_VALUE} is considered special value * meaning "no limit". * * @param length Length to validate against limit * * @throws StreamConstraintsException */ public void validateStringLengthLong(long length) throws StreamConstraintsException { if (length > _maxStringLen && _maxStringLen != Integer.MAX_VALUE) { throw _constructException( "String value length (%d) exceeds the maximum allowed (%d, from %s)", length, _maxStringLen, _constrainRef("getMaxStringLength")); } } /** * Convenience method that can be used to verify that a name * of specified length does not exceed maximum specific by this * constraints object: if it does, a * {@link StreamConstraintsException} * is thrown. * * @param length Length of name in input units * * @throws StreamConstraintsException If length exceeds maximum */ public void validateNameLength(int length) throws StreamConstraintsException { if (length > _maxNameLen) { throw _constructException( "Name length (%d) exceeds the maximum allowed (%d, from %s)", length, _maxNameLen, _constrainRef("getMaxNameLength")); } } /* /********************************************************************** /* Convenience methods for validation, other /********************************************************************** */ /** * Convenience method that can be used to verify that the scale of a * {@link java.math.BigDecimal} to convert to {@link java.math.BigInteger} * does not exceed the maximum magnitude specified by this constraints * object: if it does, a {@link StreamConstraintsException} is thrown. * * @param scale Scale (possibly negative) of {@link java.math.BigDecimal} to convert * * @throws StreamConstraintsException If magnitude (absolute value) of scale exceeds maximum * allowed */ public void validateBigIntegerScale(int scale) throws StreamConstraintsException { // 15-Sep-2026, tatu: [core#1715] Widen to `long`: `Math.abs(Integer.MIN_VALUE)` // is itself negative, and would slip past the check below final long absScale = Math.abs((long) scale); final int limit = getMaxBigIntegerScale(); if (absScale > limit) { throw _constructException( "BigDecimal scale (%d) magnitude exceeds the maximum allowed (%d, from %s)", scale, limit, _constrainRef("getMaxBigIntegerScale")); } } /* /********************************************************************** /* Error reporting /********************************************************************** */ // @since 2.16 protected StreamConstraintsException _constructException(String msgTemplate, Object... args) throws StreamConstraintsException { throw new StreamConstraintsException(String.format(msgTemplate, args)); } // @since 2.16 protected String _constrainRef(String method) { return "`StreamReadConstraints."+method+"()`"; } }