From ba03c7b806c650e5af963eb715555ea5e3ec6a8b Mon Sep 17 00:00:00 2001 From: CyberpsychoJacob Date: Wed, 13 May 2026 21:47:41 +0400 Subject: [PATCH 1/2] Fix incorrect ~1 decoding in decode_pointer_inplace The ~1 escape sequence (RFC 6901 JSON Pointer, representing '/') was writing the decoded '/' to decoded_string[1] instead of decoded_string[0], leaving the original '~' in place and producing '~/' instead of '/'. This caused any JSON Patch operation whose path contained a ~1 sequence to silently target the wrong key, making ADD/REMOVE/REPLACE/MOVE/COPY operate on an incorrect node without error. --- cJSON_Utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cJSON_Utils.c b/cJSON_Utils.c index 8b38eb25..402e8b4a 100644 --- a/cJSON_Utils.c +++ b/cJSON_Utils.c @@ -374,7 +374,7 @@ static void decode_pointer_inplace(unsigned char *string) } else if (string[1] == '1') { - decoded_string[1] = '/'; + decoded_string[0] = '/'; } else { From 2fded400f527ee62e754cd9d94c56a2a06a50e62 Mon Sep 17 00:00:00 2001 From: CyberpsychoJacob Date: Wed, 13 May 2026 21:52:28 +0400 Subject: [PATCH 2/2] Add unit test for ~1 JSON Pointer decode fix Verifies that cJSONUtils_ApplyPatches correctly resolves ~1 escape sequences in patch paths to a literal '/' when targeting object keys that contain a forward slash. --- tests/misc_utils_tests.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/misc_utils_tests.c b/tests/misc_utils_tests.c index 7d300bc8..7bf71830 100644 --- a/tests/misc_utils_tests.c +++ b/tests/misc_utils_tests.c @@ -29,6 +29,29 @@ #include "common.h" #include "../cJSON_Utils.h" +static void decode_pointer_inplace_should_decode_tilde_one_to_forward_slash(void) +{ + /* A key whose name contains a literal '/' must be addressed in a JSON + * Pointer using the ~1 escape sequence (RFC 6901). A previous bug wrote + * '/' to decoded_string[1] instead of decoded_string[0], leaving '~' in + * place and producing "~/" instead of "/". */ + cJSON *object = cJSON_Parse("{\"foo/bar\": 1}"); + cJSON *patch = cJSON_Parse("[{\"op\": \"replace\", \"path\": \"/foo~1bar\", \"value\": 2}]"); + cJSON *item = NULL; + + TEST_ASSERT_NOT_NULL(object); + TEST_ASSERT_NOT_NULL(patch); + + TEST_ASSERT_EQUAL_INT(0, cJSONUtils_ApplyPatches(object, patch)); + + item = cJSON_GetObjectItemCaseSensitive(object, "foo/bar"); + TEST_ASSERT_NOT_NULL_MESSAGE(item, "Key \"foo/bar\" not found after patch with ~1 path."); + TEST_ASSERT_EQUAL_DOUBLE(2.0, item->valuedouble); + + cJSON_Delete(object); + cJSON_Delete(patch); +} + static void cjson_utils_functions_shouldnt_crash_with_null_pointers(void) { cJSON *item = cJSON_CreateString("item"); @@ -74,6 +97,7 @@ int main(void) { UNITY_BEGIN(); + RUN_TEST(decode_pointer_inplace_should_decode_tilde_one_to_forward_slash); RUN_TEST(cjson_utils_functions_shouldnt_crash_with_null_pointers); return UNITY_END();