json_patch: remove members whose names contain '/' or '~'

json_pointer_get_internal() decodes the last reference token in a
private copy of the path, then pointed key_in_parent at the same offset
in the caller's path, where the token is still escaped. For a path like
"/a~1b" that gave "a~1b" instead of "a/b", so json_patch's remove,
and the remove step of move, called json_object_object_del() with a key
that doesn't exist. Nothing was removed and the patch still reported
success; a move left the source member in place.

Point key_in_parent at the key as it is stored in the parent object
instead, which is always the decoded name.

Added remove and move cases with escaped member names to
json_patch_tests.json and refreshed test_json_patch.expected.
This commit is contained in:
fhgffy authored and Eric Hawicz committed 2026-10-25 18:19:07 -04:00
1 parent e77ca4fb3d
commit 86fe287188
4 files changed
+26 -3

No files matched your search

+11 -2
View File
@@ -18,6 +18,7 @@
#include "json_object_private.h"
#include "json_pointer.h"
#include "json_pointer_private.h"
#include "linkhash.h"
#include "strdup_compat.h"
#include "vasprintf_compat.h"
@@ -260,9 +261,17 @@ int json_pointer_get_internal(struct json_object *obj, const char *path,
return -1;
}
rc = json_pointer_result_get_recursive(obj, path_copy, res);
/* re-map the path string to the const-path string */
/* key_in_parent points at the decoded reference token in path_copy,
* which is freed below. The same offset in the caller's path is still
* escaped ("a~1b" rather than "a/b"), so use the key as it is stored in
* the parent object instead.
*/
if (rc == 0 && json_object_is_type(res->parent, json_type_object) && res->key_in_parent)
res->key_in_parent = path + (res->key_in_parent - path_copy);
{
struct lh_entry *entry =
lh_table_lookup_entry(json_object_get_object(res->parent), res->key_in_parent);
res->key_in_parent = entry ? (const char *)lh_entry_k(entry) : NULL;
}
free(path_copy);
return rc;