fix: validate same proxy uri without scheme in wallet rpc (#151)
What changed, and why it matters
This commit fixes a bug where the wallet RPC component treated the same proxy address as different if one version included a scheme prefix like 'socks5://' and the other did not. Previously, this caused an unnecessary error when setting a daemon connection. The fix normalizes proxy URIs before comparing them, so equivalent addresses are recognized as the same. It is a usability and correctness fix rather than a clear-cut security vulnerability.
Review the new isSameProxyUri() helper for edge cases such as invalid URIs, default ports, userinfo, and percent-encoded hostnames. Ensure that ignoring the scheme is safe for all callers, since different proxy schemes (e.g., socks4 vs socks5) may have different semantics even when host/port match. Consider whether the helper should also normalize host case and default ports consistently.
Security signals we found
Proxy URI comparison logic changed from strict string equality to semantic equivalence
New helper parses URIs and ignores scheme, which could mask real scheme differences if a caller relies on scheme-specific behavior
Fixes a reported mismatch (#151) that caused operational errors when configuring daemon connections
Evidence from the diff
The change introduces NetworkUtils.isSameProxyUri(), which compares two proxy URI strings by parsing them, stripping optional IPv6 brackets, and comparing host (case-insensitive) and port while ignoring the scheme. MoneroWalletRpc now uses this helper instead of a raw String.equals() when validating that a daemon connection’s proxy URI matches the proxy URI monero-wallet-rpc was started with. This prevents false-positive mismatches such as ‘socks5://127.0.0.1:9050’ vs ‘127.0.0.1:9050’.
Changed components
src/main/java/monero/common/NetworkUtils.javasrc/main/java/monero/wallet/MoneroWalletRpc.javasrc/test/java/TestNetworkUtils.javaInspect captured patch +44 / −1
diff --git a/src/main/java/monero/common/NetworkUtils.java b/src/main/java/monero/common/NetworkUtils.java
index 2ded09c..66a6211 100644
--- a/src/main/java/monero/common/NetworkUtils.java
+++ b/src/main/java/monero/common/NetworkUtils.java
@@ -132,6 +132,30 @@ public class NetworkUtils {
return formatHost(host) + ":" + port;
}
+ /**
+ * Determine if two proxy URIs refer to the same proxy, ignoring any scheme
+ * (e.g. "socks5://") which may or may not be present on either side. For
+ * example "socks5://127.0.0.1:9050" and "127.0.0.1:9050" are equivalent.
+ *
+ * @param proxyUri1 is the first proxy URI (scheme optional)
+ * @param proxyUri2 is the second proxy URI (scheme optional)
+ * @return true if both are null or refer to the same host and port, false otherwise
+ */
+ public static boolean isSameProxyUri(String proxyUri1, String proxyUri2) {
+ if (proxyUri1 == null) return proxyUri2 == null;
+ if (proxyUri2 == null) return false;
+ if (proxyUri1.equals(proxyUri2)) return true;
+ try {
+ URI uri1 = parseUri(proxyUri1);
+ URI uri2 = parseUri(proxyUri2);
+ String host1 = stripIpv6Brackets(uri1.getHost());
+ String host2 = stripIpv6Brackets(uri2.getHost());
+ return host1 != null && host1.equalsIgnoreCase(host2) && uri1.getPort() == uri2.getPort();
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+
/**
* Strip surrounding brackets from an IPv6 literal, if present.
*
diff --git a/src/main/java/monero/wallet/MoneroWalletRpc.java b/src/main/java/monero/wallet/MoneroWalletRpc.java
index 4294796..3d8974c 100644
--- a/src/main/java/monero/wallet/MoneroWalletRpc.java
+++ b/src/main/java/monero/wallet/MoneroWalletRpc.java
@@ -47,6 +47,7 @@ import monero.common.MoneroError;
import monero.common.MoneroRpcConnection;
import monero.common.MoneroRpcError;
import monero.common.MoneroUtils;
+import monero.common.NetworkUtils;
import monero.common.SslOptions;
import monero.common.TaskLooper;
import monero.daemon.model.MoneroBlock;
@@ -519,7 +520,7 @@ public class MoneroWalletRpc extends MoneroWalletDefault {
if (startupProxyUri != null) throw new MoneroError("Cannot set daemon connection without proxy URI because monero-wallet-rpc was started with a proxy URI: " + startupProxyUri);
} else {
if (startupProxyUri == null) params.put("proxy", connection == null ? "" : connection.getProxyUri());
- else if (!startupProxyUri.equals(connection.getProxyUri())) {
+ else if (!NetworkUtils.isSameProxyUri(startupProxyUri, connection.getProxyUri())) {
throw new MoneroError("Cannot set daemon connection with proxy URI " + connection.getProxyUri() + " because monero-wallet-rpc was started with a different proxy URI: " + startupProxyUri);
}
}
diff --git a/src/test/java/TestNetworkUtils.java b/src/test/java/TestNetworkUtils.java
index d97332d..9aae51a 100644
--- a/src/test/java/TestNetworkUtils.java
+++ b/src/test/java/TestNetworkUtils.java
@@ -94,6 +94,24 @@ public class TestNetworkUtils {
assertEquals("feder8.me:18089", NetworkUtils.formatHostAndPort("feder8.me", 18089));
}
+ @Test
+ public void testIsSameProxyUri() {
+ // identical and null cases
+ assertTrue(NetworkUtils.isSameProxyUri(null, null));
+ assertTrue(NetworkUtils.isSameProxyUri("127.0.0.1:9050", "127.0.0.1:9050"));
+ assertFalse(NetworkUtils.isSameProxyUri("127.0.0.1:9050", null));
+ assertFalse(NetworkUtils.isSameProxyUri(null, "127.0.0.1:9050"));
+ // scheme prefix ignored (the reported socks5:// vs bare mismatch)
+ assertTrue(NetworkUtils.isSameProxyUri("socks5://127.0.0.1:9050", "127.0.0.1:9050"));
+ assertTrue(NetworkUtils.isSameProxyUri("127.0.0.1:9050", "socks5://127.0.0.1:9050"));
+ assertTrue(NetworkUtils.isSameProxyUri("socks5://127.0.0.1:9050", "socks4://127.0.0.1:9050"));
+ // ipv6 with and without scheme
+ assertTrue(NetworkUtils.isSameProxyUri("socks5://[::1]:9050", "[::1]:9050"));
+ // different host or port are not the same
+ assertFalse(NetworkUtils.isSameProxyUri("socks5://127.0.0.1:9050", "127.0.0.1:9051"));
+ assertFalse(NetworkUtils.isSameProxyUri("socks5://127.0.0.1:9050", "socks5://127.0.0.2:9050"));
+ }
+
@Test
public void testIsIpv6Literal() {
assertTrue(NetworkUtils.isIpv6Literal("::1"));
Why this scored 22/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.