From 68d28c4fd4ef5946b44029ef91a4596df5a013d0 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 29 Nov 2022 21:50:51 +0800 Subject: [PATCH] add exception for json --- hutool-crypto/pom.xml | 2 +- hutool-db/pom.xml | 12 +++--- hutool-extra/pom.xml | 12 +++--- hutool-json/pom.xml | 2 +- .../main/java/cn/hutool/json/JSONParser.java | 13 +++++-- .../main/java/cn/hutool/json/JSONTokener.java | 21 ++++++++++- .../java/cn/hutool/json/xml/XMLTokener.java | 35 ++++++++++++++++-- .../java/cn/hutool/json/Issue2746Test.java | 24 ++++++++++++ .../java/cn/hutool/json/Issue2749Test.java | 37 +++++++++++++++++++ 9 files changed, 137 insertions(+), 21 deletions(-) create mode 100755 hutool-json/src/test/java/cn/hutool/json/Issue2746Test.java create mode 100755 hutool-json/src/test/java/cn/hutool/json/Issue2749Test.java diff --git a/hutool-crypto/pom.xml b/hutool-crypto/pom.xml index 000d02103..92f9d7344 100755 --- a/hutool-crypto/pom.xml +++ b/hutool-crypto/pom.xml @@ -18,7 +18,7 @@ - 1.71 + 1.72 diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml index b9294e920..23fb8f403 100755 --- a/hutool-db/pom.xml +++ b/hutool-db/pom.xml @@ -20,10 +20,12 @@ 0.9.5.5 2.9.0 - 10.0.20 - 1.2.12 + + 10.0.27 + 1.2.15 + 4.0.3 - 3.39.3.0 + 3.39.4.1 2.5.2 @@ -79,7 +81,7 @@ com.github.chris2018998 beecp - 3.3.8 + 3.3.9 slf4j-api @@ -135,7 +137,7 @@ org.slf4j slf4j-simple - 2.0.0 + 2.0.4 test diff --git a/hutool-extra/pom.xml b/hutool-extra/pom.xml index a7c4357e6..867765389 100755 --- a/hutool-extra/pom.xml +++ b/hutool-extra/pom.xml @@ -19,7 +19,7 @@ 2.3 - 3.10.0.RELEASE + 3.13.0.RELEASE 1.4.1 2.3.31 4.9.21 @@ -31,7 +31,7 @@ 3.8.0 5.1.1 4.0.1 - 2.7.4 + 2.7.5 3.3.0 @@ -430,7 +430,7 @@ com.googlecode.aviator aviator - 5.3.2 + 5.3.3 compile true @@ -465,7 +465,7 @@ org.springframework spring-expression - 5.3.23 + 5.3.24 compile true @@ -495,13 +495,13 @@ com.github.oshi oshi-core - 6.2.2 + 6.3.2 provided com.sun.xml.bind jaxb-impl - 2.3.6 + 2.3.7 provided diff --git a/hutool-json/pom.xml b/hutool-json/pom.xml index 13613ab9a..46454a77c 100755 --- a/hutool-json/pom.xml +++ b/hutool-json/pom.xml @@ -18,7 +18,7 @@ - 1.70 + 1.72 diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONParser.java b/hutool-json/src/main/java/cn/hutool/json/JSONParser.java index ab5423817..5b75ab9d6 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONParser.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONParser.java @@ -50,19 +50,26 @@ public class JSONParser { public void parseTo(final JSONObject jsonObject, final Predicate> predicate) { final JSONTokener tokener = this.tokener; - char c; - String key; - if (tokener.nextClean() != '{') { throw tokener.syntaxError("A JSONObject text must begin with '{'"); } + + char prev; + char c; + String key; while (true) { + prev = tokener.getPrevious(); c = tokener.nextClean(); switch (c) { case 0: throw tokener.syntaxError("A JSONObject text must end with '}'"); case '}': return; + case '{': + case '[': + if(prev=='{') { + throw tokener.syntaxError("A JSONObject can not directly nest another JSONObject or JSONArray."); + } default: tokener.back(); key = tokener.nextValue().toString(); diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONTokener.java b/hutool-json/src/main/java/cn/hutool/json/JSONTokener.java index a3ecf3c9f..5955f59ae 100755 --- a/hutool-json/src/main/java/cn/hutool/json/JSONTokener.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONTokener.java @@ -162,6 +162,15 @@ public class JSONTokener { return this.previous; } + /** + * Get the last character read from the input or '\0' if nothing has been read yet. + * + * @return the last character read from the input. + */ + protected char getPrevious() { + return this.previous; + } + /** * 读取下一个字符,并比对是否和指定字符匹配 * @@ -334,10 +343,18 @@ public class JSONTokener { return this.nextString(c); case '{': this.back(); - return new JSONObject(this, this.config); + try { + return new JSONObject(this, this.config); + } catch (final StackOverflowError e) { + throw new JSONException("JSONObject depth too large to process.", e); + } case '[': this.back(); - return new JSONArray(this, this.config); + try { + return new JSONArray(this, this.config); + } catch (final StackOverflowError e) { + throw new JSONException("JSONArray depth too large to process.", e); + } } /* diff --git a/hutool-json/src/main/java/cn/hutool/json/xml/XMLTokener.java b/hutool-json/src/main/java/cn/hutool/json/xml/XMLTokener.java index 548ac934f..4546432aa 100644 --- a/hutool-json/src/main/java/cn/hutool/json/xml/XMLTokener.java +++ b/hutool-json/src/main/java/cn/hutool/json/xml/XMLTokener.java @@ -113,9 +113,38 @@ public class XMLTokener extends JSONTokener { throw syntaxError("Missing ';' in XML entity: &" + sb); } } - final String string = sb.toString(); - final Object object = entity.get(string); - return object != null ? object : ampersand + string + ";"; + return unescapeEntity(sb.toString()); + } + + /** + * Unescape an XML entity encoding; + * + * @param e entity (only the actual entity value, not the preceding & or ending ; + * @return Unescape str + */ + static String unescapeEntity(final String e) { + // validate + if (e == null || e.isEmpty()) { + return ""; + } + // if our entity is an encoded unicode point, parse it. + if (e.charAt(0) == '#') { + final int cp; + if (e.charAt(1) == 'x' || e.charAt(1) == 'X') { + // hex encoded unicode + cp = Integer.parseInt(e.substring(2), 16); + } else { + // decimal encoded unicode + cp = Integer.parseInt(e.substring(1)); + } + return new String(new int[]{cp}, 0, 1); + } + final Character knownEntity = entity.get(e); + if (knownEntity == null) { + // we don't know the entity so keep it encoded + return '&' + e + ';'; + } + return knownEntity.toString(); } /** diff --git a/hutool-json/src/test/java/cn/hutool/json/Issue2746Test.java b/hutool-json/src/test/java/cn/hutool/json/Issue2746Test.java new file mode 100755 index 000000000..f01439f5d --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/Issue2746Test.java @@ -0,0 +1,24 @@ +package cn.hutool.json; + +import cn.hutool.core.text.StrUtil; +import org.junit.Assert; +import org.junit.Test; + +public class Issue2746Test { + + @Test + public void parseObjTest() { + final String str = StrUtil.repeat("{", 1500) + StrUtil.repeat("}", 1500); + try{ + JSONUtil.parseObj(str); + } catch (final JSONException e){ + Assert.assertTrue(e.getMessage().startsWith("A JSONObject can not directly nest another JSONObject or JSONArray")); + } + } + + @Test(expected = JSONException.class) + public void parseTest() { + final String str = StrUtil.repeat("[", 1500) + StrUtil.repeat("]", 1500); + JSONUtil.parseArray(str); + } +} diff --git a/hutool-json/src/test/java/cn/hutool/json/Issue2749Test.java b/hutool-json/src/test/java/cn/hutool/json/Issue2749Test.java new file mode 100755 index 000000000..98b9a9bfa --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/Issue2749Test.java @@ -0,0 +1,37 @@ +package cn.hutool.json; + +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +/** + * https://github.com/dromara/hutool/issues/2749 + *

+ * 由于使用了递归方式解析和写出,导致JSON太长的话容易栈溢出。 + */ +public class Issue2749Test { + + @Test + @Ignore + public void jsonObjectTest() { + final Map map = new HashMap<>(1, 1f); + Map node = map; + for (int i = 0; i < 1000; i++) { + //noinspection unchecked + node = (Map) node.computeIfAbsent("a", k -> new HashMap(1, 1f)); + } + node.put("a", 1); + final String jsonStr = JSONUtil.toJsonStr(map); + + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") + final JSONObject jsonObject = new JSONObject(jsonStr); + Assert.assertNotNull(jsonObject); + + // 栈溢出 + //noinspection ResultOfMethodCallIgnored + jsonObject.toString(); + } +}