fix: audit pass — WebRTC audit, HTTPS gate, leak compare, kill-switch guard, doc backlog
Some checks failed
CI / Test Python 3.10 (push) Has been cancelled
CI / Test Python 3.11 (push) Has been cancelled
CI / Test Python 3.12 (push) Has been cancelled

This commit is contained in:
Dr Jones
2026-05-22 18:00:45 -07:00
parent 255fdf3e8c
commit 04d486a335
9 changed files with 164 additions and 31 deletions

View File

@@ -33,7 +33,7 @@ All meaningful changes to this repository should be recorded here.
discover` on Python 3.10 / 3.11 / 3.12 on `windows-latest`.
- **LICENSE**: MIT license added to repo root.
- **requirements.txt**: All four runtime dependencies pinned to exact versions; `cryptography`
added for future Fernet-based secret storage.
listed for optional future credential encryption (not yet wired).
- **`.gitignore`**: Added `settings.json`, `*.json` credential files, and screenshot noise.
- **`--version` flag**: `run.py` now supports `--version` / `-V`.

View File

@@ -60,7 +60,7 @@ exe = EXE(
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx=False,
upx_exclude=[],
runtime_tmpdir=None,
console=False,

View File

@@ -199,4 +199,74 @@ All remaining E/M/B/U/T items, installer signing, metrics, coverage gates
---
*Proxy God Audit — 2026-05-22*
## 9. Remediation log (2026-05-22 passes)
| ID | Status | Notes |
|----|--------|-------|
| C-01 | **Fixed** | `fingerprint.py`, `webrtc_check.py` — DWORD `3` + `WebRtcIPHandling` REG_SZ |
| C-02 | **Fixed** | `browser_launcher.py` — scoped `taskkill /PID` |
| C-04 | **Fixed** | `app.py``_final` scheduled after all `after(0)` callbacks |
| C-05 | **Fixed** | `app.py` preflight uses `is_chain_leak()` + VPN-aware `/16` |
| C-06 | **Fixed** | `service.py` — HTTPS CONNECT failure rotates chain (not healthy) |
| C-08 | **Fixed** | `firewall.py``emergency_disengage()` no-op unless `is_engaged()` |
| C-10 | **Fixed** | `app.py` — auto-save on launch is `pending` with empty password |
| E-06 | **Fixed** | `service.py` — log when pinned chain enabled but empty |
| E-11 | **Fixed** | `dns_leak.py`, `ban_tester.py` — restore socket timeout in `finally` |
| E-24 | **Fixed** | `config.py` — pool sources must be `https://` only |
| E-26 | **Fixed** | `ban_tester.py` — removed bare `"captcha"` hint; stricter phrases |
| E-27 | **Fixed** | `ban_tester.py` — dedupe by URL |
| M-06 / U-06 | **Fixed** | Preflight subtitle no longer claims “fingerprint” check |
| M-15 | **Fixed** | `CHANGELOG.md` — Fernet/DPAPI not implemented (clarified) |
| B-02 | **Fixed** | `ProxyChainManager.spec``upx=False` |
| leak_audit | **Fixed** | `_check_webrtc_policy()` accepts DWORD `3` or REG_SZ |
| (prior) | **Fixed** | CI, GOST zip SHA256, settings migration/backup, LICENSE, log rotation |
---
## 10. Still needs to be fixed
Items below were **not** changed in this pass because they need design decisions, external tooling, or behavior that is not safe to guess.
### Critical / security (P0)
| ID | Why left open | Suggested direction |
|----|---------------|---------------------|
| **C-03 / S-01** | Encrypting `settings.json` / signup JSON requires key management (DPAPI vs Fernet passphrase) and migration for existing installs | Windows DPAPI via `cryptography` or OS credential store; one-time migration on load |
| **C-07** | Pinned chains need per-hop health probes without breaking intentional manual order | Optional “validate pinned hops on start” toggle + blacklist dead hops only |
| **C-09** | Narrowing signup extension off `<all_urls>` breaks **custom signup URLs** | Dynamic host permissions or user-approved host list per session |
| **C-11** | No pinned SHA256 for extracted `gost.exe` in repo | Add `GOST_EXE_SHA256` constant from official release manifest |
| **C-12** | `asyncio.run()` in worker threads (`app.py` preflight) — refactor needs dedicated event-loop policy | Run network checks via `asyncio.new_event_loop()` in thread or shared async runner |
| **S-03** | `verify=False` on httpx is intentional for broken proxy TLS; enabling verify breaks many public proxies | Per-setting toggle; document risk |
| **B-01** | Authenticode signing needs cert + build pipeline | Sign `dist\ProxyChainManager.exe` in release script |
### High (P1)
| ID | Why left open |
|----|---------------|
| **E-01** | Refresh `real_ip` when VPN state changes mid-session |
| **E-02E-03** | Sticky exit + leak interaction needs product rule (rotate vs warn) |
| **E-04** | `is_chain_leak(real_ip=None)` fail-open — changing affects rotation semantics |
| **E-05** | /16 neighbor policy is intentional without VPN |
| **E-07E-10** | Pool/pinned/exit edge cases need operator UX, not one-line fixes |
| **E-12E-13** | DNS leak test methodology (DoH vs system DNS) needs spec |
| **E-14 / S-08** | Kill-switch DNS allow rule required for GOST hostname resolution |
| **E-15** | Non-admin kill-switch skip — document or block Start without admin |
| **E-16E-18** | OS adapter/MAC spoof recovery after crash |
| **E-19E-20** | Firefox PID tracking / relaunch loop limits |
| **E-21** | Boot task LIMITED vs admin kill-switch |
| **E-22** | GP proxy lock remediation (destructive) |
| **E-23** | Auto-rewrite sanitized settings — add “dont auto-save” option |
| **E-25** | `ban_tester.check_dns_leak()` uses useless `x-real-ip` header — **dead code**, unused by GUI; remove or replace with `dns_leak.run_dns_leak_test` |
| **E-28E-30** | Third-party intel APIs / map geo / GOST blacklist rules |
| **M-01M-14** | README alignment, GUI kill-switch button, encrypted vault, ban history, etc. |
| **U-01U-10** | Tray/minimize UX, admin relaunch save state, About version, cookie menu keys, password masking in list |
| **T-01T-14** | Test coverage expansion |
| **B-03B-10** | UAC manifest, PyInstaller pin, SHA256 sidecar, bundle GOST, SBOM, Defender scope |
### Medium (P2)
Installer polish, metrics, coverage gates, IPv6 chain path, SOCKS5 remote DNS policy (`browser_profile.py:125` — may be intentional for Firefox+GOST).
---
*Proxy God Audit — last updated 2026-05-22*

View File

@@ -1773,7 +1773,7 @@ def _main_inner() -> None:
preflight_card = _signup_card(
"Pre-flight check",
"Exhaustive readiness check: chain, exit IP, geo/ASN, DNS leak, WebRTC, "
"IPv6, fingerprint, target site reachability, and direct-IP comparison.",
"IPv6, HTTPS tunnel, target site reachability, and direct-IP comparison.",
)
pf_rows: dict[str, dict[str, ctk.StringVar | ctk.CTkLabel]] = {}
@@ -1853,8 +1853,11 @@ def _main_inner() -> None:
target_host = urlparse(url).hostname or "(unknown)"
def work() -> None:
from .leak_detect import is_chain_leak, leak_reason
timeout = min(15.0, max(8.0, float(svc.settings.validation_timeout_seconds) + 2.0))
warns: list[str] = []
vpn_active = detect_vpn().active
# ── 1. Direct IP (bypass chain) ──────────────────────────────────
from .validator import get_direct_ip as _get_direct_ip
@@ -1882,12 +1885,12 @@ def _main_inner() -> None:
_pf_set("exit_ip", "ok", ip_)
else:
_pf_set("exit_ip", "warn", "no exit IP yet")
# IP leak compare
# IP leak compare (same logic as production leak_detect)
if direct_ip and ip_ and ip_ not in ("", ""):
if direct_ip == ip_:
_pf_set("leak_compare", "fail",
f"exit == direct ({ip_}) — chain not working!")
warns.append("exit IP matches direct IP (chain not routing)")
if is_chain_leak(ip_, direct_ip, vpn_active):
reason = leak_reason(ip_, direct_ip, vpn_active)
_pf_set("leak_compare", "fail", reason[:80])
warns.append(f"IP leak: {reason}")
else:
_pf_set("leak_compare", "ok",
f"exit {ip_} ≠ direct {direct_ip}")
@@ -2148,12 +2151,38 @@ def _main_inner() -> None:
tag = f" [{' · '.join(extras)}]" if extras else ""
signup_status_var.set(f"Opened {label}{tag} — autofill active (you submit + captcha).")
_log(f"Signup prep: opened {url} with autofill extension{tag}.")
# Auto-save entry so the account is recorded even if user forgets to save manually
_save_signup_account()
# Record draft metadata only — password saved when user clicks Save Account
_save_signup_account_draft_only()
else:
signup_status_var.set("Browser launch failed.")
svc.set_sticky(0)
def _save_signup_account_draft_only() -> None:
"""After browser open: save email/exit metadata without password until user confirms."""
draft = _persist_signup_draft()
url = resolve_signup_url(draft)
exit_ip, exit_hop = _current_exit_meta()
preset = SIGNUP_PRESETS.get(draft.site_key)
site_label = preset.label if preset else "Custom"
rec = AccountRecord(
id=datetime.now().strftime("%Y%m%d%H%M%S%f"),
site=site_label,
url=url,
email=draft.email,
password="",
username=draft.username,
first_name=draft.first_name,
last_name=draft.last_name,
exit_ip=exit_ip,
exit_hop=exit_hop,
notes=draft.notes,
status="pending",
created_at=datetime.now().isoformat(timespec="seconds"),
)
append_account(rec)
_refresh_accounts_ui()
_log(f"Signup prep: draft account {draft.email or draft.username} ({site_label}) — save password after signup.")
def _save_signup_account() -> None:
draft = _persist_signup_draft()
url = resolve_signup_url(draft)

View File

@@ -75,17 +75,18 @@ _BANNED_TEXT_HINTS = (
"temporarily blocked",
"request blocked",
"unusual traffic",
"captcha",
"challenge required",
"bot detected",
"suspicious activity",
"geo-blocked",
"not available in your region",
"your ip",
"ip address",
"cloudflare ray",
"ddos protection",
"attention required",
"complete the captcha",
"captcha required",
"g-recaptcha",
"hcaptcha",
)

View File

@@ -267,7 +267,7 @@ def _is_safe_https_url(url: str) -> bool:
"""Return True for HTTPS URLs pointing at public hosts (not RFC-1918/link-local)."""
try:
p = urlparse(url)
if p.scheme not in ("http", "https"):
if p.scheme != "https":
return False
host = p.hostname or ""
if not host:

View File

@@ -168,7 +168,12 @@ def emergency_disengage() -> None:
Unlike ``disengage()``, this never raises and does not require prior
admin check — it simply tries, logs the outcome, and returns. Safe to
call from atexit or signal handlers where exceptions must not propagate.
No-op when our rules are not present (avoids touching firewall policy on
every process exit).
"""
if not is_engaged():
return
try:
_set_outbound_policy("blockinbound,allowoutbound")
_delete_rules()

View File

@@ -141,14 +141,29 @@ def _check_webrtc_policy() -> tuple[bool, str]:
r"SOFTWARE\Policies\Google\Chrome",
r"SOFTWARE\Policies\Microsoft\Edge",
)
_DWORD_DISABLE = 3 # disable_non_proxied_udp
_STR_KEY = "WebRtcIPHandling"
_STR_DISABLE = "disable_non_proxied_udp"
found = []
for p in paths:
try:
with winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE, p, 0, winreg.KEY_QUERY_VALUE
) as key:
v = int(winreg.QueryValueEx(key, "DefaultWebRtcIpHandlingPolicy")[0])
if v == 2:
ok = False
try:
v = int(winreg.QueryValueEx(key, "DefaultWebRtcIpHandlingPolicy")[0])
if v == _DWORD_DISABLE:
ok = True
except OSError:
pass
try:
v_str = str(winreg.QueryValueEx(key, _STR_KEY)[0]).strip().lower()
if v_str == _STR_DISABLE:
ok = True
except OSError:
pass
if ok:
found.append(p.split("\\")[-1])
except OSError:
continue

View File

@@ -390,6 +390,14 @@ class ChainService:
)
# Pinned (manual) chain mode — skip pool management
if self._settings.use_pinned_chain and not self._settings.pinned_chain:
self._notify({
"type": "log",
"text": (
"Pinned chain enabled but list is empty — add hops in Chain Builder "
"or disable 'Use pinned chain'."
),
})
if self._settings.use_pinned_chain and self._settings.pinned_chain:
chain = list(self._settings.pinned_chain)
rotation_num += 1
@@ -564,22 +572,27 @@ class ChainService:
self._proc = None
return False
# HTTPS CONNECT must work before the chain is marked healthy.
https_ok, https_msg = await check_https_tunnel(local_proxy, timeout)
if not https_ok:
self._notify({"type": "hops", "hops": chain, "status": "dead", "exit_ip": exit_ip})
self._notify({"type": "log", "text": (
"HTTPS tunnel FAILED — chain forwards plain HTTP but refuses CONNECT. "
"Browsers will time out on HTTPS sites. Rotating. " + https_msg
)})
fixed = self._manual_exit_url()
for h in chain:
if fixed and h == fixed:
continue
self._blacklist.add(h)
self._available = [x for x in self._available if x != h]
terminate_process(self._proc)
self._proc = None
return False
self._notify({"type": "hops", "hops": chain, "status": "healthy", "exit_ip": exit_ip})
self._notify({"type": "log", "text": f"✓ Chain healthy — Exit IP: {exit_ip}"})
# HTTPS-tunnel probe: a chain can pass HTTP IP check but refuse CONNECT.
# Without this warning, "all proxies green" yet "every browser broken"
# is a black-box failure for the user.
https_ok, https_msg = await check_https_tunnel(local_proxy, timeout)
if https_ok:
self._notify({"type": "log", "text": f"✓ HTTPS tunnel OK — {https_msg}"})
else:
self._notify({"type": "log", "text": (
"⚠ HTTPS tunnel FAILED — chain forwards plain HTTP but refuses "
"CONNECT. Browsers will time out on every HTTPS page (i.e. "
"every site). Replace the proxies that don't support CONNECT, "
"or use a SOCKS5 / paid HTTPS-capable exit. " + https_msg
)})
self._notify({"type": "log", "text": f"✓ HTTPS tunnel OK — {https_msg}"})
self._notify({"type": "phase", "phase": "running"})
# Pre-flight: surface Group Policy locks (they will override us).