diff --git a/src/main/java/org/json/JSONPointer.java b/src/main/java/org/json/JSONPointer.java
index a38043d7b..13af84e78 100644
--- a/src/main/java/org/json/JSONPointer.java
+++ b/src/main/java/org/json/JSONPointer.java
@@ -224,18 +224,46 @@ public Object queryFrom(Object document) throws JSONPointerException {
* @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 rfc6901 section 4
+ */
+ 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));
+ }
+ 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);
}
diff --git a/src/test/java/org/json/junit/JSONPointerTest.java b/src/test/java/org/json/junit/JSONPointerTest.java
index 3d70e23c0..7db585069 100644
--- a/src/test/java/org/json/junit/JSONPointerTest.java
+++ b/src/test/java/org/json/junit/JSONPointerTest.java
@@ -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 rfc6901 section 4
+ */
+ @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)
*/