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,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.
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.
- 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.
- Preflight race condition —
app.py:2001-2009 uses fixed root.after(200, _final) which can report "All passed" before async network checks finish.
- Preflight IP compare is exact-match only — ignores the VPN-aware
/16 logic used in production leak detection (leak_detect.py:24-38).
- HTTPS CONNECT failure doesn't stop chain —
service.py:573-580 logs 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-19 uses <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.exe on disk is not re-checked (gost_util.py:61-64).
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 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 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-02–E-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-07–E-10 |
Pool/pinned/exit edge cases need operator UX, not one-line fixes |
| E-12–E-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-16–E-18 |
OS adapter/MAC spoof recovery after crash |
| E-19–E-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 “don’t 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-28–E-30 |
Third-party intel APIs / map geo / GOST blacklist rules |
| M-01–M-14 |
README alignment, GUI kill-switch button, encrypted vault, ban history, etc. |
| U-01–U-10 |
Tray/minimize UX, admin relaunch save state, About version, cookie menu keys, password masking in list |
| T-01–T-14 |
Test coverage expansion |
| B-03–B-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