13 KiB
13 KiB
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
- Wrong Chrome/Edge WebRTC policy key/value —
fingerprint.py:21,177writes DWORD2which isdefault_public_and_private_interfaces, NOTdisable_non_proxied_udp(DWORD3). Modern Chrome also requires REG_SZWebRtcIPHandling = disable_non_proxied_udp. Audits treat2as success → false green. BrowserSession.stop()kills ALL Firefox on the machine —browser_launcher.py:297-305runstaskkill /F /IM firefox.exewith no PID scope. Operator loses unrelated browser sessions.- Credentials plaintext at rest —
config.py:19-22,signup_prep.py:252-325store proxy auth + signup passwords in JSON.cryptography==48.0.0is in requirements but unused. - Preflight race condition —
app.py:2001-2009uses fixedroot.after(200, _final)which can report "All passed" before async network checks finish. - Preflight IP compare is exact-match only — ignores the VPN-aware
/16logic used in production leak detection (leak_detect.py:24-38). - HTTPS CONNECT failure doesn't stop chain —
service.py:573-580logs warning only; chain stays "healthy" and system proxy remains set. - Pinned manual chain never validates hops at runtime — dead chain retries every 10s forever (
service.py:393-403). 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).- Signup extension runs on all URLs —
signup_extension/manifest.json:13-19uses<all_urls>. - Account auto-saved before signup completes — password saved on browser launch before user registers (
app.py:2147-2174). - GOST exe not re-hashed on reuse — zip is verified but extracted
gost.exeon disk is not re-checked (gost_util.py:61-64). asyncio.run()from worker threads —app.py:1860-1863nested 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 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
Proxy God Audit — 2026-05-22