Skip to content

Commit

Permalink
Merge pull request #772 from eamonnmcmanus/complexkey
Browse files Browse the repository at this point in the history
Disallow nested objects and arrays as keys in objects.
  • Loading branch information
stleary committed Oct 1, 2023
2 parents ef68cdf + eaa5611 commit beb2fb5
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 19 deletions.
16 changes: 4 additions & 12 deletions src/main/java/org/json/JSONObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -208,22 +208,14 @@ public JSONObject(JSONTokener x) throws JSONException {
throw x.syntaxError("A JSONObject text must begin with '{'");
}
for (;;) {
char prev = x.getPrevious();
c = x.nextClean();
switch (c) {
case 0:
throw x.syntaxError("A JSONObject text must end with '}'");
case '}':
return;
case '{':
case '[':
if(prev=='{') {
throw x.syntaxError("A JSON Object can not directly nest another JSON Object or JSON Array.");
}
// fall through
default:
x.back();
key = x.nextValue().toString();
key = x.nextSimpleValue(c).toString();
}

// The key is followed by ':'.
Expand Down Expand Up @@ -1726,12 +1718,12 @@ && isValidMethodName(method.getName())) {
final Object result = method.invoke(bean);
if (result != null) {
// check cyclic dependency and throw error if needed
// the wrap and populateMap combination method is
// the wrap and populateMap combination method is
// itself DFS recursive
if (objectsRecord.contains(result)) {
throw recursivelyDefinedObjectException(key);
}

objectsRecord.add(result);

this.map.put(key, wrap(result, objectsRecord));
Expand All @@ -1740,7 +1732,7 @@ && isValidMethodName(method.getName())) {

// we don't use the result anywhere outside of wrap
// if it's a resource we should be sure to close it
// after calling toString
// after calling toString
if (result instanceof Closeable) {
try {
((Closeable) result).close();
Expand Down
16 changes: 11 additions & 5 deletions src/main/java/org/json/JSONTokener.java
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,7 @@ public String nextTo(String delimiters) throws JSONException {
*/
public Object nextValue() throws JSONException {
char c = this.nextClean();
String string;

switch (c) {
case '"':
case '\'':
return this.nextString(c);
case '{':
this.back();
try {
Expand All @@ -423,6 +418,17 @@ public Object nextValue() throws JSONException {
throw new JSONException("JSON Array or Object depth too large to process.", e);
}
}
return nextSimpleValue(c);
}

Object nextSimpleValue(char c) {
String string;

switch (c) {
case '"':
case '\'':
return this.nextString(c);
}

/*
* Handle unquoted text. This could be the values true, false, or
Expand Down
16 changes: 15 additions & 1 deletion src/test/java/org/json/junit/JSONArrayTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public void nullException() {
* Expects a JSONException.
*/
@Test
public void emptStr() {
public void emptyStr() {
String str = "";
try {
assertNull("Should throw an exception", new JSONArray(str));
Expand Down Expand Up @@ -460,6 +460,20 @@ public void failedGetArrayValues() {
Util.checkJSONArrayMaps(jsonArray);
}

/**
* The JSON parser is permissive of unambiguous unquoted keys and values.
* Such JSON text should be allowed, even if it does not strictly conform
* to the spec. However, after being parsed, toString() should emit strictly
* conforming JSON text.
*/
@Test
public void unquotedText() {
String str = "[value1, something!, (parens), foo@bar.com, 23, 23+45]";
JSONArray jsonArray = new JSONArray(str);
List<Object> expected = Arrays.asList("value1", "something!", "(parens)", "foo@bar.com", 23, "23+45");
assertEquals(expected, jsonArray.toList());
}

/**
* Exercise JSONArray.join() by converting a JSONArray into a
* comma-separated string. Since this is very nearly a JSON document,
Expand Down
42 changes: 41 additions & 1 deletion src/test/java/org/json/junit/JSONObjectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,17 @@ public void jsonObjectByNullBean() {
*/
@Test
public void unquotedText() {
String str = "{key1:value1, key2:42}";
String str = "{key1:value1, key2:42, 1.2 : 3.4, -7e5 : something!}";
JSONObject jsonObject = new JSONObject(str);
String textStr = jsonObject.toString();
assertTrue("expected key1", textStr.contains("\"key1\""));
assertTrue("expected value1", textStr.contains("\"value1\""));
assertTrue("expected key2", textStr.contains("\"key2\""));
assertTrue("expected 42", textStr.contains("42"));
assertTrue("expected 1.2", textStr.contains("\"1.2\""));
assertTrue("expected 3.4", textStr.contains("3.4"));
assertTrue("expected -7E+5", textStr.contains("\"-7E+5\""));
assertTrue("expected something!", textStr.contains("\"something!\""));
Util.checkJSONObjectMaps(jsonObject);
}

Expand Down Expand Up @@ -2224,6 +2228,42 @@ public void jsonObjectParsingErrors() {
"Expected a ',' or '}' at 15 [character 16 line 1]",
e.getMessage());
}
try {
// key is a nested map
String str = "{{\"foo\": \"bar\"}: \"baz\"}";
assertNull("Expected an exception",new JSONObject(str));
} catch (JSONException e) {
assertEquals("Expecting an exception message",
"Missing value at 1 [character 2 line 1]",
e.getMessage());
}
try {
// key is a nested array containing a map
String str = "{\"a\": 1, [{\"foo\": \"bar\"}]: \"baz\"}";
assertNull("Expected an exception",new JSONObject(str));
} catch (JSONException e) {
assertEquals("Expecting an exception message",
"Missing value at 9 [character 10 line 1]",
e.getMessage());
}
try {
// key contains }
String str = "{foo}: 2}";
assertNull("Expected an exception",new JSONObject(str));
} catch (JSONException e) {
assertEquals("Expecting an exception message",
"Expected a ':' after a key at 5 [character 6 line 1]",
e.getMessage());
}
try {
// key contains ]
String str = "{foo]: 2}";
assertNull("Expected an exception",new JSONObject(str));
} catch (JSONException e) {
assertEquals("Expecting an exception message",
"Expected a ':' after a key at 5 [character 6 line 1]",
e.getMessage());
}
try {
// \0 after ,
String str = "{\"myKey\":true, \0\"myOtherKey\":false}";
Expand Down

0 comments on commit beb2fb5

Please sign in to comment.