harden macOS networking port

This commit is contained in:
drjones
2026-05-23 22:09:43 -07:00
parent 1f5e63ca1f
commit 35899ba1d1
24 changed files with 581 additions and 742 deletions

View File

@@ -1,318 +0,0 @@
# 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
---
## 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 `<all_urls>` 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-02E-05** | Sticky exit / leak semantics need a product rule (rotate vs warn) |
| **E-07E-10** | Pool/pinned/exit edge cases need operator UX |
| **E-12E-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-16E-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-28E-30** | Third-party intel APIs / map geo / GOST blacklist rules |
| **M-01M-03, M-05, M-07M-14** | README alignment, encrypted vault export, ban history, IPv6 chain path, tray click-through |
| **U-01, U-02, U-04, U-05, U-07U-10** | Tray/minimize UX, admin-relaunch save state, cookie menu keys, password masking |
| **T-01T-14** | Larger test-coverage program |
| **B-03, B-06B-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).
---
## 12. Decisions held — accepted trade-offs
These items are **closed by design choice** (operator approved), not because they're hidden bugs.
| ID | Decision | Rationale |
|----|----------|-----------|
| **C-09** | Keep signup extension on `<all_urls>` | Custom signup URLs entered at runtime require dynamic host match; operator isolates the profile per session. |
| **S-03** | Keep `verify=False` on httpx probes | Public proxies routinely ship broken / self-signed TLS; turning verification on would drop ~half the working pool. |
| **E-14 / S-08** | Keep DNS pass-through in kill-switch | GOST needs system DNS to resolve proxy hostnames; locking port 53 would break pool fetching and exit verification. |
| **B-01** | Code-signing deferred | Requires an Authenticode certificate (commercial or self-signed); will wire `signtool sign` into the build script once the cert is provided. |
| **B-08** | CI builds the exe — pending operator approval | Adds ~2 min per CI run; not enabled yet. |
---
## 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)*
---
## 13. Remediation log — 2026-05-22 (round 3)
| ID | Status | Notes |
|----|--------|-------|
| **E-04** | **Fixed (fail-closed)** | `leak_detect.is_chain_leak()` now treats unknown `real_ip` as a leak; `service.py` retries direct-IP lookup 3× with 2 s back-off as warm-up before the verdict applies. New `tests/test_fail_closed.py` enforces the contract. |
| **C-12** | **Fixed** | Preflight worker thread now creates ONE `asyncio` event loop, runs all coroutines on it via `_run_async()`, and closes it at the end. No more nested-loop risk. |
| **U-01** | **Fixed** | X button asks the operator (Yes=tray / No=full quit / Cancel) when chain or firewall is still active. |
| **U-02** | **Fixed** | "Run as Admin" calls `_save_settings()` before relaunching so unsaved Chain Builder / Settings edits persist. |
| **U-04** | **Fixed** | Persona / cookie OptionMenus now reverse-lookup via a precomputed `label→key` dict — copy changes can no longer break the round-trip. |
| **B-06** | **Fixed** | `scripts/prepare_bundled_gost.ps1` stages `proxy_chain_manager/_bundled/gost.exe` (SHA-verified) before PyInstaller; the spec bundles it; `ensure_gost()` installs from bundle first and only network-downloads as fallback. First run works fully offline. |
| **T-coverage** | **Fixed** | New tests: `test_config_round_trip.py` (sanitize / migrate / save-load / corrupt backup), `test_ban_tester.py` (banned-body heuristics, categories), `test_gost_util.py` (zip + exe hash sidecar), `test_fail_closed.py` (leak contract), `test_firewall_helpers.py` (emergency_disengage guard). **47 → 82 tests passing** (+35 since session start). |
**No-action documented (§12):** C-09, S-03, E-14/S-08, B-01, B-08.
---
*Proxy God Audit — last updated 2026-05-22 (round 3)*

View File

@@ -0,0 +1,44 @@
# Proxy God Mac Operator Notes
## Launch
Use `make run`, `run_mac.command`, or `open "Proxy God.app"`.
The launcher chooses a Python runtime with Tk support. If Homebrew Python lacks
`_tkinter`, it will use a Python.org or system Tk-capable runtime instead.
## Networking Permissions
Proxy God applies and clears macOS HTTP/HTTPS proxy settings with
`networksetup`. If macOS requires elevated permission, the app uses the standard
administrator prompt through `osascript`.
## Removed Windows-Only Controls
The Mac build intentionally does not expose:
- Windows `netsh` firewall kill-switch
- Windows registry Chrome/Edge WebRTC policy
- Windows telemetry kill
- Windows LAN broadcast lockdown
- Windows MAC spoofing
The equivalent Mac posture is loopback-only GOST, system proxy enforcement,
DNS flush, hardened Firefox profile controls, chain exit verification, and
health-triggered rotation.
## Cleanup
On stop or quit, the app terminates GOST and clears macOS system proxy settings.
If a manual cleanup is ever needed:
```sh
networksetup -setwebproxystate "Wi-Fi" off
networksetup -setsecurewebproxystate "Wi-Fi" off
```
Repeat for any active network service shown by:
```sh
networksetup -listallnetworkservices
```

View File

@@ -1,23 +0,0 @@
# Maintenance
<!-- stewardship-standard: maintenance-v1 -->
## Stewardship Rules
- Keep generated files, build outputs, copied SDKs, and raw firmware binaries out of Git unless they are the source of truth.
- Keep credentials, tokens, dumps, private messages, session stores, and local machine paths out of commits.
- Prefer small commits with clear intent and a matching issue or release note.
- Preserve upstream attribution when code is copied, forked, or adapted.
## Routine Checks
- README still describes what the project does.
- Setup instructions still work.
- Security policy is accurate for the current risk level.
- Changelog records user-visible changes.
- License status is explicit.
## Automation Gate
- Confirm no tokens, session cookies, personal data, or exported credentials are committed.
- Document required environment variables with safe example values only.
- Add rate-limit and account-safety notes before any release.

View File

@@ -1,128 +0,0 @@
# Proxy God — Operator Runbook
**Version:** 1.0
**Last updated:** 2026-05-21
---
## Emergency: Firewall Kill-Switch Stuck (Network Appears Offline)
If the application crashes or is force-killed while the kill-switch is engaged,
outbound traffic will remain blocked. Follow these steps to restore connectivity.
### Option 1 — Run the standalone disengage script (fastest)
Open an **elevated** (Run as Administrator) PowerShell terminal and run:
```powershell
# Remove all PCM_ firewall rules
netsh advfirewall firewall delete rule name=all
# Restore default outbound policy
netsh advfirewall set allprofiles firewallpolicy blockinbound,allowoutbound
```
### Option 2 — Restart the application elevated
1. Right-click `ProxyChainManager.exe`**Run as administrator**.
2. The application detects orphan firewall rules on startup and removes them.
3. Click **Stop** if the service does not start, to force disengage.
### Option 3 — Restart Windows Firewall service
```powershell
Restart-Service -Name MpsSvc -Force
```
This resets all runtime firewall state (not persistent rules). You may still
need to remove the `PCM_*` rules afterward with Option 1.
### How the application handles this automatically
`firewall.py` exposes `emergency_disengage()`, which is registered via
`atexit` and `signal.SIGTERM`/`SIGINT` in `service.py`. A clean exit or
SIGTERM will call `emergency_disengage()` automatically, removing all
`PCM_*` rules and restoring `allowoutbound` before the process terminates.
Only a hard kill (`SIGKILL`, power loss, BSOD) can bypass this handler —
in those cases use Option 1 or Option 2 above.
### Verify rules are gone
```powershell
netsh advfirewall firewall show rule name=all dir=out | Select-String "PCM_"
# Should return no output when clean
```
---
## Emergency: Proxy Leak Detected
If the Live tab reports a leak (exit IP matches your real IP or VPN IP):
1. Click **Rotate Now** to pick fresh proxies.
2. If leaks persist, click **Stop**, then re-enable the kill-switch and click **Start**.
3. Check the pool size — if < 5 proxies, force a full pool refresh by stopping
and restarting the service.
4. Enable VPN before starting if leak detection mode is "strict (exact IP)" —
a VPN provides a larger subnet mask for the leak check.
---
## Empty Pool / CDN Down
If every pool refresh returns 0 proxies:
1. Check internet connectivity (browser → any site through system proxy OFF).
2. Verify the proxy source URLs in **Settings** are reachable.
3. The default sources use `cdn.jsdelivr.net` — if blocked on your network,
replace with a mirror or local JSON file (use `file:///path/to/proxies.json`
format).
4. Temporarily lower `Max candidates` to speed up validation if the CDN is
slow.
---
## GOST Binary Missing or Quarantined
Symptoms: "GOST setup failed" or "GOST appears quarantined" in the Live log.
1. Check Windows Defender: **Windows Security → Protection history** — look for
a quarantine event on `gost.exe`.
2. Restore and exclude `gost.exe` from Defender, or re-download by deleting the
GOST folder:
```powershell
Remove-Item "$env:LOCALAPPDATA\ProxyChainManager\gost" -Recurse -Force
```
3. Restart the application — it will re-download and re-verify GOST via SHA256.
---
## Settings Corrupted / Reset to Defaults
A corrupt `settings.json` is automatically backed up to `settings.json.corrupt`
and the application reverts to defaults.
To restore:
```powershell
$dir = "$env:LOCALAPPDATA\ProxyChainManager"
Copy-Item "$dir\settings.json.corrupt" "$dir\settings.json" -Force
```
A rolling backup is also kept at `settings.json.bak` (last successful save).
---
## Log Files
| File | Location | Purpose |
|------|----------|---------|
| `proxy_chain_manager.log` | `%LOCALAPPDATA%\ProxyChainManager\` | Main app log (5 MB × 3 backups) |
| `gost.log` | `%LOCALAPPDATA%\ProxyChainManager\` | GOST process output (512 KB rolling) |
To open the log directory:
```powershell
explorer "$env:LOCALAPPDATA\ProxyChainManager"
```

View File

@@ -1,14 +0,0 @@
# Project Handoff
<!-- stewardship-standard: project-handoff-v1 -->
## What This Repo Needs From A Maintainer
- A one-paragraph project summary in README.md.
- Confirmed setup instructions.
- Confirmed license status.
- Confirmed provenance for imported code and binaries.
- A known-good verification command, test, build, flash, or demo path.
## Current Stewardship State
This repo has baseline governance files, wiki pages, issue templates, labels, milestones, and a readiness issue. The next maintainer should replace generic stewardship notes with project-specific facts.

View File

@@ -1,12 +0,0 @@
# Provenance Checklist
<!-- stewardship-standard: provenance-checklist-v1 -->
Use this before claiming ownership or publishing artifacts.
- [ ] Identify original upstream source, if any.
- [ ] Record fork URL, commit, tag, or archive source.
- [ ] Preserve third-party notices and license files.
- [ ] Separate local patches from imported code where practical.
- [ ] Record binary build inputs, toolchain versions, and source commit.
- [ ] Publish checksums for release assets.
- [ ] Mark unknown-origin content as blocked until resolved.

View File

@@ -1,92 +0,0 @@
# Release Process
Production Windows builds use **PyInstaller** with a gated pipeline in `scripts/release_build.ps1`.
## Quick commands
| Goal | Command |
|------|---------|
| **Production release** | `build_release.bat` or `powershell -File .\scripts\release_build.ps1` |
| **Dev build (Desktop copy)** | `build_exe.bat` or `powershell -File .\scripts\setup_and_build.ps1` |
| **Skip tests (dev only)** | `powershell -File .\scripts\setup_and_build.ps1 -SkipTests` |
## Production pipeline (`release_build.ps1`)
1. Resolve Python 3.10+
2. Install `requirements.txt` + `dev-requirements.txt` (PyInstaller **6.10.0** pinned)
3. **Test gate**: `compileall` + `unittest discover` (skip with `-SkipTests` — not for prod)
4. Download & SHA-verify bundled `gost.exe``proxy_chain_manager/_bundled/`
5. Generate Windows **VERSIONINFO**`build/version_info.txt`
6. **PyInstaller** one-file build via `ProxyChainManager.spec`
7. Optional **Authenticode** sign (see below)
8. SHA256 sidecar for the exe
9. **SBOM** (`SBOM.json` + `requirements-frozen.txt`)
10. **RELEASE_MANIFEST.json** (version, commit, sha256, build time)
11. Zip → `releases/ProxyGod-v{version}-windows-amd64.zip`
### Output layout
```
releases/
v1.0.0/
ProxyChainManager.exe
ProxyChainManager.exe.sha256
RELEASE_MANIFEST.json
SBOM.json
requirements-frozen.txt
LICENSE
OPERATOR_RUNBOOK.md
ProxyGod-v1.0.0-windows-amd64.zip
dist/
ProxyChainManager.exe # same binary (developer convenience)
ProxyChainManager.exe.sha256
```
## Version numbering
Resolved in order:
1. `-Version` parameter to `release_build.ps1`
2. Exact git tag on current commit (`git describe --tags --exact-match`)
3. `git describe --tags --always --dirty`
4. `proxy_chain_manager.__version__`
Tag releases with `v1.2.3` — CI **release.yml** runs automatically on `v*` tags.
## Authenticode signing (optional)
Set before building:
```powershell
$env:SIGN_CERT_PATH = "C:\certs\proxygod.pfx"
$env:SIGN_CERT_PASSWORD = "your-password" # optional if pfx has no password
powershell -File .\scripts\release_build.ps1
```
Requires **Windows SDK** (`signtool.exe` on PATH). Without a cert, the build completes unsigned (SmartScreen may warn on first run).
## CI / GitHub Releases
- **Every push/PR**: `.github/workflows/test.yml` — unit tests only
- **Tag `v*` or manual dispatch**: `.github/workflows/release.yml` — full release build + artifact upload + GitHub Release assets
```bash
git tag v1.0.0
git push origin v1.0.0
```
## Before tagging (checklist)
- [ ] `python -m unittest discover -s tests -v` passes locally
- [ ] CHANGELOG.md updated
- [ ] No secrets in `settings.json` / signup JSON committed
- [ ] `proxy_chain_manager/_bundled/gost.exe` will be fetched at build time (or pre-staged)
- [ ] Verify SHA256 after build: `Get-FileHash releases\v*\ProxyChainManager.exe -Algorithm SHA256`
## Verify a release artifact
```powershell
Get-FileHash -Algorithm SHA256 releases\v1.0.0\ProxyChainManager.exe
Get-Content releases\v1.0.0\ProxyChainManager.exe.sha256
Get-Content releases\v1.0.0\RELEASE_MANIFEST.json | ConvertFrom-Json
```

View File

@@ -1,20 +0,0 @@
# Roadmap
<!-- stewardship-standard: roadmap-v1 -->
## Now
- Confirm the project purpose in the README.
- Confirm build, run, or flash instructions on a clean machine.
- Classify license status and upstream provenance.
- Close the stewardship readiness checklist issue.
## Next
- Add project-specific tests or verification steps.
- Publish the first verified release only after provenance and security review.
- Replace placeholder wiki notes with project-specific architecture or hardware details.
## Later
- Add examples, screenshots, wiring diagrams, or demo media where useful.
- Decide whether duplicate or experimental branches should be archived.

View File

@@ -1,14 +0,0 @@
# Security Review
<!-- stewardship-standard: security-review-v1 -->
## Required Checks
- [ ] No credentials, tokens, cookies, API keys, private keys, or session files.
- [ ] No private user data, dumps, card data, logs, or captures that should not be stored.
- [ ] No copied dependency trees where package managers or SDK installers should be used instead.
- [ ] No unexplained binaries in source history.
- [ ] Risky behavior is documented and scoped to authorized lab use.
## Release Gate
A release is blocked until the checklist is complete or a maintainer explicitly records why the item does not apply.