Repository navigation
Security Report: net.minidev:json-smart parser depth-counter leak (CWE-459/CWE-672) #299
Description
Date: 27 August 2026
Reporter: Sandipan Roy (saroy@redhat.com)
Summary
| Attribute | Value |
|---|---|
| Package | net.minidev:json-smart |
| Class | CWE-459 Incomplete Cleanup + CWE-672: instance depth counter leaks across parse() calls on a reused JSONParser |
| Impact | Remote DoS: attacker permanently bricks a shared parser (all later parses fail) or steals depth budget to reject valid payloads |
| Root cause | parse() resets pos but never depth (JSONParserBase.java:210); ~10 mid-parse error paths skip the non-finally clean-close decrements |
| Distinct from prior CVEs | CVE-2023-1370 / CVE-2024-57699 fixed the recursion depth; this is incomplete cleanup + designed instance reuse — separate behavior |
| Affected range | 2.5.0 -> 2.6.0-SNAPSHOT |
Vulnerable code
json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java
Entry (no depth reset):
protected <T> T parse(JsonReaderI<T> mapper) throws ParseException {
this.pos = -1; // depth NOT reset
return parseInner(mapper);
}
protected <T> T parseNext(JsonReaderI<T> mapper) throws ParseException {
this.pos = this.pos > 0 ? this.pos : -1; // depth not reset either
return parseInner(mapper);
}Increment / decrement sites (decrements NOT in finally):
case start: if (limitJsonDepth && ++this.depth > MAX_DEPTH) throw new ParseException(...JSON_DEPTH...);
...
case ']': this.depth--; ... // only reached on clean close
case '}': this.depth--; ...Any throw between an increment and its decrement leaves depth elevated.
JSONParser caches one inner parser per wrapper (getPString() reuse) and
MultipleJsonParser.parseNext() streams many docs through one instance by
design -> leaked counter persists across calls.
Proof of Concept
Poc.java + run-poc.sh (builds both modules from source, compiles, runs).
== [3/4] running PoC ==
R1 fresh+{} -> OK
R2 poison(400) -> ParseException(Unexpected End Of File position 2000: null)
R3 same,{} -> ParseException(Malicious payload, having non natural depths, parsing stoped on { at position 0.) <<< {} on SAME instance REJECTED
R4 fresh-again,{} -> OK
R5 leak397+depth5 -> ParseException(... non natural depths ... position 15.) <<< valid doc rejected (stolen budget)
VERDICT: depth-counter leak CONFIRMED on reused parser instances
EXIT 3
Controls: fresh guarded instances parse fine (R1, R4); only the reused instance
after an attacker-fed malformed doc fails (R3, R5 demonstrate the defect).
Impact
- A 400-deep malformed payload permanently poisons the guarded parser: every
subsequent parse() on the same instance (even{}) throws
Malicious payload, having non natural depths. - Partial budget theft: leak ~397 then a valid 5-deep doc is rejected.
- Reproduces identically on v2.5.2 and HEAD v2.6.0.
CVSS v3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H = 7.5 HIGH
Suggested fix
protected <T> T parse(JsonReaderI<T> mapper) throws ParseException {
this.pos = -1;
this.depth = 0; // ADD
return parseInner(mapper);
}and move the clean-close decrements into a try/finally so any error restores
depth irrespective of where it strikes.
Activity