Skip to content

Commit ef66e7b

Browse files
committed
MB-68697: Sanitise agent name
The agent name is pushed into a JSON object as part of the connections stat request. HELO must ensure that we don't store an agent name which cannot be represent in JSON. Change-Id: Ibbb375ba84b8af9b1edbf4792dd40894efae3432 Reviewed-on: https://review.couchbase.org/c/kv_engine/+/234361 Reviewed-by: Trond Norbye <[email protected]> Well-Formed: Restriction Checker Tested-by: Jim Walker <[email protected]> Reviewed-by: Mohammad Zaeem <[email protected]>
1 parent af01fe1 commit ef66e7b

File tree

3 files changed

+43
-4
lines changed

3 files changed

+43
-4
lines changed

daemon/protocol/mcbp/hello_packet_executor.cc

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,18 @@ void process_hello_packet_executor(Cookie& cookie) {
223223
if (obj != json.end() && obj->is_string()) {
224224
connection.setAgentName(obj->get<std::string>());
225225
}
226-
} catch (const nlohmann::json::exception&) {
227-
connection.setAgentName(key);
226+
} catch (const nlohmann::json::exception& e) {
227+
// MB-68697: Not valid JSON, sanitise the key as this could be
228+
// invalid UTF-8 which could later crash stat requests.
229+
LOG_INFO("{}: Failed to parse agent name: {}",
230+
connection.getId(),
231+
e.what());
232+
connection.setAgentName(req.getPrintableKey());
228233
}
229234
} else {
230-
connection.setAgentName(key);
235+
// MB-68697: Store a sanitised version of the key as the agent name
236+
// may be invalid UTF-8 which could later crash stat requests.
237+
connection.setAgentName(req.getPrintableKey());
231238
}
232239

233240
log_buffer.append("[");

protocol/mcbp/request.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ std::string Request::getPrintableKey() const {
6161

6262
std::string buffer{reinterpret_cast<const char*>(key.data()), key.size()};
6363
for (auto& ii : buffer) {
64-
if (!std::isgraph(ii)) {
64+
if (!std::isprint(ii)) {
6565
ii = '.';
6666
}
6767
}

tests/testapp/testapp_stats.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,3 +555,35 @@ TEST_P(StatsTest, MB59260) {
555555
EXPECT_EQ(max_system, stats["max_system_connections"]);
556556
EXPECT_EQ(max_user, stats["max_user_connections"]);
557557
}
558+
559+
TEST_P(StatsTest, MB68697_json) {
560+
// Do a RAW hello with a junked JSON string
561+
BinprotHelloCommand cmd("{\"a\":\"8\xf8\"}");
562+
const auto resp = BinprotHelloResponse(adminConnection->execute(cmd));
563+
ASSERT_TRUE(resp.isSuccess());
564+
565+
auto stats = adminConnection->stats("connections");
566+
// We have at _least_ 2 connections
567+
ASSERT_LE(2, stats.size());
568+
569+
stats = adminConnection->stats("connections self");
570+
ASSERT_EQ(1, stats.size());
571+
// See sanitised agent name
572+
EXPECT_EQ(R"({"a":"8."})", stats.front()["agent_name"].get<std::string>());
573+
}
574+
575+
TEST_P(StatsTest, MB68697_raw) {
576+
// Do a RAW hello with a junked string that later cannot be turned into JSON
577+
BinprotHelloCommand cmd("\"a\":\"8\xf8\"");
578+
const auto resp = BinprotHelloResponse(adminConnection->execute(cmd));
579+
ASSERT_TRUE(resp.isSuccess());
580+
581+
auto stats = adminConnection->stats("connections");
582+
// We have at _least_ 2 connections
583+
ASSERT_LE(2, stats.size());
584+
585+
stats = adminConnection->stats("connections self");
586+
ASSERT_EQ(1, stats.size());
587+
// See sanitised agent name
588+
EXPECT_EQ(R"("a":"8.")", stats.front()["agent_name"].get<std::string>());
589+
}

0 commit comments

Comments
 (0)