mirror of
https://gitee.com/dromara/hutool.git
synced 2025-04-05 17:37:59 +08:00
增加ParseConfig,通过增加maxNestingDepth参数避免StackOverflowError问题,修复CVE-2022-45688漏洞
This commit is contained in:
parent
17d773181e
commit
6a2b585de0
@ -15,6 +15,7 @@
|
||||
* 【core 】 修复RandomUtil.randomInt,RandomUtil.randomLong边界问题(pr#3450@Github)
|
||||
* 【db 】 修复Druid连接池无法设置部分属性问题(issue#I8STFC@Gitee)
|
||||
* 【core 】 修复金额转换为英文时缺少 trillion 单位问题(pr#3454@Github)
|
||||
* 【json 】 增加ParseConfig,通过增加maxNestingDepth参数避免StackOverflowError问题,修复CVE-2022-45688漏洞(issue#2748@Github)
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
# 5.8.24(2023-12-23)
|
||||
|
@ -3,6 +3,7 @@ package cn.hutool.json;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.json.xml.JSONXMLParser;
|
||||
import cn.hutool.json.xml.JSONXMLSerializer;
|
||||
import cn.hutool.json.xml.ParseConfig;
|
||||
|
||||
/**
|
||||
* 提供静态方法在XML和JSONObject之间转换
|
||||
@ -86,6 +87,22 @@ public class XML {
|
||||
return toJSONObject(new JSONObject(), string, keepStrings);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换XML为JSONObject
|
||||
* 转换过程中一些信息可能会丢失,JSON中无法区分节点和属性,相同的节点将被处理为JSONArray。
|
||||
* Content text may be placed in a "content" member. Comments, prologs, DTDs, and {@code <[ [ ]]>} are ignored.
|
||||
* All values are converted as strings, for 1, 01, 29.0 will not be coerced to numbers but will instead be the exact value as seen in the XML document.
|
||||
*
|
||||
* @param string XML字符串
|
||||
* @param parseConfig XML解析选项
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException Thrown if there is an errors while parsing the string
|
||||
* @since 5.8.25
|
||||
*/
|
||||
public static JSONObject toJSONObject(final String string, final ParseConfig parseConfig) throws JSONException {
|
||||
return toJSONObject(new JSONObject(), string, parseConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换XML为JSONObject
|
||||
* 转换过程中一些信息可能会丢失,JSON中无法区分节点和属性,相同的节点将被处理为JSONArray。
|
||||
@ -102,6 +119,22 @@ public class XML {
|
||||
return jo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换XML为JSONObject
|
||||
* 转换过程中一些信息可能会丢失,JSON中无法区分节点和属性,相同的节点将被处理为JSONArray。
|
||||
*
|
||||
* @param jo JSONObject
|
||||
* @param xmlStr XML字符串
|
||||
* @param parseConfig XML解析选项
|
||||
* @return A JSONObject 解析后的JSON对象,与传入的jo为同一对象
|
||||
* @throws JSONException 解析异常
|
||||
* @since 5.8.25
|
||||
*/
|
||||
public static JSONObject toJSONObject(final JSONObject jo, final String xmlStr, final ParseConfig parseConfig) throws JSONException {
|
||||
JSONXMLParser.parseJSONObject(jo, xmlStr, parseConfig);
|
||||
return jo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换JSONObject为XML
|
||||
*
|
||||
|
@ -24,9 +24,22 @@ public class JSONXMLParser {
|
||||
* @throws JSONException 解析异常
|
||||
*/
|
||||
public static void parseJSONObject(JSONObject jo, String xmlStr, boolean keepStrings) throws JSONException {
|
||||
XMLTokener x = new XMLTokener(xmlStr, jo.getConfig());
|
||||
parseJSONObject(jo, xmlStr, ParseConfig.of().setKeepStrings(keepStrings));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换XML为JSONObject
|
||||
* 转换过程中一些信息可能会丢失,JSON中无法区分节点和属性,相同的节点将被处理为JSONArray。
|
||||
*
|
||||
* @param xmlStr XML字符串
|
||||
* @param jo JSONObject
|
||||
* @param parseConfig 解析选项
|
||||
* @throws JSONException 解析异常
|
||||
*/
|
||||
public static void parseJSONObject(final JSONObject jo, final String xmlStr, final ParseConfig parseConfig) throws JSONException {
|
||||
final XMLTokener x = new XMLTokener(xmlStr, jo.getConfig());
|
||||
while (x.more() && x.skipPast("<")) {
|
||||
parse(x, jo, null, keepStrings);
|
||||
parse(x, jo, null, parseConfig, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,10 +49,12 @@ public class JSONXMLParser {
|
||||
* @param x The XMLTokener containing the source string.
|
||||
* @param context The JSONObject that will include the new material.
|
||||
* @param name The tag name.
|
||||
* @param parseConfig 解析选项
|
||||
* @param currentNestingDepth 当前层级
|
||||
* @return true if the close tag is processed.
|
||||
* @throws JSONException JSON异常
|
||||
*/
|
||||
private static boolean parse(XMLTokener x, JSONObject context, String name, boolean keepStrings) throws JSONException {
|
||||
private static boolean parse(XMLTokener x, JSONObject context, String name, ParseConfig parseConfig, int currentNestingDepth) throws JSONException {
|
||||
char c;
|
||||
int i;
|
||||
JSONObject jsonobject;
|
||||
@ -112,6 +127,7 @@ public class JSONXMLParser {
|
||||
tagName = (String) token;
|
||||
token = null;
|
||||
jsonobject = new JSONObject();
|
||||
final boolean keepStrings = parseConfig.isKeepStrings();
|
||||
for (; ; ) {
|
||||
if (token == null) {
|
||||
token = x.nextToken();
|
||||
@ -155,14 +171,21 @@ public class JSONXMLParser {
|
||||
return false;
|
||||
} else if (token instanceof String) {
|
||||
string = (String) token;
|
||||
if (string.length() > 0) {
|
||||
if (!string.isEmpty()) {
|
||||
jsonobject.accumulate("content", keepStrings ? token : InternalJSONUtil.stringToValue(string));
|
||||
}
|
||||
|
||||
} else if (token == XML.LT) {
|
||||
// Nested element
|
||||
if (parse(x, jsonobject, tagName, keepStrings)) {
|
||||
if (jsonobject.size() == 0) {
|
||||
// issue#2748 of CVE-2022-45688
|
||||
final int maxNestingDepth = parseConfig.getMaxNestingDepth();
|
||||
if (maxNestingDepth > -1 && currentNestingDepth >= maxNestingDepth) {
|
||||
throw x.syntaxError("Maximum nesting depth of " + maxNestingDepth + " reached");
|
||||
}
|
||||
|
||||
// Nested element
|
||||
if (parse(x, jsonobject, tagName, parseConfig, currentNestingDepth + 1)) {
|
||||
if (jsonobject.isEmpty()) {
|
||||
context.accumulate(tagName, "");
|
||||
} else if (jsonobject.size() == 1 && jsonobject.get("content") != null) {
|
||||
context.accumulate(tagName, jsonobject.get("content"));
|
||||
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2024. looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* https://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package cn.hutool.json.xml;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* XML解析为JSON的可选选项<br>
|
||||
* 参考:https://github.com/stleary/JSON-java/blob/master/src/main/java/org/json/ParserConfiguration.java
|
||||
*
|
||||
* @author AylwardJ, Looly
|
||||
*/
|
||||
public class ParseConfig implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 默认最大嵌套深度
|
||||
*/
|
||||
public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512;
|
||||
|
||||
/**
|
||||
* 创建ParseConfig
|
||||
*
|
||||
* @return ParseConfig
|
||||
*/
|
||||
public static ParseConfig of() {
|
||||
return new ParseConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否保持值为String类型,如果为{@code false},则尝试转换为对应类型(numeric, boolean, string)
|
||||
*/
|
||||
private boolean keepStrings;
|
||||
/**
|
||||
* 最大嵌套深度,用于解析时限制解析层级,当大于这个层级时抛出异常,-1表示无限制
|
||||
*/
|
||||
private int maxNestingDepth = -1;
|
||||
|
||||
/**
|
||||
* 是否保持值为String类型,如果为{@code false},则尝试转换为对应类型(numeric, boolean, string)
|
||||
*
|
||||
* @return 是否保持值为String类型
|
||||
*/
|
||||
public boolean isKeepStrings() {
|
||||
return keepStrings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否保持值为String类型,如果为{@code false},则尝试转换为对应类型(numeric, boolean, string)
|
||||
*
|
||||
* @param keepStrings 是否保持值为String类型
|
||||
* @return this
|
||||
*/
|
||||
public ParseConfig setKeepStrings(final boolean keepStrings) {
|
||||
this.keepStrings = keepStrings;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最大嵌套深度,用于解析时限制解析层级,当大于这个层级时抛出异常,-1表示无限制
|
||||
*
|
||||
* @return 最大嵌套深度
|
||||
*/
|
||||
public int getMaxNestingDepth() {
|
||||
return maxNestingDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最大嵌套深度,用于解析时限制解析层级,当大于这个层级时抛出异常,-1表示无限制
|
||||
*
|
||||
* @param maxNestingDepth 最大嵌套深度
|
||||
* @return this
|
||||
*/
|
||||
public ParseConfig setMaxNestingDepth(final int maxNestingDepth) {
|
||||
this.maxNestingDepth = maxNestingDepth;
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package cn.hutool.json.xml;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONException;
|
||||
import cn.hutool.json.XML;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class Issue2748Test {
|
||||
|
||||
@Test
|
||||
public void toJSONObjectTest() {
|
||||
final String s = StrUtil.repeat("<a>", 600);
|
||||
|
||||
Assert.assertThrows(JSONException.class, () -> {
|
||||
XML.toJSONObject(s, ParseConfig.of().setMaxNestingDepth(512));
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user