This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/main by this push:
     new 50a92ec4496 Add a depth limit for JSON parsing mirroring what Jettison 
does (#3149)
50a92ec4496 is described below

commit 50a92ec4496ef25bbd76a25ddc5f3e4a2e26fc28
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Wed May 27 15:13:15 2026 +0100

    Add a depth limit for JSON parsing mirroring what Jettison does (#3149)
---
 .../json/basic/JsonMapObjectReaderWriter.java      | 30 +++++++++++++++--
 .../json/basic/JsonMapObjectReaderWriterTest.java  | 38 ++++++++++++++++++++++
 2 files changed, 65 insertions(+), 3 deletions(-)

diff --git 
a/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java
 
b/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java
index 3522a6a496c..7ea3ca1d926 100644
--- 
a/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java
+++ 
b/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java
@@ -30,6 +30,13 @@ import org.apache.cxf.helpers.IOUtils;
 
 
 public class JsonMapObjectReaderWriter {
+    /**
+     * Maximum JSON nesting depth accepted by the parser, matching Jettison's 
default
+     * {@code RECURSION_DEPTH_LIMIT} of 500.  Payloads nested more deeply than 
this
+     * throw an {@link java.io.UncheckedIOException} rather than exhausting 
the JVM
+     * thread stack with unbounded recursion.
+     */
+    static final int MAX_RECURSION_DEPTH = 500;
     private static final Set<Character> ESCAPED_CHARS;
     private static final char DQUOTE = '"';
     private static final char COMMA = ',';
@@ -185,6 +192,14 @@ public class JsonMapObjectReaderWriter {
         return internalFromJsonAsList(name, theJson.substring(1, 
theJson.length() - 1));
     }
     protected void readJsonObjectAsSettable(Settable values, String json) {
+        readJsonObjectAsSettable(values, json, 0);
+    }
+
+    private void readJsonObjectAsSettable(Settable values, String json, int 
depth) {
+        if (depth > MAX_RECURSION_DEPTH) {
+            throw new UncheckedIOException(new IOException(
+                    "JSON nesting depth exceeds maximum of " + 
MAX_RECURSION_DEPTH));
+        }
         for (int i = 0; i < json.length(); i++) {
             if (Character.isWhitespace(json.charAt(i))) {
                 continue;
@@ -207,13 +222,13 @@ public class JsonMapObjectReaderWriter {
                 int closingIndex = getClosingIndex(json, OBJECT_START, 
OBJECT_END, sepIndex + j);
                 String newJson = json.substring(sepIndex + j + 1, 
closingIndex);
                 MapSettable nextMap = new MapSettable();
-                readJsonObjectAsSettable(nextMap, newJson);
+                readJsonObjectAsSettable(nextMap, newJson, depth + 1);
                 values.put(name, nextMap.map);
                 i = closingIndex + 1;
             } else if (json.charAt(sepIndex + j) == ARRAY_START) {
                 int closingIndex = getClosingIndex(json, ARRAY_START, 
ARRAY_END, sepIndex + j);
                 String newJson = json.substring(sepIndex + j + 1, 
closingIndex);
-                values.put(name, internalFromJsonAsList(name, newJson));
+                values.put(name, internalFromJsonAsList(name, newJson, depth + 
1));
                 i = closingIndex + 1;
             } else {
                 int commaIndex = getCommaIndex(json, sepIndex + j);
@@ -224,7 +239,16 @@ public class JsonMapObjectReaderWriter {
 
         }
     }
+
     protected List<Object> internalFromJsonAsList(String name, String json) {
+        return internalFromJsonAsList(name, json, 0);
+    }
+
+    private List<Object> internalFromJsonAsList(String name, String json, int 
depth) {
+        if (depth > MAX_RECURSION_DEPTH) {
+            throw new UncheckedIOException(new IOException(
+                    "JSON nesting depth exceeds maximum of " + 
MAX_RECURSION_DEPTH));
+        }
         List<Object> values = new LinkedList<>();
         for (int i = 0; i < json.length(); i++) {
             if (Character.isWhitespace(json.charAt(i))) {
@@ -233,7 +257,7 @@ public class JsonMapObjectReaderWriter {
             if (json.charAt(i) == OBJECT_START) {
                 int closingIndex = getClosingIndex(json, OBJECT_START, 
OBJECT_END, i);
                 MapSettable nextMap = new MapSettable();
-                readJsonObjectAsSettable(nextMap, json.substring(i + 1, 
closingIndex));
+                readJsonObjectAsSettable(nextMap, json.substring(i + 1, 
closingIndex), depth + 1);
                 values.add(nextMap.map);
                 i = closingIndex + 1;
             } else {
diff --git 
a/rt/rs/extensions/json-basic/src/test/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriterTest.java
 
b/rt/rs/extensions/json-basic/src/test/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriterTest.java
index 77b20e410a0..a8583cce40e 100644
--- 
a/rt/rs/extensions/json-basic/src/test/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriterTest.java
+++ 
b/rt/rs/extensions/json-basic/src/test/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriterTest.java
@@ -392,4 +392,42 @@ public class JsonMapObjectReaderWriterTest {
         }
     }
 
+    /**
+     * Add a test to check an exception is thrown on parsing deeply nested 
JSON structures that exceed the 
+     * recursion depth limit.
+     */
+    @Test(expected = UncheckedIOException.class)
+    public void testDepthLimitExceededThrowsUncheckedIOException() {
+        int levels = JsonMapObjectReaderWriter.MAX_RECURSION_DEPTH + 2;
+        StringBuilder sb = new StringBuilder(levels * 8);
+        for (int i = 0; i < levels; i++) {
+            sb.append("{\"a\":");
+        }
+        sb.append("\"v\"");
+        for (int i = 0; i < levels; i++) {
+            sb.append('}');
+        }
+        new JsonMapObjectReaderWriter().fromJson(sb.toString());
+    }
+
+    /**
+     * A payload with exactly {@code MAX_RECURSION_DEPTH + 1} brace levels 
reaches a
+     * maximum internal depth of {@code MAX_RECURSION_DEPTH} — right at the 
boundary —
+     * and must parse successfully.
+     */
+    @Test
+    public void testDepthLimitNotExceededParsesSuccessfully() {
+        int levels = JsonMapObjectReaderWriter.MAX_RECURSION_DEPTH + 1;
+        StringBuilder sb = new StringBuilder(levels * 8);
+        for (int i = 0; i < levels; i++) {
+            sb.append("{\"a\":");
+        }
+        sb.append("\"v\"");
+        for (int i = 0; i < levels; i++) {
+            sb.append('}');
+        }
+        // Should not throw
+        new JsonMapObjectReaderWriter().fromJson(sb.toString());
+    }
+
 }

Reply via email to