From 1162f9874db0ca6ea43ad806ceb6d8d511df0c1e Mon Sep 17 00:00:00 2001 From: Davyd Santos Date: Wed, 27 Mar 2024 12:41:19 -0300 Subject: [PATCH 1/5] Port fix from v2.x related to Buffer overflow into v1.x --- .../minidev/json/parser/JSONParserBase.java | 15 ++++++++- .../minidev/json/parser/ParseException.java | 8 ++++- .../net/minidev/json/test/TestOverflow.java | 32 +++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 json-smart/src/test/java/net/minidev/json/test/TestOverflow.java diff --git a/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java b/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java index a7254c7..05fc80e 100644 --- a/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java +++ b/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java @@ -39,7 +39,12 @@ */ abstract class JSONParserBase { protected char c; - public final static byte EOI = 0x1A; + /** + * hard coded maximal depth for JSON parsing + */ + public final static int MAX_DEPTH = 400; + + protected int depth = 0;public final static byte EOI = 0x1A; protected static final char MAX_STOP = 126; // '}' -> 125 // @@ -247,6 +252,9 @@ protected List readArray() throws ParseException, IOException { List obj = containerFactory.createArrayContainer(); if (c != '[') throw new RuntimeException("Internal Error"); + if (++this.depth > MAX_DEPTH) { + throw new ParseException(pos, ERROR_UNEXPECTED_JSON_DEPTH, c); + } read(); boolean needData = false; handler.startArray(); @@ -261,6 +269,7 @@ protected List readArray() throws ParseException, IOException { case ']': if (needData && !acceptUselessComma) throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, (char) c); + this.depth--; read(); /* unstack */ handler.endArray(); return obj; @@ -403,6 +412,9 @@ protected Map readObject() throws ParseException, IOException { Map obj = this.containerFactory.createObjectContainer(); if (c != '{') throw new RuntimeException("Internal Error"); + if (++this.depth > MAX_DEPTH) { + throw new ParseException(pos, ERROR_UNEXPECTED_JSON_DEPTH, c); + } handler.startObject(); boolean needData = false; boolean acceptData = true; @@ -422,6 +434,7 @@ protected Map readObject() throws ParseException, IOException { case '}': if (needData && !acceptUselessComma) throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, (char) c); + this.depth--; read(); /* unstack */ handler.endObject(); return obj; diff --git a/json-smart/src/main/java/net/minidev/json/parser/ParseException.java b/json-smart/src/main/java/net/minidev/json/parser/ParseException.java index e652cf2..90da60b 100644 --- a/json-smart/src/main/java/net/minidev/json/parser/ParseException.java +++ b/json-smart/src/main/java/net/minidev/json/parser/ParseException.java @@ -30,6 +30,7 @@ public class ParseException extends Exception { public static final int ERROR_UNEXPECTED_UNICODE = 4; public static final int ERROR_UNEXPECTED_DUPLICATE_KEY = 5; public static final int ERROR_UNEXPECTED_LEADING_0 = 6; + public static final int ERROR_UNEXPECTED_JSON_DEPTH = 7; private int errorType; private Object unexpectedObject; @@ -114,7 +115,12 @@ private static String toMessage(int position, int errorType, Object unexpectedOb sb.append(" at position "); sb.append(position); sb.append("."); - } else { + } else if (errorType == ERROR_UNEXPECTED_JSON_DEPTH) { + sb.append("Malicious payload, having non natural depths, parsing stoped on "); + sb.append(unexpectedObject); + sb.append(" at position "); + sb.append(position); + sb.append("."); } else { sb.append("Unkown error at position "); sb.append(position); sb.append("."); diff --git a/json-smart/src/test/java/net/minidev/json/test/TestOverflow.java b/json-smart/src/test/java/net/minidev/json/test/TestOverflow.java new file mode 100644 index 0000000..df99d1a --- /dev/null +++ b/json-smart/src/test/java/net/minidev/json/test/TestOverflow.java @@ -0,0 +1,32 @@ +package net.minidev.json.test; + +import net.minidev.json.JSONValue; +import net.minidev.json.parser.ParseException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class TestOverflow { + @Test + public void stressTest() throws Exception { + int size = 10000; + StringBuilder sb = new StringBuilder(10 + size*4); + for (int i=0; i < size; i++) { + sb.append("{a:"); + } + sb.append("true"); + for (int i=0; i < size; i++) { + sb.append("}"); + } + String s = sb.toString(); + try { + JSONValue.parseWithException(s); + } catch (ParseException e) { + assertEquals(e.getErrorType(), ParseException.ERROR_UNEXPECTED_JSON_DEPTH); + return; + } + assertTrue(false); + } +} From 6f8d264dd06f9bc217ccc0c7d33086fd07eeaafb Mon Sep 17 00:00:00 2001 From: Davyd Santos Date: Wed, 27 Mar 2024 12:56:48 -0300 Subject: [PATCH 2/5] Adding incremental fix for unstacking objects in an array --- .../minidev/json/parser/JSONParserBase.java | 1 + .../net/minidev/json/test/TestOverflow.java | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java b/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java index 05fc80e..f88813c 100644 --- a/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java +++ b/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java @@ -480,6 +480,7 @@ protected Map readObject() throws ParseException, IOException { skipSpace(); if (c == '}') { read(); /* unstack */ + this.depth--; handler.endObject(); return obj; } diff --git a/json-smart/src/test/java/net/minidev/json/test/TestOverflow.java b/json-smart/src/test/java/net/minidev/json/test/TestOverflow.java index df99d1a..07912f2 100644 --- a/json-smart/src/test/java/net/minidev/json/test/TestOverflow.java +++ b/json-smart/src/test/java/net/minidev/json/test/TestOverflow.java @@ -1,5 +1,6 @@ package net.minidev.json.test; +import net.minidev.json.JSONArray; import net.minidev.json.JSONValue; import net.minidev.json.parser.ParseException; @@ -29,4 +30,21 @@ public void stressTest() throws Exception { } assertTrue(false); } + + @Test + public void shouldNotFailParsingArraysWith400Elements() throws Exception { + int size = 400; + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i=0; i < size; i++) { + sb.append("{a:true}"); + if(i+1 < size) { + sb.append(","); + } + } + sb.append("]"); + String s = sb.toString(); + JSONArray array = (JSONArray) JSONValue.parseWithException(s); + assertEquals(array.size(), size); + } } From 6b05676a8626765693ad985ecb4587900ab57e05 Mon Sep 17 00:00:00 2001 From: Davyd Santos Date: Tue, 23 Apr 2024 15:10:16 -0300 Subject: [PATCH 3/5] Adding \n --- .../src/main/java/net/minidev/json/parser/JSONParserBase.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java b/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java index f88813c..d389c17 100644 --- a/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java +++ b/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java @@ -44,7 +44,8 @@ abstract class JSONParserBase { */ public final static int MAX_DEPTH = 400; - protected int depth = 0;public final static byte EOI = 0x1A; + protected int depth = 0; + public final static byte EOI = 0x1A; protected static final char MAX_STOP = 126; // '}' -> 125 // From 2bbe303d3dbd54001aacd505ea23ccd29c33d6e0 Mon Sep 17 00:00:00 2001 From: Davyd Santos Date: Tue, 23 Apr 2024 15:11:33 -0300 Subject: [PATCH 4/5] Update ParseException.java Add \n --- .../src/main/java/net/minidev/json/parser/ParseException.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/json-smart/src/main/java/net/minidev/json/parser/ParseException.java b/json-smart/src/main/java/net/minidev/json/parser/ParseException.java index 90da60b..8ae0944 100644 --- a/json-smart/src/main/java/net/minidev/json/parser/ParseException.java +++ b/json-smart/src/main/java/net/minidev/json/parser/ParseException.java @@ -120,7 +120,8 @@ private static String toMessage(int position, int errorType, Object unexpectedOb sb.append(unexpectedObject); sb.append(" at position "); sb.append(position); - sb.append("."); } else { + sb.append("."); + } else { sb.append("Unkown error at position "); sb.append(position); sb.append("."); From 33bbe189296139ac0ab68eb9dc63715f1ce5c3ab Mon Sep 17 00:00:00 2001 From: Davyd Santos Date: Tue, 4 Jun 2024 12:16:58 -0300 Subject: [PATCH 5/5] Allowing changes on MAX size --- .../src/main/java/net/minidev/json/parser/JSONParserBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java b/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java index d389c17..6ad1e30 100644 --- a/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java +++ b/json-smart/src/main/java/net/minidev/json/parser/JSONParserBase.java @@ -42,7 +42,7 @@ abstract class JSONParserBase { /** * hard coded maximal depth for JSON parsing */ - public final static int MAX_DEPTH = 400; + public static int MAX_DEPTH = 400; protected int depth = 0; public final static byte EOI = 0x1A;