fix(webrtc): ship the validated proxy realness config + CI guards

Audit follow-up (2026-06-10), all validated before commit.

#2 WebRTC — the shipped baseline now MATCHES the manually-validated config
(behind a residential proxy: host=<uuid>.local, srflx=proxy egress, No-Leak,
gathering completes, indistinguishable from vanilla Firefox on BrowserLeaks +
CreepJS):
  - prefs baseline obfuscate_host_addresses False->True; add
    zoom.stealth.webrtc.disable_ipv6=True; drop the dead
    media.peerconnection.ice.disableIPv6 (no-op on FF150)
  - launcher auto-derives the proxy egress IP via _geo.prepare_session_geo
    (one round-trip shared with the timezone resolution) and feeds nICEr via
    STEALTHFOX_WEBRTC_PUBLIC_IP + STEALTHFOX_WEBRTC_DISABLE_IPV6 in _build_env
    (sync + async); an explicit caller env still wins. The C++ mechanisms were
    already in firefox-9 — this activates them, no rebuild.

#1 drop orphan prefs zoom.stealth.timezone + zoom.stealth.seed (read by no C++;
   the live ones are juggler.timezone.override + zoom.stealth.fpp.hw_seed).

#3 release title 'rev N' instead of 'rev firefox-N'.

CI guards (unit, leak-safe — no real proxy/creds, the kind that would have
caught this gap at zero cost):
  - shipped-baseline guard + no-orphan-prefs (test_webrtc_realness.py)
  - egress auto-derive in _build_env (test_launcher_helpers.py)
  - prepare_session_geo returns (tz, egress) (test_geo.py)
CI keeps faking 'behind a proxy' with an in-process TCP-only SOCKS5 + RFC 5737
TEST-NET IPs; real-proxy residential realness stays a LOCAL manual gate.

449 unit pass.
This commit is contained in:
feder-cr
2026-06-10 14:30:16 +02:00
parent 584ad97179
commit e524695088
10 changed files with 249 additions and 51 deletions
+37
View File
@@ -16,6 +16,7 @@ from invisible_playwright._geo import (
_proxy_is_set,
discover_egress_ip,
ip_to_timezone,
prepare_session_geo,
resolve_session_timezone,
)
@@ -286,3 +287,39 @@ def test_resolve_proxy_failure_raises(monkeypatch):
resolve_session_timezone("auto", SOCKS)
with pytest.raises(GeoTimezoneError):
resolve_session_timezone("", SOCKS)
# ──────────────────────────────────────────────────────────────────────
# prepare_session_geo — one round-trip for BOTH timezone + the WebRTC
# egress IP. The egress feeds the srflx override (only behind a proxy).
# ──────────────────────────────────────────────────────────────────────
@pytest.mark.unit
def test_prepare_geo_egress_present_behind_proxy(stub_egress):
geo = prepare_session_geo("auto", SOCKS)
assert geo.timezone == "America/New_York"
assert geo.egress_ip == "203.0.113.7" # discovered for WebRTC
assert stub_egress["proxy_arg"] == SOCKS
@pytest.mark.unit
def test_prepare_geo_egress_present_even_with_explicit_tz(stub_egress):
# explicit IANA zone still needs the egress for WebRTC behind a proxy.
geo = prepare_session_geo("Asia/Tokyo", SOCKS)
assert geo.timezone == "Asia/Tokyo"
assert geo.egress_ip == "203.0.113.7"
assert stub_egress["called"] is True
@pytest.mark.unit
def test_prepare_geo_no_egress_without_proxy(stub_egress):
# no proxy → no WebRTC override (real STUN already tells the truth).
geo = prepare_session_geo("auto", None)
assert geo.timezone == "America/New_York"
assert geo.egress_ip is None
@pytest.mark.unit
def test_prepare_geo_timezone_matches_resolve_session_timezone(stub_egress):
# the thin tz wrapper must stay equivalent to prepare_session_geo().timezone
for tz, proxy in [("Asia/Tokyo", SOCKS), ("auto", HTTP), ("", None)]:
assert prepare_session_geo(tz, proxy).timezone == resolve_session_timezone(tz, proxy)