
This repository provides a comprehensive security remediation of denial-of-service and allocation of resources without limits or throttling security vulnerabilities reported in CVE-2025-52999, GHSA-2m67-wjpj-xhg9 and sonatype-2022-6438 while maintaining full compatibility with jackson‑core 2.13.5.
This branch (2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9) contains a comprehensive security remediation of denial-of-service (DoS) and Allocation of Resources Without Limits or Throttling vulnerabilities targeting jackson-core 2.13.5. It introduces the StreamReadConstraints API —
aligned with the API introduced in jackson-core 2.15.0 but extended with broader parser coverage
and additional attack-vector protections — addressing a nesting-depth exhaustion attack
(CVE-2025-52999), Allocation of Resources Without Limits or Throttling (SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924), a document length constraint bypass (SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 / GHSA-2m67-wjpj-xhg9), and a numeric token length exhaustion attack (Sonatype-2022-6438) while remaining compatible with the public API surface of jackson-core version 2.13.5.
| Branch | Vulnerabilities Addressed |
|---|---|
2.13.5-CVE-2025-52999-sonatype-2022-6438 | CVE-2025-52999, Sonatype-2022-6438, SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924 |
2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9 | All of the above + GHSA-2m67-wjpj-xhg9 (document length constraint bypass) |
The original branch (2.13.5-CVE-2025-52999-sonatype-2022-6438) remediates three vulnerabilities
and is preserved on the sasso remote. This branch extends it with the additional remediation of
GHSA-2m67-wjpj-xhg9,
which enforces maxDocumentLength across all parser paths.
NVD Entry
Official NVD Description
jackson-core contains core low-level incremental ("streaming") parser and generator abstractions used by Jackson Data Processor. In versions prior to 2.15.0, if a user parses an input file and it has deeply nested data, Jackson could end up throwing a
StackOverflowErrorif the depth is particularly large. jackson-core 2.15.0 contains a configurable limit for how deep Jackson will traverse in an input document, defaulting to an allowable depth of 1,000. jackson-core will throw aStreamConstraintsExceptionif the limit is reached. jackson-databind also benefits from this change because it uses jackson-core to parse JSON inputs. As a workaround, users should avoid parsing input files from untrusted sources.
Workaround: avoid parsing JSON input from untrusted sources until the remediated version is deployed.
Root cause: Prior to 2.15.0, JsonParser imposed no limit on how deeply nested a JSON document
could be. Every array [ or object { token caused JsonReadContext.createChildArrayContext() /
createChildObjectContext() to allocate a new context node on the heap and increment a reference
chain. An attacker can craft a document with tens of thousands of nested levels, causing the Java Virtual Machine
to exhaust its thread-stack or heap memory.
Vulnerable code path:
The same missing check is reached through each of the four parser implementations:
JsonParser.nextToken() // common entry point
│
├─ ReaderBasedJsonParser → _parsePunctuationMark()
├─ UTF8StreamJsonParser → _parsePunctuationMark()
├─ UTF8DataInputJsonParser → _parsePunctuationMark()
└─ NonBlockingJsonParserBase → _startArrayScope() / _startObjectScope()
│
▼
_parsingContext.createChildArrayContext() // '[' encountered
_parsingContext.createChildObjectContext() // '{' encountered
⚠ no depth check — context chain grows without bound
All four JsonParser implementations share this flaw. The attack is equally exploitable through
any of them — whether input arrives via InputStream, Reader, DataInput, or the async
non-blocking feeder API.
Attack vectors:
Key observations:
[ and ] tokens are required; no keys, values, or whitespace. A 2,002-byte payload of 1,001 bracket pairs is sufficient to exceed the default limit of 1,000.{ additionally allocates a JsonReadContext key slot on top of the depth-chain node, compounding memory consumption at extreme depths.[[[ or {{{ sequences are blind to alternating-token nesting; the parser's depth counter increments identically regardless of token type.NonBlockingJsonParser and the ByteArrayFeeder API. The document may be delivered in arbitrarily small chunks; NonBlockingJsonParserBase._startArrayScope() / _startObjectScope() increment the depth counter on every context-opening token regardless of how the bytes arrive, accumulating depth across multiple calls.All three nesting strategies are reachable through any of the four JsonParser implementations.
With 2.13.5, parsing succeeds silently; with this remediation, all three vectors throw
StreamConstraintsException: Depth (1001) exceeds the maximum allowed nesting depth (1000).
Security Details
Vulnerable Methods (as identified by Sonatype)
Each of these methods processes the raw digit buffer without first validating its length. Passing a
sufficiently long numeric token triggers unbounded heap allocation and CPU exhaustion when the JVM
attempts to instantiate a BigInteger or BigDecimal from the unconstrained buffer contents.
Root cause: Prior to 2.15.0, JsonParser imposed no limit on the byte length of integer, scientific notation, simple floating-point, or compound floating-point tokens. When a parser allocates its internal _textBuffer to accumulate digits, an
attacker can provide a number with millions of digits, causing the buffer to grow without bound and
eventually exhaust heap memory.
Vulnerable code paths:
There are two structurally distinct vulnerable paths — one shared by the three synchronous parsers, and a second independent path through the async parser.
Path A — synchronous parsers (three implementations, one shared sink):
JsonParser.nextToken() // common entry point
│
├─ ReaderBasedJsonParser ─┐
├─ UTF8StreamJsonParser ├─→ ParserBase.resetInt() / resetFloat()
└─ UTF8DataInputJsonParser ─┘ │
▼
_textBuffer.contentsAsString()
⚠ no length check — buffer grows without bound
Path B — async parser (independent code path, separately unprotected):
NonBlockingJsonParser.nextToken()
│
├─ _startPositiveNumber() / _startNegativeNumber() // integer paths
├─ _finishNumberIntegralPart()
├─ _startFloat() // floating-point paths
├─ _finishFloatFraction()
└─ _finishFloatExponent()
│
▼
writes _intLength / _fractLength / _expLength directly
⚠ never calls ParserBase.resetInt() / resetFloat()
⚠ constraint validation bypassed entirely
The non-blocking (async) parser is a particularly notable attack surface: it contains its own
digit-accumulation loops in _startPositiveNumber, _startNegativeNumber,
_finishNumberIntegralPart, _startFloat, _finishFloatFraction, and _finishFloatExponent
that set _intLength / _fractLength / _expLength directly without ever going through
ParserBase.resetInt() or resetFloat(). This means the upstream PR #827 fix (which added
validation only to ParserBase) left NonBlockingJsonParser completely unprotected.
This was discovered during extended attack-surface analysis and remediated in this branch.
Attack vectors:
Key observations:
- is excluded from digit accumulation; -999… and 999… produce identical intLen values and trip the constraint at the same threshold.BigDecimal with scale ±10^1001, demanding unbounded intermediate heap allocation despite the token's small size.fractLen = 500 and expLen = 500, neither component individually reaches the 1,000-digit threshold. The unified check validateFPLength(intLen + fractLen + expLen) is the only defence that closes this gap.NonBlockingJsonParser via ByteArrayFeeder. Unlike the synchronous parsers, accumulates digits in private loops (, , , , ) that write / / directly, bypassing and entirely. The upstream PR #827 — which only modified — therefore left this parser fully unprotected, requiring six independent call-site patches in this remediation.All four token shapes and the non-blocking bypass are rejected at tokenization time — before any BigDecimal or BigInteger
is constructed — by validateIntegerLength and validateFPLength in ParserBase (synchronous
parsers) and at six dedicated call sites in NonBlockingJsonParser (async parser). With 2.13.5, all four token shapes are accepted silently; with this remediation, every parser throws
StreamConstraintsException: Number length (N) exceeds the maximum length (1000).
Note on
UTF8DataInputJsonParserwith large payloads: The DataInput parser has a pre-existing internal buffer limit of 65 536 bytes (jackson-core#493). Documents exceeding this size (e.g., 199,999-digit PoC payloads ≈ 200 KB) trigger anArrayIndexOutOfBoundsExceptionbefore the length constraint can fire. The numeric length protection forUTF8DataInputJsonParseris therefore verified with shorter payloads (≤ 1,001 digits) where the bug is still reproducible.
Security Details
Root cause: Even when StreamReadConstraints.maxDocumentLength is configured, versions prior to
2.18.7 / 2.21.2 do not enforce the document length limit in any parser path. The blocking parsers
(UTF8StreamJsonParser, ReaderBasedJsonParser) never validate cumulative bytes read against the
configured limit. The async parser (NonBlockingJsonParser) similarly lacks validation in
feedInput(). The UTF8DataInputJsonParser has no mechanism to track total bytes consumed.
In jackson-core 2.13.5, maxDocumentLength did not exist at all, meaning there was no way to
constrain document size. This remediation introduces the maxDocumentLength field in
StreamReadConstraints and enforces it across all parser paths.
Vulnerable parser paths:
Attack scenario: An attacker sends a valid but oversized JSON document (e.g., a deeply nested
or highly repetitive structure spanning gigabytes) to a service that has configured
maxDocumentLength to prevent resource exhaustion. Without enforcement, the parser processes the
entire document regardless of the configured limit, consuming unbounded memory and CPU.
All four vulnerabilities are remotely exploitable with no authentication required:
JsonParser (directly or through
Jackson Databind, which wraps jackson-core) is at risk.Upgrade to jackson-core 2.15.4 or any later stable release. All 2.15.x and 2.16+ releases
include the StreamReadConstraints API with safe defaults.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.4</version>
</dependency>
If upgrading to a later release is not immediately feasible, this branch applies a comprehensive
fix to the 2.13.5 codebase. It introduces StreamReadConstraints and StreamConstraintsException
with an API compatible with 2.15.x, hooks the constraints into all four JsonParser implementations
— including the non-blocking parser, which was not covered by the upstream fix — and enforces the
following default limits:
| Constraint | Default Limit |
|---|---|
| Maximum nesting depth | 1,000 |
| Maximum numeric token length | 1,000 digits |
| Maximum string token length | 1,000,000 characters |
Maximum BigDecimal scale magnitude | 100,000 |
StreamReadConstraints (356 lines)Immutable value object holding per-parser stream-read limits, constructed via a Builder:
// Default constraints (used by all parsers unless overridden)
StreamReadConstraints defaults = StreamReadConstraints.defaults();
// Custom constraints
StreamReadConstraints custom = StreamReadConstraints.builder()
.maxNestingDepth(500)
.maxNumberLength(2000)
.maxStringLength(5000000)
.build();
Validation methods (called by parsers on each new token):
void validateNestingDepth(int depth) throws StreamConstraintsException;
void validateIntegerLength(int length) throws StreamConstraintsException;
void validateFPLength(int length) throws StreamConstraintsException;
void validateStringLength(int length) throws StreamConstraintsException;
void validateBigIntegerScale(int scale) throws StreamConstraintsException;
Exception message format:
"Depth (%d) exceeds the maximum allowed nesting depth (%d)""Number length (%d) exceeds the maximum length (%d)""String length (%d) exceeds the maximum length (%d)""BigDecimal scale (%d) magnitude exceeds maximum allowed (%d)"StreamConstraintsException (52 lines)Extends StreamReadException (itself a JsonProcessingException). Thrown exclusively by
StreamReadConstraints validation methods.
base/ParserBase.javaAdded _streamReadConstraints field (defaults to StreamReadConstraints.defaults()).
resetInt() and resetFloat() now validate numeric token length immediately after the token
has been accumulated:
protected void resetInt(boolean negative, int intLen) throws IOException {
_streamReadConstraints.validateIntegerLength(intLen);
// … existing reset logic …
}
protected void resetFloat(boolean negative, int intLen, int decLen, int expLen) throws IOException {
int totalLen = intLen + decLen + expLen;
_streamReadConstraints.validateFPLength(totalLen);
// … existing reset logic …
}
New helper methods _createChildArrayContext() and _createChildObjectContext() wrap context
creation with a depth check:
protected JsonReadContext _createChildArrayContext(int line, int col) throws IOException {
_streamReadConstraints.validateNestingDepth(_parsingContext.getNestingDepth() + 1);
return _parsingContext.createChildArrayContext(line, col);
}
All four parsers now call the new depth-checking helpers instead of accessing
_parsingContext.createChild*() directly:
json/ReaderBasedJsonParser.javajson/UTF8StreamJsonParser.javajson/UTF8DataInputJsonParser.javajson/async/NonBlockingJsonParserBase.javaJsonStreamContext.javaAdded getNestingDepth() that walks the parent chain to compute absolute depth:
public int getNestingDepth() {
int depth = 0;
JsonStreamContext curr = this;
while ((curr = curr.getParent()) != null) {
depth++;
}
return depth;
}
json/JsonReadContext.javacreateChildArrayContext() and createChildObjectContext() signatures updated to propagate
throws IOException.
json/async/NonBlockingJsonParser.java (Sonatype-2022-6438 only)This is the principal additional fix discovered beyond the upstream PR #827 scope. Six
number-completion sites that bypassed ParserBase.resetInt() were individually remediated:
All sites are reachable: the fast-path methods handle the case where the full number is
available in a single feedInput() call; the MINOR_* resume-state cases handle the chunked
case where number digits arrive across multiple calls. Both paths must be guarded.
Parser modes key:
ALL_STREAMING_MODES=UTF8StreamJsonParser(stream),UTF8StreamJsonParser(throttled),ReaderBasedJsonParserALL_MODES= above three +UTF8DataInputJsonParsernon-blocking=NonBlockingJsonParserviaByteArrayFeederstream=UTF8StreamJsonParser(defaultJsonFactory.createParser)
Tests run: 957, Failures: 0, Errors: 0, Skipped: 0
All 957 tests pass on the remediated build. Breakdown of new security tests added:
Build command:
./mvnw test
The CVE test methods were designed to fail on the 2.13.5 codebase and pass on the remediated build.
NumberOverflowTest#testSonatype_2022_6438 against 2.13.5:
FAIL — Sonatype-2022-6438 VULNERABILITY PRESENT: parser returned VALUE_NUMBER_INT
for a 1001-digit integer — number length limit is not enforced
ArrayParsingTest#testCVE_2025_52999 against 2.13.5:
FAIL — CVE-2025-52999 VULNERABILITY PRESENT: StackOverflowError after 20000 nesting
levels — parser enforces no depth limit (2.13.5)
Both tests on the remediated build: PASS.
// Build a 1001-level nested array document (just over the limit)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1001; i++) sb.append('[');
for (int i = 0; i < 1001; i++) sb.append(']');
JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createParser(sb.toString());
try {
while (parser.nextToken() != null) { } // ← throws on 1001st '['
System.err.println("VULNERABLE: no exception thrown");
} catch (StreamConstraintsException e) {
System.out.println("REMEDIATED: " + e.getMessage());
// Depth (1001) exceeds the maximum allowed nesting depth (1000)
} finally {
parser.close();
}
All four token shapes trigger validateFPLength / validateIntegerLength before any
BigDecimal / BigInteger conversion is attempted.
JsonFactory factory = new JsonFactory();
// ── 1. Long integer ──────────────────────────────────────────────────────────
String longInt = "9".repeat(1001); // 1001-digit integer
// Negative form works identically: "-" + "9".repeat(1001)
// ── 2. Long fractional part (floating-point) ─────────────────────────────────
// intLen=1, fractLen=1001, total=1002
String longFloat = "0." + "9".repeat(1001);
// ── 3. Long exponent / scientific notation ───────────────────────────────────
// intLen=1, fractLen=0, expLen=1001, total=1002 — only 1,004 bytes on the wire
String longExp = "1e" + "9".repeat(1001);
// ── 4. Combined fractional + exponent ────────────────────────────────────────
// intLen=1, fractLen=500, expLen=500, total=1001 — neither part alone is over the limit
String combined = "0." + "9".repeat(500) + "e" + "9".repeat(500);
for (String payload : new String[]{ longInt, longFloat, longExp, combined }) {
JsonParser parser = factory.createParser("[" + payload + "]");
try {
parser.nextToken(); // START_ARRAY
parser.nextToken(); // ← throws on the number token
System.err.println("VULNERABLE: " + payload.substring(0, 20) + "…");
} catch (StreamConstraintsException e) {
System.out.println("REMEDIATED: " + e.getMessage());
// Number length (N) exceeds the maximum length (1000)
} finally {
parser.close();
}
}
<!-- Maven -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.4</version>
</dependency>
<!-- Or with Jackson BOM -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.15.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
With 2.15+, you can also adjust limits at runtime if your application legitimately needs deeper nesting or longer numbers:
JsonFactory factory = JsonFactory.builder()
.streamReadConstraints(StreamReadConstraints.builder()
.maxNestingDepth(2000)
.maxNumberLength(10000)
.maxDocumentLength(50_000_000L)
.build())
.build();
git clone https://github.com/sassoftware/jackson-core.git
cd jackson-core
git checkout 2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9
./mvnw install -DskipTests
Then update your project's dependency to use the locally installed 2.13.5-SNAPSHOT artifact, or
deploy it to your internal artifact repository.
Total: 16 files changed, 1,606 insertions (as reported by git diff jackson-core-2.13.5 --stat).
StreamReadConstraints API (2.15 javadoc): https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.15.4/com/fasterxml/jackson/core/StreamReadConstraints.htmlAny application that:
jackson-databind)is vulnerable to both attacks.
Even after remediation, consider:
maxRequestSize in Servlet containers)
to reject extremely large request bodies before the parser is invoked.StreamReadConstraints if your application legitimately requires larger or deeper
documents — tune limits to the minimum necessary for your use case.| Dimension | Requirement |
|---|---|
| Build JDK | Java 8 (JDK 1.8) or later — this branch requires Java 8+ for build and test |
| Minimum runtime JRE | Java 8 or later |
| Maven | 3.6.3 or later (bundled wrapper ./mvnw satisfies this automatically) |
Note: The original jackson-core 2.13.5 release targeted Java 6 (
-source 1.6 -target 1.6). As of this security branch, Java 8 or later is required for both build and runtime. No Jackson 2.13 public API surface is changed; only JDK 6/7 runtimes lose compatibility due to Maven and JDK requirements.
# Full build and test
./mvnw test
# Install to local Maven repository
./mvnw install -DskipTests
Licensed under the Apache License, Version 2.0.
Copyright 2024–2025 The Jackson Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Security Vulnerability Research and Remediation Author : Jinwoo Hwang (https://JinwooHwang.com)
| ID | Type | Severity | CVSS | Upstream Fix |
|---|
| CVE-2025-52999 | Denial of Service — unbounded nesting depth | High | 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) | jackson-core 2.15.0 |
| Sonatype-2022-6438 / SNYK-JAVA-COMFASTERXMLJACKSONCORE-7569538 | Denial of Service — unbounded numeric token length | High | 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) | jackson-core 2.15.0 |
| SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924 | Allocation of Resources Without Limits or Throttling | High | 8.7 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) | jackson-core 2.18.6, 2.21.1 or higher. |
| SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 / GHSA-2m67-wjpj-xhg9 | Allocation of Resources Without Limits or Throttling — document length constraint bypass | High | 8.7 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) | jackson-core 2.18.7, 2.21.2 or higher. |
| Version | CVE-2025-52999 | Sonatype-2022-6438 / SNYK-JAVA-COMFASTERXMLJACKSONCORE-7569538 | SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924 | SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 |
|---|
| 2.13.5 | Vulnerable | Vulnerable | Vulnerable | Vulnerable |
| 2.13.5-CVE-2025-52999-sonatype-2022-6438 | Remediated | Remediated | Remediated | Vulnerable |
| 2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9 | Remediated | Remediated | Remediated | Remediated |
| 2.14.x | Vulnerable | Vulnerable | Vulnerable | Vulnerable |
| 2.15.x | Remediated | Remediated | Vulnerable | Vulnerable |
| 2.16.x | Remediated | Remediated | Vulnerable | Vulnerable |
| 2.17.x | Remediated | Remediated | Vulnerable | Vulnerable |
| 2.18.6+ | Remediated | Remediated | Remediated | Vulnerable |
| 2.18.7+ | Remediated | Remediated | Remediated | Remediated |
| 2.21.1+ | Remediated | Remediated | Remediated | Vulnerable |
| 2.21.2+ | Remediated | Remediated | Remediated | Remediated |
| Field | Value |
|---|
| CVE ID | CVE-2025-52999 |
| Published | 2025-06-25 |
| Last Modified | 2025-06-26 |
| Source (CNA) | GitHub, Inc. |
| CVSS v4.0 Score | 8.7 HIGH — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N |
| CWE | CWE-121 — Stack-based Buffer Overflow |
| GitHub Advisory | GHSA-h46c-h94j-95f3 |
| Upstream Fix PR | jackson-core#943 |
| # | Strategy | Example payload | Depth increment | Affected parsers |
|---|
| 1 | Array nesting | [[[…]]] — 1,001 consecutive [ tokens | +1 per [ | All four |
| 2 | Object nesting | {"k":{"k":{…}}} — 1,001 consecutive { tokens | +1 per { | All four |
| 3 | Alternating nesting | [{"k":[{"k":…}]}] — 1,001 mixed [/{ tokens | +1 per [ or { | All four |
feedInput()| Field | Value |
|---|
| Sonatype ID | sonatype-2022-6438 |
| Description | jackson-core — Denial of Service (DoS) |
| Published | 2022-12-07 |
| Source | Sonatype |
| CVSS v3.1 Score | 7.5 HIGH — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-770 — Allocation of Resources Without Limits or Throttling |
| EPSS Score | 0% |
| Upstream Fix PRs | jackson-core#827, jackson-core#846 |
| Method | Notes |
|---|
com.fasterxml.jackson.core.base.ParserBase._parseSlowInt(I)V | Vulnerable param: index 0 |
com.fasterxml.jackson.core.base.ParserBase.convertNumberToBigDecimal()V | |
com.fasterxml.jackson.core.base.ParserMinimalBase.getValueAsDouble(D)D | Vulnerable param: index 0 |
com.fasterxml.jackson.core.util.TextBuffer.contentsAsDecimal() | Returns BigDecimal |
com.fasterxml.jackson.core.util.TextBuffer.contentsAsDouble(Z)D | |
com.fasterxml.jackson.core.util.TextBuffer.contentsAsFloat(Z)F |
| # | Token shape | Example payload | intLen | fractLen | expLen | Total | Constraint |
|---|
| 1 | Integer | 999… — 199,999 consecutive digits | 199,999 | 0 | 0 | 199,999 | validateIntegerLength |
| 2 | Decimal fraction | 0.999… — 1-digit integer, 1,001-digit fraction | 1 | 1,001 | 0 | 1,002 | validateFPLength |
| 3 | Scientific notation | 1e999… — 1-digit significand, 1,001-digit exponent | 1 | 0 | 1,001 | 1,002 | validateFPLength |
| 4 | Compound floating-point | 0.999…e999… — 500-digit fraction, 500-digit exponent | 1 | 500 | 500 | 1,001 | validateFPLength |
NonBlockingJsonParser_startPositiveNumber_finishNumberIntegralPart_startFloat_finishFloatFraction_finishFloatExponent_intLength_fractLength_expLengthParserBase.resetInt()resetFloat()ParserBase| Field | Value |
|---|
| Snyk ID | SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 |
| GitHub Advisory | GHSA-2m67-wjpj-xhg9 |
| Description | Allocation of Resources Without Limits or Throttling |
| Disclosed | 2026-04-04 |
| CVSS v4.0 Score | 8.7 HIGH |
| CWE | CWE-770 — Allocation of Resources Without Limits or Throttling |
| Affected Versions | [2.8.0, 2.21.2) |
| Upstream Fix | jackson-core 2.18.7, 2.21.2 or higher |
| Fix Commits | 74c9ee25 (3.x), 7ce3622f (2.18.x) |
| Parser | Path | Enforcement Point |
|---|
UTF8StreamJsonParser | InputStream → _loadMore() | Validates _currInputProcessed + count after each buffer refill and at EOF |
ReaderBasedJsonParser | Reader → _loadMore() | Same pattern as UTF8StreamJsonParser |
NonBlockingJsonParser | feedInput() | Validates _currInputProcessed + _origBufferLen after each input chunk |
UTF8DataInputJsonParser | DataInput | Fail-fast: throws StreamConstraintsException if maxDocumentLength is configured |
| Method | Validation added |
|---|
_startPositiveNumber() — fast-path return | validateIntegerLength(_intLength) |
_startNegativeNumber() — fast-path return | validateIntegerLength(_intLength) |
_finishNumberIntegralPart() — final return | validateIntegerLength(_intLength) |
_finishToken() case MINOR_NUMBER_INTEGER_DIGITS | validateIntegerLength(_intLength) |
_startFloat() / _finishFloatFraction() / _finishFloatExponent() final returns | validateFPLength(_intLength + _fractLength + _expLength) |
_finishToken() cases MINOR_NUMBER_FRACTION_DIGITS / MINOR_NUMBER_EXPONENT_DIGITS | validateFPLength(...) |
| Test File | Test Method | Parser modes | Covers |
|---|
read/ArrayParsingTest.java | testCVE_2025_52999 | stream | Recursive traversal simulating real app use; confirms StreamConstraintsException at depth 1001 (remediated) and StackOverflowError at depth 20,000 (2.13.5) |
read/ArrayParsingTest.java | testCustomNestingDepthConstraint | direct API | StreamReadConstraints.builder().maxNestingDepth(5) — accessor, at-limit pass, over-limit throws |
read/ArrayParsingTest.java | testObjectNestingDepthLimit | stream | Objects exactly at limit 1000 (pass), 1001-level objects throw |
read/ArrayParsingTest.java | testDataInputParserDepthLimit | UTF8DataInputJsonParser | UTF8DataInputJsonParser enforces 1001-level depth limit |
read/ArrayParsingTest.java | testNonBlockingParserDepthLimit | non-blocking | NonBlockingJsonParser enforces 1001-level depth limit |
| Test File | Test Method | Parser modes | Covers |
|---|
read/NumberOverflowTest.java | testSonatype_2022_6438 | ALL_MODES | Integer at limit (pass), integer over limit (fail), float at limit (pass), float over limit (fail), scientific notation exponent at limit (pass), exponent over limit (fail) — all four parsers |
read/NumberOverflowTest.java | testNonBlockingParserNumericLengthLimit | non-blocking | NonBlockingJsonParser enforces numeric length |
read/NumberOverflowTest.java | testNonBlockingParserExponentLengthLimit | non-blocking | NonBlockingJsonParser enforces scientific notation exponent length via validateFPLength |
read/NumberOverflowTest.java | testCustomMaxNumberLengthConstraint | direct API | StreamReadConstraints.builder().maxNumberLength(5) — accessor, at-limit pass, over-limit throws for both validateIntegerLength() and validateFPLength(), error message format |
| Test File | Test Method | Parser modes | Covers |
|---|
constraints/LargeDocReadTest.java | testInputStreamExceedsLimit | UTF8StreamJsonParser | InputStream parser enforces maxDocumentLength — 20K doc rejected with 10K limit |
constraints/LargeDocReadTest.java | testInputStreamUnderLimitSucceeds | UTF8StreamJsonParser | InputStream parser accepts doc within limit |
constraints/LargeDocReadTest.java | testReaderExceedsLimit | ReaderBasedJsonParser | Reader parser enforces maxDocumentLength — 20K doc rejected with 10K limit |
constraints/LargeDocReadTest.java | testReaderUnderLimitSucceeds | ReaderBasedJsonParser | Reader parser accepts doc within limit |
constraints/LargeDocReadTest.java | testAsyncExceedsLimit | NonBlockingJsonParser | Async parser enforces maxDocumentLength — 20K doc rejected in feedInput() |
constraints/LargeDocReadTest.java | testAsyncUnderLimitSucceeds | NonBlockingJsonParser | Async parser accepts doc within limit |
constraints/LargeDocReadTest.java | testDataInputWithDocLengthLimitFails | UTF8DataInputJsonParser | DataInput parser fails fast when maxDocumentLength is configured |
constraints/LargeDocReadTest.java | testDataInputWithoutDocLengthLimitWorks | UTF8DataInputJsonParser | DataInput parser works normally without maxDocumentLength configured |
constraints/LargeDocReadTest.java | testDefaultFactoryNoLimit | UTF8StreamJsonParser | Default factory (no limit) accepts large documents |
| Test File | New / Extended Methods |
|---|
read/ArrayParsingTest.java | testCVE_2025_52999, testCustomNestingDepthConstraint, testObjectNestingDepthLimit, testDataInputParserDepthLimit, testNonBlockingParserDepthLimit |
read/NumberOverflowTest.java | testSonatype_2022_6438 (extended to include exponent cases), testNonBlockingParserNumericLengthLimit, testCustomMaxNumberLengthConstraint, testNonBlockingParserExponentLengthLimit |
constraints/LargeDocReadTest.java | testInputStreamExceedsLimit, testInputStreamUnderLimitSucceeds, testReaderExceedsLimit, testReaderUnderLimitSucceeds, testAsyncExceedsLimit, testAsyncUnderLimitSucceeds, testDataInputWithDocLengthLimitFails, testDataInputWithoutDocLengthLimitWorks, testDefaultFactoryNoLimit |
| File | Change Type | Lines Changed | Description |
|---|
src/main/java/.../StreamReadConstraints.java | New | +356 | Constraint config + 5 validation methods |
src/main/java/.../exc/StreamConstraintsException.java | New | +52 | Exception type for constraint violations |
src/main/java/.../base/ParserBase.java | Modified | +44 | Depth/length hooks in reset and context helpers |
src/main/java/.../json/JsonReadContext.java | Modified | +6 | throws IOException propagation |
src/main/java/.../json/ReaderBasedJsonParser.java | Modified | +30 | Use depth-checking _createChild* helpers |
src/main/java/.../json/UTF8StreamJsonParser.java | Modified | +26 | Use depth-checking _createChild* helpers |
src/main/java/.../json/UTF8DataInputJsonParser.java | Modified | +26 | Use depth-checking _createChild* helpers |
src/main/java/.../json/async/NonBlockingJsonParserBase.java | Modified | +4 | Use depth-checking _createChild* helpers |
src/main/java/.../json/async/NonBlockingJsonParser.java | Modified | +9 | NEW — validateIntegerLength / validateFPLength at 6 number-completion sites; validateDocumentLength in feedInput() |
src/main/java/.../JsonStreamContext.java | Modified | +20 | Added getNestingDepth() |
src/main/java/.../TSFBuilder.java | Modified | +15 | Added _streamReadConstraints field and streamReadConstraints() setter |
src/main/java/.../JsonFactory.java | Modified | +12 | Wire builder constraints into constructors; DataInput fail-fast for maxDocumentLength |
src/test/java/.../read/NumberOverflowTest.java | Modified | +241 | testSonatype_2022_6438 (incl. exponent cases), testNonBlockingParserNumericLengthLimit, testNonBlockingParserExponentLengthLimit, testCustomMaxNumberLengthConstraint |
src/test/java/.../read/NumberParsingTest.java | Modified | +39 | Updated 3 verifyException call sites |
src/test/java/.../read/ArrayParsingTest.java | Modified | +143 | testCVE_2025_52999, testCustomNestingDepthConstraint |
src/test/java/.../constraints/LargeDocReadTest.java | New | +200 | 9 tests for maxDocumentLength enforcement across all parser paths |
| Component | Version |
|---|
| Base tag | jackson-core-2.13.5 |
| Branch | 2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9 |
| Build tool | Maven (wrapper: ./mvnw) |
| Test framework | JUnit 3 / TestCase-style |
| Test count | 957 |