diff --git a/app/src/main/java/com/tobevpn/app/vpn/VlessUrlParser.kt b/app/src/main/java/com/tobevpn/app/vpn/VlessUrlParser.kt index 508a96d..4e3ef99 100644 --- a/app/src/main/java/com/tobevpn/app/vpn/VlessUrlParser.kt +++ b/app/src/main/java/com/tobevpn/app/vpn/VlessUrlParser.kt @@ -69,7 +69,9 @@ object VlessUrlParser { ?.lowercase(Locale.ROOT) ?: "", serviceName = params["serviceName"] ?: "", - extra = params["extra"] ?: "", + extra = params["extra"] + ?.takeUnless { it.trim() == "null" } + .orEmpty(), mode = params["mode"] ?.trim() ?.lowercase(Locale.ROOT) diff --git a/app/src/test/java/com/tobevpn/app/data/repository/BaseStationBypassProfileParserTest.kt b/app/src/test/java/com/tobevpn/app/data/repository/BaseStationBypassProfileParserTest.kt index 36d6591..b5f42d3 100644 --- a/app/src/test/java/com/tobevpn/app/data/repository/BaseStationBypassProfileParserTest.kt +++ b/app/src/test/java/com/tobevpn/app/data/repository/BaseStationBypassProfileParserTest.kt @@ -40,6 +40,19 @@ class BaseStationBypassProfileParserTest { assertEquals(1, servers.size) } + @Test + fun `xhttp null extra is accepted with the same identity as omitted extra`() { + val link = "vless://same-user@node.example:443?type=xhttp&security=tls" + val withNullExtra = BaseStationBypassProfileParser.parse( + "$link&extra=null#Node", + ).single() + val withoutExtra = BaseStationBypassProfileParser.parse("$link#Node").single() + + assertEquals("", withNullExtra.extra) + assertEquals(withoutExtra.id, withNullExtra.id) + assertEquals(withoutExtra.probeKey, withNullExtra.probeKey) + } + @Test fun `stable id changes with vpn configuration and does not expose uuid`() { val first = BaseStationBypassProfileParser.parse( @@ -124,6 +137,8 @@ class BaseStationBypassProfileParserTest { "vless://tcp-header@six.example:443?type=tcp&headerType=srtp#TCP SRTP", "vless://xhttp-mode@seven.example:443?type=xhttp&mode=invalid#XHTTP mode", "vless://xhttp-extra@eight.example:443?type=xhttp&extra=not-json#XHTTP extra", + "vless://xhttp-array@array.example:443?type=xhttp&extra=%5B%5D#XHTTP array", + "vless://xhttp-string@string.example:443?type=xhttp&extra=%22null%22#XHTTP string", "vless://reality-http@nine.example:443?type=tcp&security=reality" + "&headerType=http#Reality HTTP", ).joinToString("\n"), diff --git a/app/src/test/java/com/tobevpn/app/vpn/VlessUrlParserTest.kt b/app/src/test/java/com/tobevpn/app/vpn/VlessUrlParserTest.kt index 9401c56..d025362 100644 --- a/app/src/test/java/com/tobevpn/app/vpn/VlessUrlParserTest.kt +++ b/app/src/test/java/com/tobevpn/app/vpn/VlessUrlParserTest.kt @@ -63,6 +63,18 @@ class VlessUrlParserTest { assertEquals("{\"noGRPCHeader\":true}", server.extra) } + @Test + fun `treats json null extra as absent options`() { + listOf("null", "%6E%75%6C%6C", "%20null%20").forEach { extra -> + val server = VlessUrlParser.parse( + "vless://user@node.example:443?type=xhttp&extra=$extra#Node", + ) + + requireNotNull(server) + assertEquals("", server.extra) + } + } + @Test fun `normalizes fingerprint for xray lookup`() { val server = VlessUrlParser.parse(