v1.1.9 — Security review sprint 1+2 (P0 + P1 fixes)
Closes 6 P0 (critical) and 9 P1 (high) findings from the post-v1.1.8 code-review fix list.
P0 — SSRF / leak surface
parseVmessvalidates the JSONaddfield. All other URI parsers ran throughAddressValidator.requirePublicAddress; VMess (base64-encoded JSON) silently accepted127.0.0.1/ private / CGNAT addresses. Port range1..65535is now enforced too.ProfileEditViewModel.isValidAddressdelegates toAddressValidator. Manual save of127.0.0.1,192.168.x,100.64.x, hex/octal IPv4 or IPv4-mapped IPv6 from the Profile Edit screen is now rejected. IPv6 is parsed viaInetAddress.getByName("[…]"), replacing the previous accept-anything-with-a-colon path.ServerPreflightre-validates the resolved IP. Closes a DNS-rebinding window where a hostname could resolve to a private IP between profile parsing and TCP probe.autoSelectAndConnectno longer fires N parallel TCP SYNs from the user's real IP. Uses cachedlastPingMsif any profile has a fresh value; otherwise probes only 3 random candidates sequentially with an early stop on<200ms. Eliminates the burst-of-SYNs fingerprint that ISP / corp DPI uses to flag VPN clients in the auto-select phase.- xray / tun2socks Log.d output is now redacted. Lines pass through
LogBuffer.redactPublicBEFORELog.d, not just inside the in-app Log viewer. ProGuard stripsLog.d/Log.vfrom release builds entirely as defence in depth. - QR scan confirmation dialog. No more silent imports — VPN URIs go through a parser +
MaterialAlertDialog; HTTPS subscriptions show a confirmation with the host preview; HTTP and unrecognised QR content are rejected with a Toast. Mirrors the existing deep-link flow.
P1 — high-priority hardening
LogBuffer.redactrewritten. Coverspassword/passwd/pwd/pass/secret/api_key/token/bearer/authorization(case-insensitive, both=and:, optional quotes), JSON form"key":"value", Realitypbk/sidlong base64, Shadowsocks URL base64 between://and@, hostname/IP in network-error lines (dial/connect to/dns/resolve). Quick-check keywords removed (the-keyword matched almost every line).ArrayDeque.removeFirst()instead ofArrayList.removeAt(0)(O(1) vs O(n)). Real CONFLATED-channel debounce instead of the previous broken throttle flag.SubscriptionRepository.validateUrlis now public. Called BEFORE DB insert inSubscriptionViewModel.addSubscriptionandSettingsViewModel.importConfig. Junk subscription URLs no longer persist and are not retried by the periodicSubscriptionUpdateWorker. Subscription name capped at 256 chars (mirrorsProfileParser.MAX_NAME_LENGTH).ProfileEditViewModel.testConnectionrefuses without VPN. When connected, performs a SOCKS5 CONNECT through the local authenticated bridge and times the handshake. No more direct probe from the user's real IP to the VPN server.HomeFragmentskipsGeoLookup.fetchUserLocation()when tunnel is down. Was leaking real IP toipwho.ison every Home open, and once a week even with a warm cache.- Subscription list masks the URL. Shown as
host/…/abcd(last 4 chars of path). The full token-bearing URL is no longer rendered into aRecyclerViewwhere any screenshot leaks it. SettingsFragment.isValidDnsdelegates toAddressValidator. Covers CGNAT, IPv6, hex/octal IPv4, IPv4-mapped IPv6 — all of which the previous regex missed.- New self-test: "Own SOCKS5 auth". Connects to our own ephemeral SOCKS port and asserts that the no-auth handshake is rejected. Catches future regressions in
XrayConfigGenerator.buildInboundsautomatically. ServiceTesterrefuses without VPN. Returns uniform error results instead of testing youtube.com / openai.com / instagram.com / discord.com from the user's real IP.buildClient()returns null if credentials aren't ready — no direct-connection fallback path remains.- Tapjacking on onboarding. All interactive elements (Back / Next / Grant VPN / Grant Usage Stats / Paste / Import / Skip) carry
android:filterTouchesWhenObscured="true". Onboarding is the highest-stakes UI flow and was the only place the existing tapjacking-protection claim didn't apply.
Install
Sideload NetGuard-v1.1.9.apk (arm64-v8a, 27 MB). Upgrade in place from any 1.1.x — settings and profiles preserved.