Skip to content

Commit 4f859fd

Browse files
authored
Merge pull request stleary#1072 from mechko/1071-xml-tostring-name-validation
stleary#1071: reject invalid XML element names in XML.toString (CWE-91)
2 parents 6c14048 + 3dd0ec0 commit 4f859fd

2 files changed

Lines changed: 65 additions & 0 deletions

File tree

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,28 @@ public static void noSpace(String string) throws JSONException {
238238
}
239239
}
240240

241+
/**
242+
* Throw an exception if the string contains an XML metacharacter
243+
* ({@code < > & " ' /}). Used by {@link #toString(Object)} to reject JSON
244+
* keys that would otherwise be emitted verbatim between {@code <} and
245+
* {@code >} and could break out of the tag context (element injection,
246+
* CWE-91; see issue #1071).
247+
*
248+
* @param string the candidate element name
249+
* @throws JSONException if {@code string} contains an XML metacharacter
250+
*/
251+
static void noXmlMetachars(String string) throws JSONException {
252+
int length = string.length();
253+
for (int i = 0; i < length; i++) {
254+
char c = string.charAt(i);
255+
if (c == '<' || c == '>' || c == '&'
256+
|| c == '"' || c == '\'' || c == '/') {
257+
throw new JSONException("'" + string
258+
+ "' contains an XML metacharacter and may not be used as an element name.");
259+
}
260+
}
261+
}
262+
241263
/**
242264
* Scan the content following the named tag, attaching it to the context.
243265
*
@@ -974,6 +996,10 @@ private static String toString(final Object object, final String tagName, final
974996
JSONObject jo;
975997
String string;
976998

999+
if (tagName != null) {
1000+
noXmlMetachars(tagName);
1001+
}
1002+
9771003
if (object instanceof JSONObject) {
9781004

9791005
// Emit <tagName>
@@ -992,6 +1018,9 @@ private static String toString(final Object object, final String tagName, final
9921018
// don't use the new entrySet accessor to maintain Android Support
9931019
jo = (JSONObject) object;
9941020
for (final String key : jo.keySet()) {
1021+
if (!key.equals(config.getcDataTagName())) {
1022+
noXmlMetachars(key);
1023+
}
9951024
Object value = jo.opt(key);
9961025
if (value == null) {
9971026
value = "";

‎src/test/java/org/json/junit/XMLTest.java‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,42 @@ public void shouldHandleIllegalJSONNodeNames()
565565
assertTrue("Illegal@node",result.contains("<Illegal@node>someValue2</Illegal@node>"));
566566
}
567567

568+
/**
569+
* A JSON key containing XML metacharacters must not be emitted as a raw
570+
* tag name, since doing so allows the key to break out of its element and
571+
* inject sibling structure into the output (CWE-91, issue #1071).
572+
*/
573+
@Test
574+
public void toStringRejectsElementInjectionInKey()
575+
{
576+
JSONObject jo = new JSONObject(
577+
"{\"a/><injected>evil</injected><a\":\"\"}");
578+
try {
579+
XML.toString(jo, "root");
580+
fail("expected JSONException for key containing XML metacharacters");
581+
} catch (JSONException expected) {
582+
// expected: '/', '<', '>' are rejected in element names
583+
}
584+
585+
// caller-supplied tagName is checked too
586+
try {
587+
XML.toString(new JSONObject(), "bad<tag");
588+
fail("expected JSONException for tagName containing '<'");
589+
} catch (JSONException expected) {
590+
// expected: '<' is rejected in element names
591+
}
592+
593+
// each metacharacter is rejected individually
594+
for (char c : new char[] {'<', '>', '&', '"', '\'', '/'}) {
595+
try {
596+
XML.toString(new JSONObject().put("a" + c + "b", "v"));
597+
fail("expected JSONException for key containing '" + c + "'");
598+
} catch (JSONException expected) {
599+
// expected
600+
}
601+
}
602+
}
603+
568604
/**
569605
* JSONObject with NULL value, to XML.toString()
570606
*/

0 commit comments

Comments
 (0)