Skip to content

Commit f566a1d

Browse files
committed
fix: limit the nesting depth
1 parent 5920eca commit f566a1d

File tree

4 files changed

+110
-3
lines changed

4 files changed

+110
-3
lines changed

src/main/java/org/json/XML.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public static void noSpace(String string) throws JSONException {
232232
* @return true if the close tag is processed.
233233
* @throws JSONException
234234
*/
235-
private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config)
235+
private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, int currentNestingDepth)
236236
throws JSONException {
237237
char c;
238238
int i;
@@ -402,7 +402,11 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
402402

403403
} else if (token == LT) {
404404
// Nested element
405-
if (parse(x, jsonObject, tagName, config)) {
405+
if (currentNestingDepth == config.getMaxNestingDepth()) {
406+
throw x.syntaxError("Maximum nesting depth of " + config.getMaxNestingDepth() + " reached");
407+
}
408+
409+
if (parse(x, jsonObject, tagName, config, currentNestingDepth + 1)) {
406410
if (config.getForceList().contains(tagName)) {
407411
// Force the value to be an array
408412
if (jsonObject.length() == 0) {
@@ -644,6 +648,10 @@ public static JSONObject toJSONObject(Reader reader, boolean keepStrings) throws
644648
* All values are converted as strings, for 1, 01, 29.0 will not be coerced to
645649
* numbers but will instead be the exact value as seen in the XML document.
646650
*
651+
* This method can parse documents with a maximum nesting depth of 256. If you
652+
* need to parse documents with a nesting depth greater than 256, you should use
653+
*
654+
*
647655
* @param reader The XML source reader.
648656
* @param config Configuration options for the parser
649657
* @return A JSONObject containing the structured data from the XML string.
@@ -655,7 +663,7 @@ public static JSONObject toJSONObject(Reader reader, XMLParserConfiguration conf
655663
while (x.more()) {
656664
x.skipPast("<");
657665
if(x.more()) {
658-
parse(x, jo, null, config);
666+
parse(x, jo, null, config, 0);
659667
}
660668
}
661669
return jo;

src/main/java/org/json/XMLParserConfiguration.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
*/
1717
@SuppressWarnings({""})
1818
public class XMLParserConfiguration {
19+
/**
20+
* Used to indicate there's no defined limit to the maximum nesting depth when parsing a XML
21+
* document to JSON.
22+
*/
23+
public static final int UNDEFINED_MAXIMUM_NESTING_DEPTH = -1;
24+
1925
/** Original Configuration of the XML Parser. */
2026
public static final XMLParserConfiguration ORIGINAL
2127
= new XMLParserConfiguration();
@@ -54,6 +60,12 @@ public class XMLParserConfiguration {
5460
*/
5561
private Set<String> forceList;
5662

63+
/**
64+
* When parsing the XML into JSON, specifies the tags whose values should be converted
65+
* to arrays
66+
*/
67+
private int maxNestingDepth = UNDEFINED_MAXIMUM_NESTING_DEPTH;
68+
5769
/**
5870
* Default parser configuration. Does not keep strings (tries to implicitly convert
5971
* values), and the CDATA Tag Name is "content".
@@ -297,4 +309,33 @@ public XMLParserConfiguration withForceList(final Set<String> forceList) {
297309
newConfig.forceList = Collections.unmodifiableSet(cloneForceList);
298310
return newConfig;
299311
}
312+
313+
/**
314+
* The maximum nesting depth that the parser will descend before throwing an exception
315+
* when parsing the XML into JSON.
316+
* @return the maximum nesting depth set for this configuration
317+
*/
318+
public int getMaxNestingDepth() {
319+
return maxNestingDepth;
320+
}
321+
322+
/**
323+
* Defines the maximum nesting depth that the parser will descend before throwing an exception
324+
* when parsing the XML into JSON. The default max nesting depth is undefined, which means the
325+
* parser will go as deep as the maximum call stack size allows. Using any negative value as a
326+
* parameter is equivalent to setting no limit to the nesting depth.
327+
* @param maxNestingDepth the maximum nesting depth allowed to the XML parser
328+
* @return The existing configuration will not be modified. A new configuration is returned.
329+
*/
330+
public XMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) {
331+
XMLParserConfiguration newConfig = this.clone();
332+
333+
if (maxNestingDepth > UNDEFINED_MAXIMUM_NESTING_DEPTH) {
334+
newConfig.maxNestingDepth = maxNestingDepth;
335+
} else {
336+
newConfig.maxNestingDepth = UNDEFINED_MAXIMUM_NESTING_DEPTH;
337+
}
338+
339+
return newConfig;
340+
}
300341
}

src/test/java/org/json/junit/XMLConfigurationTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,6 +1051,29 @@ public void testEmptyTagForceList() {
10511051

10521052
Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject);
10531053
}
1054+
1055+
@Test
1056+
public void testMaxNestingDepthIsSet() {
1057+
XMLParserConfiguration xmlParserConfiguration = XMLParserConfiguration.ORIGINAL;
1058+
1059+
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), XMLParserConfiguration.UNDEFINED_MAXIMUM_NESTING_DEPTH);
1060+
1061+
xmlParserConfiguration = xmlParserConfiguration.withMaxNestingDepth(42);
1062+
1063+
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), 42);
1064+
1065+
xmlParserConfiguration = xmlParserConfiguration.withMaxNestingDepth(0);
1066+
1067+
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), 0);
1068+
1069+
xmlParserConfiguration = xmlParserConfiguration.withMaxNestingDepth(-31415926);
1070+
1071+
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), XMLParserConfiguration.UNDEFINED_MAXIMUM_NESTING_DEPTH);
1072+
1073+
xmlParserConfiguration = xmlParserConfiguration.withMaxNestingDepth(Integer.MIN_VALUE);
1074+
1075+
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), XMLParserConfiguration.UNDEFINED_MAXIMUM_NESTING_DEPTH);
1076+
}
10541077

10551078
/**
10561079
* Convenience method, given an input string and expected result,

0 commit comments

Comments
 (0)