Skip to content

Commit bcbbc89

Browse files
committed
Fixes corner case where strict mode isn't working when JSONTokener's next() and back() are called first
1 parent 4f859fd commit bcbbc89

5 files changed

Lines changed: 81 additions & 7 deletions

File tree

src/main/java/org/json/JSONArray.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,21 @@ public JSONArray(JSONTokener x) throws JSONException {
9494
* @throws JSONException If a syntax error occurs during the construction of the JSONArray.
9595
*/
9696
public JSONArray(JSONTokener x, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
97+
this(x, jsonParserConfiguration, x.isAtStart());
98+
}
99+
100+
/**
101+
* Constructs a JSONArray from a JSONTokener and a JSONParserConfiguration, for internal use. <br>
102+
* Never call this instead of using withStrictMode(boolean).
103+
*
104+
* @param x A JSONTokener instance from which the JSONArray is constructed.
105+
* @param jsonParserConfiguration A JSONParserConfiguration instance that controls the behavior of the parser.
106+
* @param eofRequired A boolean that determines whether this array is the root.
107+
* @throws JSONException If a syntax error occurs during the construction of the JSONArray.
108+
*/
109+
JSONArray(JSONTokener x, JSONParserConfiguration jsonParserConfiguration, boolean eofRequired) throws JSONException {
97110
this();
98111

99-
boolean isInitial = x.getPrevious() == 0;
100112
if (x.nextClean() != '[') {
101113
throw x.syntaxError("A JSONArray text must start with '['");
102114
}
@@ -118,10 +130,10 @@ public JSONArray(JSONTokener x, JSONParserConfiguration jsonParserConfiguration)
118130
x.back();
119131
this.myArrayList.add(x.nextValue());
120132
}
121-
if (checkForSyntaxError(x, jsonParserConfiguration, isInitial)) return;
133+
if (checkForSyntaxError(x, jsonParserConfiguration, eofRequired)) return;
122134
}
123135
} else {
124-
if (isInitial && jsonParserConfiguration.isStrictMode() && x.nextClean() != 0) {
136+
if (eofRequired && jsonParserConfiguration.isStrictMode() && x.nextClean() != 0) {
125137
throw x.syntaxError("Strict mode error: Unparsed characters found at end of input text");
126138
}
127139
}

src/main/java/org/json/JSONObject.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,14 +214,31 @@ public JSONObject(JSONTokener x) throws JSONException {
214214
* duplicated key.
215215
*/
216216
public JSONObject(JSONTokener x, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
217+
this(x, jsonParserConfiguration, x.isAtStart());
218+
}
219+
220+
/**
221+
* Construct a JSONObject from a JSONTokener with custom json parse configurations, for internal use. <br>
222+
* Never call this instead of using withStrictMode(boolean).
223+
*
224+
* @param x
225+
* A JSONTokener object containing the source string.
226+
* @param jsonParserConfiguration
227+
* Variable to pass parser custom configuration for json parsing.
228+
* @param eofRequired
229+
* A boolean that determines whether this object is the root.
230+
* @throws JSONException
231+
* If there is a syntax error in the source string or a
232+
* duplicated key.
233+
*/
234+
JSONObject(JSONTokener x, JSONParserConfiguration jsonParserConfiguration, boolean eofRequired) throws JSONException {
217235
this();
218-
boolean isInitial = x.getPrevious() == 0;
219236

220237
if (x.nextClean() != '{') {
221238
throw x.syntaxError("A JSONObject text must begin with '{'");
222239
}
223240
for (;;) {
224-
if (parseJSONObject(x, jsonParserConfiguration, isInitial)) {
241+
if (parseJSONObject(x, jsonParserConfiguration, eofRequired)) {
225242
return;
226243
}
227244
}

src/main/java/org/json/JSONTokener.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ public void setJsonParserConfiguration(JSONParserConfiguration jsonParserConfigu
120120
this.jsonParserConfiguration = jsonParserConfiguration;
121121
}
122122

123+
/**
124+
* Returns whether the tokener is currently positioned at the beginning,
125+
*
126+
* @return true if the current input position is the beginning
127+
*/
128+
public boolean isAtStart() {
129+
return this.index == 0;
130+
}
131+
123132
/**
124133
* Back up one character. This provides a sort of lookahead capability,
125134
* so that you can test for a digit or letter before attempting to parse
@@ -458,14 +467,14 @@ public Object nextValue() throws JSONException {
458467
case '{':
459468
this.back();
460469
try {
461-
return new JSONObject(this, jsonParserConfiguration);
470+
return new JSONObject(this, jsonParserConfiguration, false);
462471
} catch (StackOverflowError e) {
463472
throw new JSONException("JSON Array or Object depth too large to process.", e);
464473
}
465474
case '[':
466475
this.back();
467476
try {
468-
return new JSONArray(this, jsonParserConfiguration);
477+
return new JSONArray(this, jsonParserConfiguration, false);
469478
} catch (StackOverflowError e) {
470479
throw new JSONException("JSON Array or Object depth too large to process.", e);
471480
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,4 +1566,22 @@ public void TestLenientCommas() {
15661566
"[1,null,3]", jsonArray.toString());
15671567
}
15681568
}
1569+
1570+
@Test
1571+
public void strictModeShouldCheckTrailingCharactersAfterNextAndBack() {
1572+
JSONParserConfiguration strict =
1573+
new JSONParserConfiguration().withStrictMode();
1574+
1575+
JSONTokener tok = new JSONTokener("[]xxx");
1576+
1577+
tok.next();
1578+
tok.back();
1579+
1580+
JSONException exception = assertThrows(
1581+
JSONException.class,
1582+
() -> new JSONArray(tok, strict));
1583+
1584+
assertTrue(exception.getMessage().contains(
1585+
"Unparsed characters found at end of input text"));
1586+
}
15691587
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4337,4 +4337,22 @@ public void testStringToNumberInvalidFormats() {
43374337
}
43384338
}
43394339

4340+
@Test
4341+
public void strictModeShouldCheckTrailingCharactersAfterNextAndBack() {
4342+
JSONParserConfiguration strict =
4343+
new JSONParserConfiguration().withStrictMode();
4344+
4345+
JSONTokener tok = new JSONTokener("{}xxx");
4346+
4347+
tok.next();
4348+
tok.back();
4349+
4350+
JSONException exception = assertThrows(
4351+
JSONException.class,
4352+
() -> new JSONObject(tok, strict));
4353+
4354+
assertTrue(exception.getMessage().contains(
4355+
"Unparsed characters found at end of input text"));
4356+
}
4357+
43404358
}

0 commit comments

Comments
 (0)