Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/main/java/org/json/XML.java
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,28 @@ public static void noSpace(String string) throws JSONException {
}
}

/**
* Throw an exception if the string contains an XML metacharacter
* ({@code < > & " ' /}). Used by {@link #toString(Object)} to reject JSON
* keys that would otherwise be emitted verbatim between {@code <} and
* {@code >} and could break out of the tag context (element injection,
* CWE-91; see issue #1071).
*
* @param string the candidate element name
* @throws JSONException if {@code string} contains an XML metacharacter
*/
static void noXmlMetachars(String string) throws JSONException {
int length = string.length();
for (int i = 0; i < length; i++) {
char c = string.charAt(i);
if (c == '<' || c == '>' || c == '&'
|| c == '"' || c == '\'' || c == '/') {
throw new JSONException("'" + string
+ "' contains an XML metacharacter and may not be used as an element name.");
}
}
}

/**
* Scan the content following the named tag, attaching it to the context.
*
Expand Down Expand Up @@ -968,6 +990,10 @@ private static String toString(final Object object, final String tagName, final
JSONObject jo;
String string;

if (tagName != null) {
noXmlMetachars(tagName);
}

if (object instanceof JSONObject) {

// Emit <tagName>
Expand All @@ -986,6 +1012,9 @@ private static String toString(final Object object, final String tagName, final
// don't use the new entrySet accessor to maintain Android Support
jo = (JSONObject) object;
for (final String key : jo.keySet()) {
if (!key.equals(config.getcDataTagName())) {
noXmlMetachars(key);
}
Object value = jo.opt(key);
if (value == null) {
value = "";
Expand Down
36 changes: 36 additions & 0 deletions src/test/java/org/json/junit/XMLTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,42 @@ public void shouldHandleIllegalJSONNodeNames()
assertTrue("Illegal@node",result.contains("<Illegal@node>someValue2</Illegal@node>"));
}

/**
* A JSON key containing XML metacharacters must not be emitted as a raw
* tag name, since doing so allows the key to break out of its element and
* inject sibling structure into the output (CWE-91, issue #1071).
*/
@Test
public void toStringRejectsElementInjectionInKey()
{
JSONObject jo = new JSONObject(
"{\"a/><injected>evil</injected><a\":\"\"}");
try {
XML.toString(jo, "root");
fail("expected JSONException for key containing XML metacharacters");
} catch (JSONException expected) {
// expected: '/', '<', '>' are rejected in element names
}

// caller-supplied tagName is checked too
try {
XML.toString(new JSONObject(), "bad<tag");
fail("expected JSONException for tagName containing '<'");
} catch (JSONException expected) {
// expected: '<' is rejected in element names
}

// each metacharacter is rejected individually
for (char c : new char[] {'<', '>', '&', '"', '\'', '/'}) {
try {
XML.toString(new JSONObject().put("a" + c + "b", "v"));
fail("expected JSONException for key containing '" + c + "'");
} catch (JSONException expected) {
// expected
}
}
}

/**
* JSONObject with NULL value, to XML.toString()
*/
Expand Down
Loading