# Proxy God — Audit Findings **Generated:** 2026-05-22 **Tests:** 47/47 pass · compileall clean **Severity summary:** P0: 12 · P1: 28 · P2: 22 · **Total: 62** --- ## Executive Summary | Severity | Count | |----------|------:| | Critical (P0) | 12 | | High (P1) | 28 | | Medium (P2) | 22 | | **Total** | **62** | **Already remediated (prior passes):** CI workflow, GOST SHA256 pin, kill-switch `emergency_disengage` + atexit, settings migration/backup, log rotation, MIT LICENSE, GOST SHA256 verified correct. --- ## Top P0 Issues 1. **Wrong Chrome/Edge WebRTC policy key/value** — `fingerprint.py:21,177` writes DWORD `2` which is `default_public_and_private_interfaces`, NOT `disable_non_proxied_udp` (DWORD `3`). Modern Chrome also requires REG_SZ `WebRtcIPHandling = disable_non_proxied_udp`. Audits treat `2` as success → false green. 2. **`BrowserSession.stop()` kills ALL Firefox on the machine** — `browser_launcher.py:297-305` runs `taskkill /F /IM firefox.exe` with no PID scope. Operator loses unrelated browser sessions. 3. **Credentials plaintext at rest** — `config.py:19-22`, `signup_prep.py:252-325` store proxy auth + signup passwords in JSON. `cryptography==48.0.0` is in requirements but unused. 4. **Preflight race condition** — `app.py:2001-2009` uses fixed `root.after(200, _final)` which can report "All passed" before async network checks finish. 5. **Preflight IP compare is exact-match only** — ignores the VPN-aware `/16` logic used in production leak detection (`leak_detect.py:24-38`). 6. **HTTPS CONNECT failure doesn't stop chain** — `service.py:573-580` logs warning only; chain stays "healthy" and system proxy remains set. 7. **Pinned manual chain never validates hops at runtime** — dead chain retries every 10s forever (`service.py:393-403`). 8. **`emergency_disengage()` runs on ALL exits** — even if kill-switch never engaged; can mask other apps' firewall state (`service.py:105`, `firewall.py:165-177`). 9. **Signup extension runs on all URLs** — `signup_extension/manifest.json:13-19` uses ``. 10. **Account auto-saved before signup completes** — password saved on browser launch before user registers (`app.py:2147-2174`). 11. **GOST exe not re-hashed on reuse** — zip is verified but extracted `gost.exe` on disk is not re-checked (`gost_util.py:61-64`). 12. **`asyncio.run()` from worker threads** — `app.py:1860-1863` nested event loop risk if caller context changes. --- ## 1. Critical Bugs | ID | Finding | File:Line | |----|---------|-----------| | C-01 | WebRTC policy wrong key/type/value | `fingerprint.py:18-21,162-177`, `webrtc_check.py:37-96`, `leak_audit.py:138-157` | | C-02 | Browser stop kills all `firefox.exe` globally | `browser_launcher.py:297-305` | | C-03 | Plaintext `settings.json` / signup JSON credentials | `config.py:19-22`, `signup_prep.py:252-325` | | C-04 | Preflight `root.after(200)` race — final verdict before checks complete | `app.py:2001-2009` | | C-05 | Preflight IP compare exact-match only (ignores /16 VPN logic) | `app.py:1886-1893`, `leak_detect.py:24-38` | | C-06 | HTTPS CONNECT failure keeps chain "healthy" | `service.py:573-580` | | C-07 | Pinned chain never validates hops at runtime | `service.py:393-403` | | C-08 | `emergency_disengage()` fires on ALL exits | `service.py:105`, `firewall.py:165-177` | | C-09 | Signup extension `` permissions | `signup_extension/manifest.json:13-19` | | C-10 | Account saved before signup completes | `app.py:2147-2174` | | C-11 | GOST exe not re-hashed on disk reuse | `gost_util.py:61-64` | | C-12 | `asyncio.run()` from worker threads | `app.py:1860-1863` | --- ## 2. Edge Cases / Failure Modes | ID | Finding | File:Line | |----|---------|-----------| | E-01 | `real_ip` captured once at start; VPN reconnect mid-session not reflected | `service.py:339-340,551-558` | | E-02 | Sticky exit suppresses leak rotation but not manual rotate/GOST death | `service.py:607-619` | | E-03 | Sticky + health leak: status forced "healthy" while leaking | `service.py:610-618` | | E-04 | `is_chain_leak(real_ip=None)` returns False (no leak) — fail-open | `leak_detect.py:32-33` | | E-05 | Same /16 ISP neighbors never flagged — some leak modes missed by design | `leak_detect.py:36-37` | | E-06 | Empty pinned chain + `use_pinned_chain=True` falls through to pool silently | `service.py:393`, `config.py:201` | | E-07 | `chain_length=1` + manual exit only — no mid-hop redundancy | `service.py:407-415` | | E-08 | Obfuscation `random_mix` shuffles list but takes prefix slice — not random per hop | `service.py:641-644,717` | | E-09 | Pool exhausted → reshuffle; blacklisted proxies stay excluded until refresh | `service.py:409-417,768-771` | | E-10 | Fixed exit validation failure still used in chains | `service.py:822-839` | | E-11 | `socket.setdefaulttimeout()` global mutation — thread race in DNS helpers | `dns_leak.py:140-151`, `ban_tester.py:149-150` | | E-12 | DNS leak "proxy path" uses Google DoH through HTTP proxy — not equivalent to system DNS | `dns_leak.py:154-167` | | E-13 | DNS `all_match` logic can false-flag or miss due to CDN/geo DNS answers | `dns_leak.py:221-252` | | E-14 | Kill-switch allows outbound DNS globally — DNS exfil channel exists | `firewall.py:136-140` | | E-15 | Kill-switch skipped when non-admin with only log line | `service.py:350-357` | | E-16 | IPv6 disable skips virtual adapters by name heuristics only | `fingerprint.py:122-137` | | E-17 | Hostname rename may require reboot; partial apply possible | `fingerprint.py:73-103` | | E-18 | MAC spoof restore depends on in-memory originals — crash mid-session may leave spoofed MAC | `service.py:96-97` | | E-19 | `BrowserSession.is_running()` treats any `firefox.exe` within 5 min as "ours" | `browser_launcher.py:153-157` | | E-20 | Auto-relaunch can loop if profile locked/corrupt | `app.py:2866-2871` | | E-21 | `task_exists()` logon task runs LIMITED — no admin kill-switch at boot | `windows_task.py:47-48` | | E-22 | Group Policy proxy locks detected but never remediated | `sysproxy.py:298-317`, `service.py:586-594` | | E-23 | `load_settings` auto-rewrites sanitized file — can surprise operators | `config.py:436-443` | | E-24 | `_is_safe_https_url` allows `http://` sources — MITM on pool fetch | `config.py:266-271`, `fetcher.py:13-16` | | E-25 | Ban tester DNS leak helper checks `x-real-ip` header (usually absent) — nonsense results | `ban_tester.py:169-219` | | E-26 | Ban tester `_looks_banned_body` matches "captcha" on normal pages → false bans | `ban_tester.py:71-89,133-134` | | E-27 | `run_ban_tests(categories=["All"])` duplicates sites | `ban_tester.py:237-243` | | E-28 | Exit intel/geo fetch via third-party APIs through proxy — telemetry surface | `exit_intel.py`, `chain_map.py:152-179` | | E-29 | World map geo resolution uses direct `socket.getaddrinfo` — DNS leak for hop lookup | `chain_map.py:86-112,160` | | E-30 | GOST immediate exit blacklists hops but pinned/manual exit exempt | `service.py:515-522,557-562` | --- ## 3. Missing Features / README Gaps | ID | Finding | Reference | |----|---------|-----------| | M-01 | README "self-healing forever" — pinned mode doesn't rotate pool | `README.md:9`, `service.py:393-403` | | M-02 | README "fail closed" — non-admin + kill-switch skipped | `README.md:25`, `service.py:350-357` | | M-03 | README 1–8 hops; UI/config says 2–8 in places | `README.md:48`, `config.py:167` | | M-04 | No in-GUI emergency kill-switch disengage button | `firewall.py`, `app.py` | | M-05 | `audit_device()` is OS-only but UI shows it under "Browser Fingerprint" | `fingerprint.py:299-383`, `app.py:2310-2313` | | M-06 | Preflight card mentions "fingerprint" but no fingerprint check in `_PF_CHECKS` | `app.py:1775-1793` | | M-07 | `browser_auto_relaunch` not tied to chain health flag | `app.py:2866-2871`, `config.py:205` | | M-08 | No export/import encrypted settings/signup vault | — | | M-09 | No persisted ban-test / audit history | `app.py` | | M-10 | `prefer_elite` can empty pool with no GUI recovery wizard | `config.py:183`, `service.py:754-761` | | M-11 | SOCKS5 remote DNS disabled in Firefox profile | `browser_profile.py:125` | | M-12 | Signup presets have no ToS/disclaimer in UI | `signup_prep.py:37-160` | | M-13 | No IPv6 chain path — IPv6 disable adapter-level only | `fingerprint.py:106-138` | | M-14 | Tray red/yellow/green with no click-through to failed check detail | `tray.py`, `app.py:2841-2844` | | M-15 | `CHANGELOG.md` claims DPAPI/Fernet — not implemented | `CHANGELOG.md:35-36` | --- ## 4. Build / Packaging / Deployment | ID | Finding | Reference | |----|---------|-----------| | B-01 | No Authenticode signing | `ProxyChainManager.spec:70-71` | | B-02 | UPX enabled — AV false positives risk | `ProxyChainManager.spec:66-67` | | B-03 | `uac_admin=True` — every launch elevates | `ProxyChainManager.spec:72` | | B-04 | PyInstaller not pinned in `requirements.txt` | `scripts/setup_and_build.ps1:39` | | B-05 | Build script doesn't emit SHA256 sidecar | `scripts/setup_and_build.ps1:50-58` | | B-06 | GOST not bundled — first run requires GitHub access | `gost_util.py:61-86` | | B-07 | `__version__` static `1.0.0`, not tied to git tag | `__init__.py:3` | | B-08 | CI does not run PyInstaller build | `.github/workflows/test.yml` | | B-09 | No SBOM / `pip freeze` in release artifacts | — | | B-10 | Defender exclusion adds whole `%LOCALAPPDATA%\ProxyChainManager` folder | `gost_util.py:29-45` | --- ## 5. Security / Privacy | ID | Finding | Reference | |----|---------|-----------| | S-01 | Plaintext secrets at rest (proxy creds + signup passwords) | `config.py`, `signup_prep.py` | | S-02 | WebRTC mis-hardening (wrong policy value) | `fingerprint.py` | | S-03 | All httpx clients use `verify=False` — MITM risk | `validator.py:142`, `ban_tester.py:119`, `dns_leak.py:159` | | S-04 | Signup passwords can appear in UI/log via clipboard actions | `app.py:2059-2062` | | S-05 | Signup extension content script on all URLs | `signup_extension/manifest.json` | | S-06 | Public proxy list sources — hostile infrastructure by design | `config.py:218-223` | | S-07 | `cryptography` dependency unused — attack surface without benefit | `requirements.txt:5` | | S-08 | Kill-switch + DNS allow rule = DNS bypass channel | `firewall.py:136-140` | | S-09 | Forensic wipe deletes Recycle Bin without extra confirmation | `artifact_wipe.py:208-216` | | S-10 | No secure deletion of signup JSON on uninstall | — | --- ## 6. UI/UX Issues | ID | Finding | Reference | |----|---------|-----------| | U-01 | Window close minimizes to tray — chain/firewall active silently | `app.py:2908` | | U-02 | "Run as Admin" relaunch closes app — unsaved UI state lost | `app.py:347-350` | | U-03 | No version/build SHA in About UI | `__init__.py:3` | | U-04 | Cookie/persona menus reverse-lookup by label — fragile if labels change | `app.py:1321-1325` | | U-05 | Hardened toggles visible even when blend persona overrides them | `browser_profile.py:42-65` | | U-06 | Preflight "fingerprint" in subtitle but not in checklist | `app.py:1775-1793` | | U-07 | Ban tester has no progress bar for 30+ sites | `ban_tester.py:222-260` | | U-08 | Saved accounts list shows email + password in plain text | `app.py:2046-2049` | | U-09 | Chain map silently skips hops when geo lookup fails — no unknown marker | `chain_map.py:156-166,322-328` | | U-10 | `verbose_logs_var` affects UI handler only; file log stays INFO | `app.py:707-713` | --- ## 7. Test Coverage Gaps | ID | Finding | |----|---------| | T-01 | No tests for `service.py` (rotation, sticky, kill-switch) | | T-02 | No tests for `firewall.py` / `emergency_disengage` | | T-03 | No tests for `sysproxy.py` registry blob encoding | | T-04 | No tests for GOST SHA256 mismatch path | | T-05 | No tests for `dns_leak.run_dns_leak_test` verdict logic | | T-06 | No tests for `webrtc_check` / `apply_webrtc_hardening` | | T-07 | No tests for `BrowserSession` | | T-08 | No tests for `signup_prep.install_signup_extension` | | T-09 | No tests for `config.load_settings`/`migrate`/`sanitize` round-trip | | T-10 | No tests for `ban_tester` heuristics | | T-11 | No GUI/smoke tests for `app.py` (~2900 lines) | | T-12 | `test_fetch_smoke` is network-dependent — can skip silently in CI | | T-13 | No test that WebRTC policy value matches Chrome documentation | | T-14 | No frozen-bundle (`_MEIPASS`) path tests in CI | --- ## 8. Recommended Fix Priority **P0 — Fix immediately:** C-01, C-02, C-03, C-04, C-05, C-06, S-01, S-02, S-03, B-01 **P1 — Next sprint:** C-07, C-10, E-01–E-06, E-11, E-22, E-25–E-27, M-04–M-07, M-11, B-04–B-06, T-01–T-05, T-09, U-01–U-03 **P2 — Backlog:** All remaining E/M/B/U/T items, installer signing, metrics, coverage gates --- ## 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 because they need design decisions, external tooling (certs, infra), or behavior that is not safe to guess. ### Critical / security (P0) | ID | Why left open | Suggested direction | |----|---------------|---------------------| | **C-09** | Narrowing signup extension off `` breaks **custom signup URLs** the operator types in | Dynamic host permissions API or per-session host approval prompt | | **C-12** | `asyncio.run()` in worker threads (`app.py` preflight) needs a dedicated event-loop policy refactor | Move all preflight network ops to a single asyncio runner thread | | **S-03** | `verify=False` on httpx is **intentional** for the broken-cert reality of public proxies | Per-setting toggle with security warning, documented trade-off | | **B-01** | Authenticode signing needs an actual code-signing certificate + CI secrets | Sign `dist\ProxyChainManager.exe` in release pipeline once cert is provisioned | ### High (P1) — design / scope debt | ID | Why left open | |----|---------------| | **E-01** | Refresh `real_ip` when VPN state changes mid-session — needs interval policy | | **E-02–E-05** | Sticky exit / leak semantics need a product rule (rotate vs warn) | | **E-07–E-10** | Pool/pinned/exit edge cases need operator UX | | **E-12–E-13** | DNS leak methodology (DoH vs system DNS) needs spec | | **E-14 / S-08** | Kill-switch DNS allow rule is required for GOST hostname resolution | | **E-16–E-18** | OS adapter / MAC spoof crash recovery | | **E-21** | Boot task LIMITED vs admin kill-switch | | **E-22** | Group Policy proxy-lock remediation (destructive — needs explicit operator consent) | | **E-23** | Auto-rewrite of sanitized settings — needs UI toggle | | **E-28–E-30** | Third-party intel APIs / map geo / GOST blacklist rules | | **M-01–M-03, M-05, M-07–M-14** | README alignment, encrypted vault export, ban history, IPv6 chain path, tray click-through | | **U-01, U-02, U-04, U-05, U-07–U-10** | Tray/minimize UX, admin-relaunch save state, cookie menu keys, password masking | | **T-01–T-14** | Larger test-coverage program | | **B-03, B-06–B-10** | UAC manifest tradeoff, GOST bundling, SBOM, Defender scope | ### Medium (P2) Installer polish, metrics, coverage gates, IPv6 chain path, SOCKS5 remote DNS policy (`browser_profile.py:125` — likely intentional for Firefox+GOST). --- ## 11. Remediation log — 2026-05-22 (round 2) | ID | Status | Notes | |----|--------|-------| | **C-03 / S-01** | **Fixed** | `secrets_store.py` (Windows DPAPI via ctypes — no new deps). Signup passwords + draft passwords now encrypted on disk; legacy plaintext migrates automatically on first save | | **C-07** | **Fixed** | New `validate_pinned_on_start` setting + `_probe_tcp` helper in `service.py`; dead pinned hops logged but used | | **C-11** | **Fixed** | `gost_util.py` writes `gost.exe.sha256` sidecar on extract and re-verifies on every reuse; mismatch triggers re-download | | **E-15** | **Fixed** | `app.py` `_start()` confirms with operator before starting chain without admin when kill-switch is enabled | | **E-19** | **Fixed** | `browser_launcher.py` `is_running()` walks the spawned PID's descendants via WMIC instead of pattern-matching any `firefox.exe` | | **E-20** | **Fixed** | `max_browser_relaunches` setting + counter in `app.py`; auto-relaunch self-disables after N failures | | **E-25** | **Fixed** | Dead `ban_tester.check_dns_leak()` removed (use `dns_leak.run_dns_leak_test`) | | **M-04** | **Fixed** | Settings tab now has “⚠ Emergency disengage firewall now” button with confirm | | **U-03** | **Fixed** | Window title now reads `Proxy God v{__version__}` | | **B-04** | **Fixed** | New `dev-requirements.txt` pins `pyinstaller==6.10.0`; build script installs it | | **B-05** | **Fixed** | Build script emits `ProxyChainManager.exe.sha256` next to the exe and copies it to Desktop | **Test suite:** 47 → **53 passing** (added 6 round-trip tests for `secrets_store`). --- *Proxy God Audit — last updated 2026-05-22 (round 2)*