mirror of
https://github.com/apache/struts.git
synced 2026-08-06 15:17:00 +00:00
WW-5618 feat(json): add configurable limits to JSON plugin for DoS prevention (#1626)
Add configurable limits to the JSON plugin to prevent denial-of-service attacks via malicious JSON payloads. Limits are enforced directly in the existing JSONReader class without breaking backward compatibility (no interface extraction or class renames). New configurable constants (struts-plugin.xml defaults): - struts.json.maxElements (10000) - per-container element count - struts.json.maxDepth (64) - maximum nesting depth - struts.json.maxLength (2097152) - maximum input length in chars - struts.json.maxStringLength (262144) - maximum string value length - struts.json.maxKeyLength (512) - maximum object key length Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -31,4 +31,10 @@ public class JSONConstants {
|
||||
public static final String JSON_WRITER = "struts.json.writer";
|
||||
public static final String RESULT_EXCLUDE_PROXY_PROPERTIES = "struts.json.result.excludeProxyProperties";
|
||||
public static final String DATE_FORMAT = "struts.json.dateformat";
|
||||
|
||||
public static final String JSON_MAX_ELEMENTS = "struts.json.maxElements";
|
||||
public static final String JSON_MAX_DEPTH = "struts.json.maxDepth";
|
||||
public static final String JSON_MAX_LENGTH = "struts.json.maxLength";
|
||||
public static final String JSON_MAX_STRING_LENGTH = "struts.json.maxStringLength";
|
||||
public static final String JSON_MAX_KEY_LENGTH = "struts.json.maxKeyLength";
|
||||
}
|
||||
|
||||
@@ -71,6 +71,13 @@ public class JSONInterceptor extends AbstractInterceptor {
|
||||
private String jsonContentType = "application/json";
|
||||
private String jsonRpcContentType = "application/json-rpc";
|
||||
|
||||
private JSONUtil jsonUtil = new JSONUtil();
|
||||
private int maxElements = 10000;
|
||||
private int maxDepth = 64;
|
||||
private int maxLength = 2097152;
|
||||
private int maxStringLength = 262144;
|
||||
private int maxKeyLength = 512;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public String intercept(ActionInvocation invocation) throws Exception {
|
||||
HttpServletRequest request = ServletActionContext.getRequest();
|
||||
@@ -91,7 +98,8 @@ public class JSONInterceptor extends AbstractInterceptor {
|
||||
|
||||
if (jsonContentType.equalsIgnoreCase(requestContentType)) {
|
||||
// load JSON object
|
||||
Object obj = JSONUtil.deserialize(request.getReader());
|
||||
Object obj = jsonUtil.deserializeInput(request.getReader(), maxLength, maxElements, maxDepth,
|
||||
maxStringLength, maxKeyLength);
|
||||
|
||||
// JSON array (this.root cannot be null in this case)
|
||||
if(obj instanceof List && this.root != null) {
|
||||
@@ -133,7 +141,8 @@ public class JSONInterceptor extends AbstractInterceptor {
|
||||
Object result;
|
||||
if (this.enableSMD) {
|
||||
// load JSON object
|
||||
Object obj = JSONUtil.deserialize(request.getReader());
|
||||
Object obj = jsonUtil.deserializeInput(request.getReader(), maxLength, maxElements, maxDepth,
|
||||
maxStringLength, maxKeyLength);
|
||||
|
||||
if (obj instanceof Map) {
|
||||
Map smd = (Map) obj;
|
||||
@@ -168,9 +177,8 @@ public class JSONInterceptor extends AbstractInterceptor {
|
||||
result = rpcResponse;
|
||||
}
|
||||
|
||||
JSONUtil jsonUtil = invocation.getInvocationContext().getContainer().getInstance(JSONUtil.class);
|
||||
|
||||
String json = jsonUtil.serialize(result, excludeProperties, getIncludeProperties(),
|
||||
JSONUtil smdJsonUtil = invocation.getInvocationContext().getContainer().getInstance(JSONUtil.class);
|
||||
String json = smdJsonUtil.serialize(result, excludeProperties, getIncludeProperties(),
|
||||
ignoreHierarchy, excludeNullProperties);
|
||||
json = addCallbackIfApplicable(request, json);
|
||||
boolean writeGzip = enableGZIP && JSONUtil.isGzipInRequest(request);
|
||||
@@ -423,6 +431,36 @@ public class JSONInterceptor extends AbstractInterceptor {
|
||||
setDebug(BooleanUtils.toBoolean(mode));
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setJsonUtil(JSONUtil jsonUtil) {
|
||||
this.jsonUtil = jsonUtil;
|
||||
}
|
||||
|
||||
@Inject(value = JSONConstants.JSON_MAX_ELEMENTS, required = false)
|
||||
public void setMaxElements(String maxElements) {
|
||||
this.maxElements = Integer.parseInt(maxElements);
|
||||
}
|
||||
|
||||
@Inject(value = JSONConstants.JSON_MAX_DEPTH, required = false)
|
||||
public void setMaxDepth(String maxDepth) {
|
||||
this.maxDepth = Integer.parseInt(maxDepth);
|
||||
}
|
||||
|
||||
@Inject(value = JSONConstants.JSON_MAX_LENGTH, required = false)
|
||||
public void setMaxLength(String maxLength) {
|
||||
this.maxLength = Integer.parseInt(maxLength);
|
||||
}
|
||||
|
||||
@Inject(value = JSONConstants.JSON_MAX_STRING_LENGTH, required = false)
|
||||
public void setMaxStringLength(String maxStringLength) {
|
||||
this.maxStringLength = Integer.parseInt(maxStringLength);
|
||||
}
|
||||
|
||||
@Inject(value = JSONConstants.JSON_MAX_KEY_LENGTH, required = false)
|
||||
public void setMaxKeyLength(String maxKeyLength) {
|
||||
this.maxKeyLength = Integer.parseInt(maxKeyLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match properties
|
||||
* that should be excluded from the JSON output.
|
||||
|
||||
@@ -48,11 +48,38 @@ public class JSONReader {
|
||||
escapes.put('t', '\t');
|
||||
}
|
||||
|
||||
private static final int DEFAULT_MAX_ELEMENTS = 10000;
|
||||
private static final int DEFAULT_MAX_DEPTH = 64;
|
||||
private static final int DEFAULT_MAX_STRING_LENGTH = 262144;
|
||||
private static final int DEFAULT_MAX_KEY_LENGTH = 512;
|
||||
|
||||
private int maxElements = DEFAULT_MAX_ELEMENTS;
|
||||
private int maxDepth = DEFAULT_MAX_DEPTH;
|
||||
private int maxStringLength = DEFAULT_MAX_STRING_LENGTH;
|
||||
private int maxKeyLength = DEFAULT_MAX_KEY_LENGTH;
|
||||
private int depth;
|
||||
|
||||
private CharacterIterator it;
|
||||
private char c;
|
||||
private Object token;
|
||||
private StringBuilder buf = new StringBuilder();
|
||||
|
||||
public void setMaxElements(int maxElements) {
|
||||
this.maxElements = maxElements;
|
||||
}
|
||||
|
||||
public void setMaxDepth(int maxDepth) {
|
||||
this.maxDepth = maxDepth;
|
||||
}
|
||||
|
||||
public void setMaxStringLength(int maxStringLength) {
|
||||
this.maxStringLength = maxStringLength;
|
||||
}
|
||||
|
||||
public void setMaxKeyLength(int maxKeyLength) {
|
||||
this.maxKeyLength = maxKeyLength;
|
||||
}
|
||||
|
||||
protected char next() {
|
||||
this.c = this.it.next();
|
||||
|
||||
@@ -124,29 +151,51 @@ public class JSONReader {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Map object() throws JSONException {
|
||||
Map ret = new HashMap();
|
||||
Object next = this.read();
|
||||
if (next != OBJECT_END) {
|
||||
String key = (String) next;
|
||||
while (this.token != OBJECT_END) {
|
||||
this.read(); // should be a colon
|
||||
this.depth++;
|
||||
if (this.depth > this.maxDepth) {
|
||||
throw new JSONException("JSON object depth exceeds maximum allowed depth of " + this.maxDepth);
|
||||
}
|
||||
try {
|
||||
Map ret = new HashMap();
|
||||
Object next = this.read();
|
||||
if (next != OBJECT_END) {
|
||||
String key = (String) next;
|
||||
while (this.token != OBJECT_END) {
|
||||
validateKeyLength(key);
|
||||
this.read(); // should be a colon
|
||||
|
||||
if (this.token != OBJECT_END) {
|
||||
ret.put(key, this.read());
|
||||
if (this.token != OBJECT_END) {
|
||||
ret.put(key, this.read());
|
||||
validateElementCount(ret.size());
|
||||
|
||||
if (this.read() == COMMA) {
|
||||
Object name = this.read();
|
||||
if (this.read() == COMMA) {
|
||||
Object name = this.read();
|
||||
|
||||
if (name instanceof String) {
|
||||
key = (String) name;
|
||||
} else
|
||||
throw buildInvalidInputException();
|
||||
if (name instanceof String) {
|
||||
key = (String) name;
|
||||
} else
|
||||
throw buildInvalidInputException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
} finally {
|
||||
this.depth--;
|
||||
}
|
||||
}
|
||||
|
||||
private void validateKeyLength(String key) throws JSONException {
|
||||
if (key != null && key.length() > this.maxKeyLength) {
|
||||
throw new JSONException("JSON key length " + key.length() + " exceeds maximum allowed length of " + this.maxKeyLength);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateElementCount(int count) throws JSONException {
|
||||
if (count > this.maxElements) {
|
||||
throw new JSONException("JSON element count exceeds maximum allowed count of " + this.maxElements);
|
||||
}
|
||||
}
|
||||
|
||||
protected JSONException buildInvalidInputException() {
|
||||
@@ -156,21 +205,30 @@ public class JSONReader {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected List array() throws JSONException {
|
||||
List ret = new ArrayList();
|
||||
Object value = this.read();
|
||||
|
||||
while (this.token != ARRAY_END) {
|
||||
ret.add(value);
|
||||
|
||||
Object read = this.read();
|
||||
if (read == COMMA) {
|
||||
value = this.read();
|
||||
} else if (read != ARRAY_END) {
|
||||
throw buildInvalidInputException();
|
||||
}
|
||||
this.depth++;
|
||||
if (this.depth > this.maxDepth) {
|
||||
throw new JSONException("JSON array depth exceeds maximum allowed depth of " + this.maxDepth);
|
||||
}
|
||||
try {
|
||||
List ret = new ArrayList();
|
||||
Object value = this.read();
|
||||
|
||||
return ret;
|
||||
while (this.token != ARRAY_END) {
|
||||
ret.add(value);
|
||||
validateElementCount(ret.size());
|
||||
|
||||
Object read = this.read();
|
||||
if (read == COMMA) {
|
||||
value = this.read();
|
||||
} else if (read != ARRAY_END) {
|
||||
throw buildInvalidInputException();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
} finally {
|
||||
this.depth--;
|
||||
}
|
||||
}
|
||||
|
||||
protected Object number() throws JSONException {
|
||||
@@ -215,7 +273,7 @@ public class JSONReader {
|
||||
}
|
||||
}
|
||||
|
||||
protected Object string(char quote) {
|
||||
protected Object string(char quote) throws JSONException {
|
||||
this.buf.setLength(0);
|
||||
|
||||
while ((this.c != quote) && (this.c != CharacterIterator.DONE)) {
|
||||
@@ -234,6 +292,9 @@ public class JSONReader {
|
||||
} else {
|
||||
this.add();
|
||||
}
|
||||
if (this.buf.length() > this.maxStringLength) {
|
||||
throw new JSONException("JSON string length exceeds maximum allowed length of " + this.maxStringLength);
|
||||
}
|
||||
}
|
||||
|
||||
this.next();
|
||||
|
||||
@@ -323,6 +323,44 @@ public class JSONUtil {
|
||||
return deserialize(buffer.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a JSON object from a Reader with configurable limits to prevent DoS attacks.
|
||||
*
|
||||
* @param reader Reader to read JSON string from
|
||||
* @param maxLength maximum allowed input length in characters
|
||||
* @param maxElements maximum allowed number of elements in a single object/array
|
||||
* @param maxDepth maximum allowed nesting depth
|
||||
* @param maxStringLength maximum allowed string value length
|
||||
* @param maxKeyLength maximum allowed key length
|
||||
* @return deserialized object
|
||||
* @throws JSONException when limits are exceeded or input is malformed
|
||||
*/
|
||||
public Object deserializeInput(Reader reader, int maxLength, int maxElements, int maxDepth,
|
||||
int maxStringLength, int maxKeyLength) throws JSONException {
|
||||
BufferedReader bufferReader = new BufferedReader(reader);
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
String line;
|
||||
|
||||
try {
|
||||
while ((line = bufferReader.readLine()) != null) {
|
||||
buffer.append(line);
|
||||
if (buffer.length() > maxLength) {
|
||||
throw new JSONException("JSON input length exceeds maximum allowed length of " + maxLength);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
|
||||
JSONReader jsonReader = new JSONReader();
|
||||
jsonReader.setMaxElements(maxElements);
|
||||
jsonReader.setMaxDepth(maxDepth);
|
||||
jsonReader.setMaxStringLength(maxStringLength);
|
||||
jsonReader.setMaxKeyLength(maxKeyLength);
|
||||
|
||||
return jsonReader.read(buffer.toString());
|
||||
}
|
||||
|
||||
public static void writeJSONToResponse(SerializationParams serializationParams) throws IOException {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (StringUtils.isNotBlank(serializationParams.getSerializedJSON()))
|
||||
|
||||
@@ -27,6 +27,11 @@
|
||||
<bean type="org.apache.struts2.json.JSONWriter" name="struts" class="org.apache.struts2.json.DefaultJSONWriter"
|
||||
scope="prototype"/>
|
||||
<constant name="struts.json.writer" value="struts"/>
|
||||
<constant name="struts.json.maxElements" value="10000"/>
|
||||
<constant name="struts.json.maxDepth" value="64"/>
|
||||
<constant name="struts.json.maxLength" value="2097152"/>
|
||||
<constant name="struts.json.maxStringLength" value="262144"/>
|
||||
<constant name="struts.json.maxKeyLength" value="512"/>
|
||||
<!-- TODO: Make DefaultJSONWriter thread-safe to remove "prototype"s -->
|
||||
<bean class="org.apache.struts2.json.JSONUtil" scope="prototype"/>
|
||||
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Tests for JSON reader configurable limits (WW-5618).
|
||||
*/
|
||||
public class JSONReaderLimitsTest {
|
||||
|
||||
// --- maxDepth ---
|
||||
|
||||
@Test
|
||||
public void testMaxDepthExceededByNestedObjects() {
|
||||
JSONReader reader = new JSONReader();
|
||||
reader.setMaxDepth(2);
|
||||
// 3 levels of nesting exceeds limit of 2
|
||||
try {
|
||||
reader.read("{\"a\":{\"b\":{\"c\":1}}}");
|
||||
fail("Expected JSONException for exceeding max depth");
|
||||
} catch (JSONException e) {
|
||||
assertTrue(e.getMessage().contains("2"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxDepthExceededByNestedArrays() {
|
||||
JSONReader reader = new JSONReader();
|
||||
reader.setMaxDepth(2);
|
||||
try {
|
||||
reader.read("[[[1]]]");
|
||||
fail("Expected JSONException for exceeding max depth");
|
||||
} catch (JSONException e) {
|
||||
assertTrue(e.getMessage().contains("2"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxDepthAtBoundaryIsAllowed() throws JSONException {
|
||||
JSONReader reader = new JSONReader();
|
||||
// 3 levels of nesting, limit is exactly 3 — should pass
|
||||
reader.setMaxDepth(3);
|
||||
Object result = reader.read("{\"a\":{\"b\":{\"c\":1}}}");
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxDepthOneBelowBoundaryIsRejected() {
|
||||
JSONReader reader = new JSONReader();
|
||||
// 3 levels of nesting, limit is 2 — should fail
|
||||
reader.setMaxDepth(2);
|
||||
try {
|
||||
reader.read("{\"a\":{\"b\":{\"c\":1}}}");
|
||||
fail("Expected JSONException");
|
||||
} catch (JSONException e) {
|
||||
assertTrue(e.getMessage().contains("depth"));
|
||||
}
|
||||
}
|
||||
|
||||
// --- maxElements (per-container) ---
|
||||
|
||||
@Test
|
||||
public void testMaxElementsExceededInObject() {
|
||||
JSONReader reader = new JSONReader();
|
||||
reader.setMaxElements(2);
|
||||
// 3 keys in one object exceeds per-container limit of 2
|
||||
try {
|
||||
reader.read("{\"a\":1,\"b\":2,\"c\":3}");
|
||||
fail("Expected JSONException for exceeding max elements");
|
||||
} catch (JSONException e) {
|
||||
assertTrue(e.getMessage().contains("2"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxElementsExceededInArray() {
|
||||
JSONReader reader = new JSONReader();
|
||||
reader.setMaxElements(2);
|
||||
try {
|
||||
reader.read("[1,2,3]");
|
||||
fail("Expected JSONException for exceeding max elements");
|
||||
} catch (JSONException e) {
|
||||
assertTrue(e.getMessage().contains("2"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxElementsAtBoundaryIsAllowed() throws JSONException {
|
||||
JSONReader reader = new JSONReader();
|
||||
// exactly 3 elements, limit is 3 — should pass
|
||||
reader.setMaxElements(3);
|
||||
Object result = reader.read("{\"a\":1,\"b\":2,\"c\":3}");
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxElementsIsPerContainerNotGlobal() throws JSONException {
|
||||
JSONReader reader = new JSONReader();
|
||||
// limit is 2; two containers each with 2 elements — should pass
|
||||
// because the limit applies per-container, not globally
|
||||
reader.setMaxElements(2);
|
||||
Object result = reader.read("{\"a\":[1,2],\"b\":[3,4]}");
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
// --- maxStringLength (applies to both values and keys) ---
|
||||
|
||||
@Test
|
||||
public void testMaxStringLengthExceededByValue() {
|
||||
JSONReader reader = new JSONReader();
|
||||
reader.setMaxStringLength(5);
|
||||
// "abcdef" is 6 chars, exceeds limit of 5
|
||||
try {
|
||||
reader.read("{\"k\":\"abcdef\"}");
|
||||
fail("Expected JSONException for exceeding max string length");
|
||||
} catch (JSONException e) {
|
||||
assertTrue(e.getMessage().contains("5"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxStringLengthExceededByKey() {
|
||||
JSONReader reader = new JSONReader();
|
||||
reader.setMaxStringLength(3);
|
||||
// key "longkey" is 7 chars, parsed through string() before validateKeyLength
|
||||
try {
|
||||
reader.read("{\"longkey\":1}");
|
||||
fail("Expected JSONException for exceeding max string length on key");
|
||||
} catch (JSONException e) {
|
||||
assertTrue(e.getMessage().contains("string length") || e.getMessage().contains("key length"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxStringLengthAtBoundaryIsAllowed() throws JSONException {
|
||||
JSONReader reader = new JSONReader();
|
||||
// "abcdef" is 6 chars, limit is 6 — should pass
|
||||
reader.setMaxStringLength(6);
|
||||
Object result = reader.read("{\"k\":\"abcdef\"}");
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
// --- maxKeyLength ---
|
||||
|
||||
@Test
|
||||
public void testMaxKeyLengthExceeded() {
|
||||
JSONReader reader = new JSONReader();
|
||||
reader.setMaxKeyLength(3);
|
||||
// "longkey" is 7 chars, exceeds limit of 3
|
||||
try {
|
||||
reader.read("{\"longkey\":1}");
|
||||
fail("Expected JSONException for exceeding max key length");
|
||||
} catch (JSONException e) {
|
||||
assertTrue(e.getMessage().contains("key length") || e.getMessage().contains("string length"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxKeyLengthAtBoundaryIsAllowed() throws JSONException {
|
||||
JSONReader reader = new JSONReader();
|
||||
// "key" is 3 chars, limit is 3 — should pass
|
||||
reader.setMaxKeyLength(3);
|
||||
Object result = reader.read("{\"key\":1}");
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
// --- smoke test ---
|
||||
|
||||
@Test
|
||||
public void testDefaultLimitsAcceptTypicalPayload() throws JSONException {
|
||||
JSONReader reader = new JSONReader();
|
||||
Object result = reader.read("{\"name\":\"test\",\"values\":[1,2,3],\"nested\":{\"a\":true}}");
|
||||
assertNotNull(result);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -79,4 +80,44 @@ public class JSONUtilTest extends TestCase {
|
||||
assertEquals("{\"listOfLists\":[[\"1\",\"2\"],[\"3\",\"4\"],[\"5\",\"6\"],[\"7\",\"8\"],[\"9\",\"0\"]]}", actual);
|
||||
}
|
||||
|
||||
public void testDeserializeInputWithinLimits() throws Exception {
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
String json = "{\"name\":\"test\",\"value\":123}";
|
||||
Object result = jsonUtil.deserializeInput(new StringReader(json), 1000, 100, 10, 1000, 100);
|
||||
assertNotNull(result);
|
||||
assertTrue(result instanceof Map);
|
||||
assertEquals("test", ((Map) result).get("name"));
|
||||
}
|
||||
|
||||
public void testDeserializeInputMaxLengthExceeded() throws Exception {
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
String json = "{\"name\":\"test\",\"value\":123}";
|
||||
try {
|
||||
jsonUtil.deserializeInput(new StringReader(json), 10, 100, 10, 1000, 100);
|
||||
fail("Expected JSONException for exceeding max length");
|
||||
} catch (JSONException e) {
|
||||
assertTrue(e.getMessage().contains("10"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testDeserializeInputMaxLengthAtBoundaryIsAllowed() throws Exception {
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
String json = "{\"a\":1}";
|
||||
// length is 7, limit is 7 — should pass
|
||||
Object result = jsonUtil.deserializeInput(new StringReader(json), 7, 100, 10, 1000, 100);
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
public void testDeserializeInputPropagatesReaderLimits() throws Exception {
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
// depth 3, but maxDepth is 2
|
||||
String json = "{\"a\":{\"b\":{\"c\":1}}}";
|
||||
try {
|
||||
jsonUtil.deserializeInput(new StringReader(json), 10000, 100, 2, 1000, 100);
|
||||
fail("Expected JSONException for exceeding max depth");
|
||||
} catch (JSONException e) {
|
||||
assertTrue(e.getMessage().contains("depth"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user