Skip to content
Closed
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
48 changes: 38 additions & 10 deletions src/main/java/org/json/JSONPointer.java
Original file line number Diff line number Diff line change
Expand Up @@ -224,18 +224,46 @@
* @throws JSONPointerException is thrown if the index is out of bounds
*/
private static Object readByIndexToken(Object current, String indexToken) throws JSONPointerException {
int index = toArrayIndex(indexToken);
JSONArray currentArr = (JSONArray) current;
if (index >= currentArr.length()) {
throw new JSONPointerException(format("index %s is out of bounds - the array has %d elements", indexToken,
Integer.valueOf(currentArr.length())));
}
try {
int index = Integer.parseInt(indexToken);
JSONArray currentArr = (JSONArray) current;
if (index >= currentArr.length()) {
throw new JSONPointerException(format("index %s is out of bounds - the array has %d elements", indexToken,
Integer.valueOf(currentArr.length())));
return currentArr.get(index);
} catch (JSONException e) {
throw new JSONPointerException("Error reading value at index position " + index, e);
}
}

/**
* Converts an array index token to its ordinal position. RFC 6901 section 4 restricts an array
* index to {@code "0"} or to a digit string which does not start with a zero:
* {@code array-index = %x30 / ( %x31-39 *(%x30-39) )}. Number spellings which Java accepts but
* the grammar does not, such as {@code "01"}, {@code "+1"} or {@code "-0"}, are rejected so that
* they cannot be read as a different element than the one that was asked for.
*
* @param indexToken the array index in string form
* @return the ordinal position the token denotes
* @throws JSONPointerException if the token is not a valid array index
* @see <a href="https://tools.ietf.org/html/rfc6901#section-4">rfc6901 section 4</a>
*/
private static int toArrayIndex(String indexToken) throws JSONPointerException {
char first = indexToken.charAt(0);
if (first != '0' && (first < '1' || first > '9')) {
throw new JSONPointerException(format("%s is not an array index", indexToken));

Check failure on line 255 in src/main/java/org/json/JSONPointer.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "%s is not an array index" 4 times.

See more on https://sonarcloud.io/project/issues?id=stleary_JSON-java&issues=AaGngq_8o8Gen74zQNw1&open=AaGngq_8o8Gen74zQNw1&pullRequest=1098
}
if (first == '0' && indexToken.length() > 1) {
throw new JSONPointerException(format("%s is not an array index", indexToken));
}
for (int i = 1; i < indexToken.length(); i++) {
if (indexToken.charAt(i) < '0' || indexToken.charAt(i) > '9') {
throw new JSONPointerException(format("%s is not an array index", indexToken));
}
try {
return currentArr.get(index);
} catch (JSONException e) {
throw new JSONPointerException("Error reading value at index position " + index, e);
}
}
try {
return Integer.parseInt(indexToken);
} catch (NumberFormatException e) {
throw new JSONPointerException(format("%s is not an array index", indexToken), e);
}
Expand Down
26 changes: 26 additions & 0 deletions src/test/java/org/json/junit/JSONPointerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,32 @@ public void tokenListIsCopiedInConstructor() {
}
}

/**
* RFC 6901 section 4 restricts an array index to "0", or to a digit string that does not
* start with a zero: {@code array-index = %x30 / ( %x31-39 *(%x30-39) )}. Tokens such as
* {@code "01"} and {@code "+1"} are therefore not array indexes, and navigating into an
* array with them must fail instead of silently resolving to a different element.
*
* @see <a href="https://tools.ietf.org/html/rfc6901#section-4">rfc6901 section 4</a>
*/
@Test
public void arrayIndexTokenMustMatchRfc6901Grammar() {
JSONArray array = new JSONArray("[10,20,30,40,50,60]");
assertEquals(10, array.query("/0"));
assertEquals(20, array.query("/1"));
for (String token : new String[] { "01", "00", "+1", "-0", "1.0", "(1)", "0000000000000000001" }) {
try {
Object value = array.query("/" + token);
fail("Expected JSONPointerException for array index token '" + token + "', got " + value);
} catch (JSONPointerException e) {
assertTrue("Expected index message for token '" + token + "', got: " + e.getMessage(),
e.getMessage().contains("is not an array index"));
}
}
// the same rule applies to JSONArray.optQuery, which reports a failed query as null
assertTrue("Expected null", array.optQuery("/01") == null);
}

/**
* Coverage for JSONObject query(String)
*/
Expand Down
Loading