From 44f6d8a5bce541c3327416a5bace66093faa5672 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 27 Oct 2026 17:17:55 +0100 Subject: [PATCH] Verify partial node reads and reject malformed address rows --- tools/chatgui/src/nodespage.cpp | 4 ++-- tools/chatgui/tests/test_nodes_page.cpp | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/tools/chatgui/src/nodespage.cpp b/tools/chatgui/src/nodespage.cpp index 90d02785..eab92f79 100644 --- a/tools/chatgui/src/nodespage.cpp +++ b/tools/chatgui/src/nodespage.cpp @@ -131,13 +131,13 @@ void NodesPage::refreshNodes() { const char* stage = "prepare"; QString failure; sqlite3_stmt* st = nullptr; + DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Nodes refresh begin db=%p", static_cast(d)); int rc = sqlite3_prepare_v2(d, "SELECT n.node_id,n.name,n.x25519_pubkey,n.ed25519_pubkey,n.last_seen_at,n.created_at," " a.node_id,a.family,a.protocol,a.address,a.port,a.rtt,a.nat_mapping,a.nat_inbound,a.nat_source" " FROM nodes n LEFT JOIN node_addresses a ON a.node_id=n.node_id ORDER BY n.node_id,a.family,a.port", -1, &st, nullptr); std::unique_ptr statement(st, sqlite3_finalize); - DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Nodes refresh begin db=%p", static_cast(d)); if (rc == SQLITE_OK) { stage = "step"; while ((rc = sqlite3_step(st)) == SQLITE_ROW) { @@ -187,7 +187,7 @@ void NodesPage::refreshNodes() { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Nodes refresh failed stage=%s rc=%d finalize=%d node=%016llx: %s", stage, rc, finalized, static_cast(currentNode), qPrintable(failure)); m_table->setRowCount(0); - m_dumpText->setPlainText(QCoreApplication::translate("ChatGui", "Query error") + ": " + failure); + m_dumpText->setPlainText(QCoreApplication::translate("ChatGui", "Query error")); return; } diff --git a/tools/chatgui/tests/test_nodes_page.cpp b/tools/chatgui/tests/test_nodes_page.cpp index fab64ca2..5e1cb30d 100644 --- a/tools/chatgui/tests/test_nodes_page.cpp +++ b/tools/chatgui/tests/test_nodes_page.cpp @@ -122,14 +122,29 @@ private slots: }, &state), SQLITE_OK); sqlite3_progress_handler(db.handle, 1, [](void* context) { auto* state = static_cast(context); - if (state->afterRow && !state->rows) return 0; + if (state->afterRow && state->rows < 2) return 0; state->interrupted = true; return 1; }, &state); - page.refreshNodes(); QVERIFY(state.interrupted); if (afterRow) QVERIFY(state.rows > 0); + page.refreshNodes(); QVERIFY(state.interrupted); if (afterRow) QVERIFY(state.rows >= 2); checkFailure(page); QVERIFY(!sqlite3_next_stmt(db.handle, nullptr)); sqlite3_progress_handler(db.handle, 0, nullptr, nullptr); sqlite3_trace_v2(db.handle, 0, nullptr, nullptr); page.refreshNodes(); checkSnapshot(page); } + void invalidAddress_data() { + QTest::addColumn("update"); + QTest::newRow("unknown-family") << QByteArray("family=7"); + QTest::newRow("empty-blob") << QByteArray("address=x''"); + QTest::newRow("text-instead-of-blob") << QByteArray("address='addr'"); + } + void invalidAddress() { + QFETCH(QByteArray, update); + NodesDatabase db; QVERIFY(db.open()); NodesPage page(&db.manager); + page.refreshNodes(); checkSnapshot(page); + QVERIFY(db.execute(("UPDATE node_addresses SET " + update + " WHERE family=4").constData())); + page.refreshNodes(); checkFailure(page); QVERIFY(!sqlite3_next_stmt(db.handle, nullptr)); + QVERIFY(db.execute("UPDATE node_addresses SET family=4,address=x'7F000001' WHERE protocol=1")); + page.refreshNodes(); checkSnapshot(page); + } void disconnectedDatabase() { NodesDatabase db; QVERIFY(db.open()); NodesPage page(&db.manager); page.refreshNodes(); checkSnapshot(page); db.manager.setDb(nullptr); page.refreshNodes();