From 41f8d3cf92e2615aa7c3f07f32f108639ef0b0e8 Mon Sep 17 00:00:00 2001 From: fan wu <336798967+FanWu-ai@users.noreply.github.com> Date: Sun, 4 Oct 2026 23:30:39 -0700 Subject: [PATCH] Fix removeMember truncating string keys at embedded NULs --- src/lib_json/json_value.cpp | 8 +++++++- src/test_lib_json/main.cpp | 41 +++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp index 1725f771b..feb237118 100644 --- a/src/lib_json/json_value.cpp +++ b/src/lib_json/json_value.cpp @@ -1307,7 +1307,13 @@ void Value::removeMember(const char* key) { CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication); value_.map_->erase(actualKey); } -void Value::removeMember(const String& key) { removeMember(key.c_str()); } +void Value::removeMember(const String& key) { + JSON_ASSERT_MESSAGE( + type() == nullValue || type() == objectValue, + "in Json::Value::removeMember(): requires objectValue, but found " + << valueTypeToString(type())); + removeMember(key, nullptr); +} bool Value::removeIndex(ArrayIndex index, Value* removed) { if (type() != arrayValue) { diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index e09b87b84..bfb1043c1 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -1933,6 +1933,9 @@ JSONTEST_FIXTURE_LOCAL(ValueTest, typeChecksThrowExceptions) { JSONTEST_ASSERT_THROWS(intVal.removeMember("test")); JSONTEST_ASSERT_THROWS(strVal.removeMember("test")); JSONTEST_ASSERT_THROWS(arrVal.removeMember("test")); + JSONTEST_ASSERT_THROWS(intVal.removeMember(Json::String("test"))); + JSONTEST_ASSERT_THROWS(strVal.removeMember(Json::String("test"))); + JSONTEST_ASSERT_THROWS(arrVal.removeMember(Json::String("test"))); JSONTEST_ASSERT_THROWS(intVal.getMemberNames()); JSONTEST_ASSERT_THROWS(strVal.getMemberNames()); @@ -2147,6 +2150,44 @@ JSONTEST_FIXTURE_LOCAL(ValueTest, zeroesInKeys) { } } +JSONTEST_FIXTURE_LOCAL(ValueTest, removeMemberWithEmbeddedNull) { + const Json::String keys[] = {Json::String("\0ab", 3), Json::String("a\0b", 3), + Json::String("ab\0", 3)}; + for (const auto& key : keys) { + const Json::String prefix(key.c_str()); + Json::Value root; + root[prefix] = "keep"; + root[key] = "remove"; + + root.removeMember(key); + JSONTEST_ASSERT(!root.isMember(key)); + JSONTEST_ASSERT(root.isMember(prefix)); + JSONTEST_ASSERT_STRING_EQUAL("keep", root.get(prefix, "").asString()); + JSONTEST_ASSERT_EQUAL(1U, root.size()); + + root.removeMember(key); + JSONTEST_ASSERT(root.isMember(prefix)); + JSONTEST_ASSERT_EQUAL(1U, root.size()); + } +} + +JSONTEST_FIXTURE_LOCAL(ValueTest, removeMemberStringOnNullAndEmptyKey) { + Json::Value root; + root.removeMember("missing"); + JSONTEST_ASSERT(root.isNull()); + root.removeMember(Json::String("a\0b", 3)); + JSONTEST_ASSERT(root.isNull()); + + root[""] = "remove"; + root.removeMember(Json::String()); + JSONTEST_ASSERT(root.isObject()); + JSONTEST_ASSERT(root.empty()); + + root["ordinary"] = "remove"; + root.removeMember("ordinary"); + JSONTEST_ASSERT(root.empty()); +} + JSONTEST_FIXTURE_LOCAL(ValueTest, specialFloats) { Json::StreamWriterBuilder b; b.settings_["useSpecialFloats"] = true;