Files
proxy-god/docs/AUDIT_FINDINGS.md
Dr Jones 255fdf3e8c
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
fix: C-01 WebRTC policy value, C-02 scoped taskkill, C-04 preflight race, E-11 socket timeout, E-27 ban test dedup, full audit doc
2026-05-22 00:43:57 -07:00

203 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `<all_urls>`.
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 `<all_urls>` 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 18 hops; UI/config says 28 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-01E-06, E-11, E-22, E-25E-27, M-04M-07, M-11, B-04B-06, T-01T-05, T-09, U-01U-03
**P2 — Backlog:**
All remaining E/M/B/U/T items, installer signing, metrics, coverage gates
---
*Proxy God Audit — 2026-05-22*