wallet2: validate public node addresses before display
What changed, and why it matters
This commit adds validation and formatting fixes for public Monero node addresses shown in the wallet. It prevents invalid or malformed node entries (such as those with port 0 or unparseable hostnames) from being displayed, and it correctly wraps IPv6 addresses in brackets so they are not misread. The change reduces the chance that a user connects to or trusts a bogus node listed among public nodes, but it does not by itself stop an attacker from listing a malicious node—only from listing one with an obviously invalid address.
Treat as a defensive hardening patch. Review whether the daemon-side public node registration also validates host/port before accepting entries, since this wallet-side filter only protects the user interface and does not prevent invalid nodes from being propagated. Consider adding tests for malformed hosts, port 0, and IPv6 literals.
Security signals we found
Input validation added for externally supplied node host/port data
IPv6 address formatting corrected to RFC 5952 bracketed notation
Underflow guard added for last_seen timestamp subtraction
Normalization of host string to canonical parsed form
Evidence from the diff
wallet2::get_public_nodes() now filters the public node list returned by the daemon. It drops entries whose rpc_port is 0, whose host cannot be parsed by net::get_network_address(), or whose host does not round-trip to the parsed address string. For IPv6 literals it reconstructs a network_address from boost::asio::ip::make_address_v6 and normalizes node.host to address->host_str(). simplewallet’s public_nodes display wraps IPv6 hosts in square brackets and guards against negative durations in last_seen by clamping node.last_seen to now. These are hardening/display fixes rather than a full trust-boundary fix.
Changed components
src/wallet/wallet2.cppsrc/simplewallet/simplewallet.cppwallet2::get_public_nodes()simple_wallet::public_nodes()Inspect captured patch +20 / −3
### src/simplewallet/simplewallet.cpp
@@ -2107,8 +2107,9 @@ bool simple_wallet::public_nodes(const std::vector<std::string> &args)
message_writer() << boost::format("%32s %16s") % tr("address") % tr("last_seen");
for (const auto &node: nodes)
{
- const std::string last_seen = node.last_seen == 0 ? tr("never") : tools::get_human_readable_timespan(std::chrono::seconds(now - node.last_seen));
- std::string host = node.host + ":" + std::to_string(node.rpc_port);
+ const std::string last_seen = node.last_seen == 0 ? tr("never") : tools::get_human_readable_timespan(std::chrono::seconds(now - std::min(now, node.last_seen)));
+ const std::string host = (node.host.find(':') == std::string::npos ? node.host : "[" + node.host + "]")
+ + ":" + std::to_string(node.rpc_port);
message_writer() << boost::format("%32s %16s") % host % last_seen;
}
}
### src/wallet/wallet2.cpp
@@ -15587,7 +15587,23 @@ std::vector<cryptonote::public_node> wallet2::get_public_nodes(bool white_only)
nodes = res.white;
nodes.reserve(nodes.size() + res.gray.size());
std::copy(res.gray.begin(), res.gray.end(), std::back_inserter(nodes));
- return nodes;
+ std::vector<cryptonote::public_node> valid_nodes;
+ valid_nodes.reserve(nodes.size());
+ for (auto &node: nodes)
+ {
+ if (node.rpc_port == 0)
+ continue;
+ auto address = net::get_network_address(node.host, node.rpc_port);
+ boost::system::error_code ec;
+ const auto ipv6 = boost::asio::ip::make_address_v6(node.host, ec);
+ if (!ec)
+ address = epee::net_utils::network_address{epee::net_utils::ipv6_network_address{ipv6, node.rpc_port}};
+ if (!address || (node.host != address->host_str() && node.host != address->str()))
+ continue;
+ node.host = address->host_str();
+ valid_nodes.push_back(std::move(node));
+ }
+ return valid_nodes;
}
//----------------------------------------------------------------------------------------------------
std::pair<size_t, uint64_t> wallet2::estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size)Why this scored 38/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.