From 02fcc4e5da7af8aa15e7fdad5b1430c3ec13a3d0 Mon Sep 17 00:00:00 2001 From: Mobin Dariush <34644374+dalisyron@users.noreply.github.com> Date: Mon, 8 Jun 2026 23:08:38 +0200 Subject: [PATCH] Fix #1009, reject empty array reference token in decode_array_index_from_pointer An empty reference token (the path "/" applied to an array, or a trailing/empty token such as "/0/") was accepted by decode_array_index_from_pointer() and decoded as index 0, contrary to RFC 6901. Invalid JSON Pointers could therefore resolve to the first array element, so cJSONUtils_GetPointer() and the JSON Patch add/remove/replace/move/copy/test operations could read or modify array[0] for a path that is not a valid array location. Reject an empty reference token (leading '\0' or '/') before parsing the index. The valid empty-string object key (e.g. "/" on an object) is unaffected, since that path does not go through array index decoding. Add regression tests for cJSONUtils_GetPointer and JSON Patch. --- cJSON_Utils.c | 6 +++++ tests/json-patch-tests/cjson-utils-tests.json | 14 ++++++++++- tests/old_utils_tests.c | 23 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/cJSON_Utils.c b/cJSON_Utils.c index 8b38eb253..6403eb200 100644 --- a/cJSON_Utils.c +++ b/cJSON_Utils.c @@ -276,6 +276,12 @@ static cJSON_bool decode_array_index_from_pointer(const unsigned char * const po size_t parsed_index = 0; size_t position = 0; + if ((pointer[0] == '\0') || (pointer[0] == '/')) + { + /* an empty reference token is not a valid array index */ + return 0; + } + if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/'))) { /* leading zeroes are not permitted */ diff --git a/tests/json-patch-tests/cjson-utils-tests.json b/tests/json-patch-tests/cjson-utils-tests.json index 28a5e3073..279f30529 100644 --- a/tests/json-patch-tests/cjson-utils-tests.json +++ b/tests/json-patch-tests/cjson-utils-tests.json @@ -87,5 +87,17 @@ "doc": {"foo": {"bar": 1}}, "patch": [{"op": "add", "path": "/foo/bar/baz", "value": "5"}], "error": "attempting to add to subfield of non-object" - } + }, + { + "comment": "16, an empty reference token is not a valid array index", + "doc": [ "first", "second", "third" ], + "patch": [{ "op": "remove", "path": "/" }], + "error": "an empty reference token is not a valid array index" + }, + { + "comment": "17, an empty reference token is not a valid array index", + "doc": [ "first", "second", "third" ], + "patch": [{ "op": "replace", "path": "/", "value": "x" }], + "error": "an empty reference token is not a valid array index" + } ] diff --git a/tests/old_utils_tests.c b/tests/old_utils_tests.c index 690dbb583..c19236e67 100644 --- a/tests/old_utils_tests.c +++ b/tests/old_utils_tests.c @@ -85,6 +85,28 @@ static void json_pointer_tests(void) cJSON_Delete(root); } +static void json_pointer_should_reject_empty_array_index(void) +{ + /* An empty reference token is not a valid array index (RFC 6901), so it + * must not be silently accepted as index 0. */ + cJSON *array = cJSON_Parse("[\"first\", \"second\", \"third\"]"); + cJSON *nested = cJSON_Parse("[[1, 2], [3, 4]]"); + + TEST_ASSERT_NOT_NULL(array); + TEST_ASSERT_NOT_NULL(nested); + + /* valid array indices still resolve */ + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(array, "/0"), cJSON_GetArrayItem(array, 0)); + TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(nested, "/0/1"), cJSON_GetArrayItem(cJSON_GetArrayItem(nested, 0), 1)); + + /* an empty array reference token must be rejected, not resolved to index 0 */ + TEST_ASSERT_NULL(cJSONUtils_GetPointer(array, "/")); + TEST_ASSERT_NULL(cJSONUtils_GetPointer(nested, "/0/")); + + cJSON_Delete(array); + cJSON_Delete(nested); +} + static void misc_tests(void) { /* Misc tests */ @@ -216,6 +238,7 @@ int main(void) UNITY_BEGIN(); RUN_TEST(json_pointer_tests); + RUN_TEST(json_pointer_should_reject_empty_array_index); RUN_TEST(misc_tests); RUN_TEST(sort_tests); RUN_TEST(merge_tests);