fix: audit round 2 - DPAPI secrets, pinned hop probe, gost exe hash, admin guard, PID-scoped browser tracking, emergency disengage button, build sidecar
This commit is contained in:
@@ -168,7 +168,8 @@ def _main_inner() -> None:
|
||||
ctk.set_default_color_theme("dark-blue")
|
||||
|
||||
root = ctk.CTk()
|
||||
root.title("Proxy God v2")
|
||||
from . import __version__ as _APP_VERSION
|
||||
root.title(f"Proxy God v{_APP_VERSION}")
|
||||
root.geometry("1080x860")
|
||||
root.minsize(900, 720)
|
||||
root.configure(fg_color=BG)
|
||||
@@ -225,6 +226,7 @@ def _main_inner() -> None:
|
||||
browser = BrowserSession()
|
||||
browser_should_run = [False]
|
||||
browser_last_launch_ts = [0.0]
|
||||
browser_relaunch_count = [0]
|
||||
service_running = [False]
|
||||
|
||||
# ── tray ─────────────────────────────────────────────────────────────────
|
||||
@@ -305,6 +307,26 @@ def _main_inner() -> None:
|
||||
|
||||
def _start() -> None:
|
||||
_save_settings()
|
||||
# Hard-stop: if the kill-switch is enabled but we are not admin, the
|
||||
# firewall lockdown is silently skipped — which contradicts the
|
||||
# README's "fail closed" guarantee. Refuse to start in that mode.
|
||||
try:
|
||||
from .firewall import is_admin as _is_admin
|
||||
except Exception:
|
||||
_is_admin = lambda: True # noqa: E731
|
||||
if svc.settings.kill_switch_enabled and not _is_admin():
|
||||
from tkinter import messagebox
|
||||
if messagebox.askyesno(
|
||||
"Kill-switch requires Admin",
|
||||
"Kill-switch is enabled in Settings but this process is not "
|
||||
"running as Administrator.\n\n"
|
||||
"Continuing now will start the chain WITHOUT the firewall "
|
||||
"lockdown — your traffic will not 'fail closed' if the chain "
|
||||
"drops.\n\n"
|
||||
"Start anyway?",
|
||||
):
|
||||
svc.start()
|
||||
return
|
||||
svc.start()
|
||||
|
||||
start_btn = _btn(topbar, "▶ Start", _start, w=86)
|
||||
@@ -1442,12 +1464,14 @@ def _main_inner() -> None:
|
||||
_save_settings()
|
||||
cfg = _browser_cfg()
|
||||
browser_should_run[0] = True
|
||||
browser_relaunch_count[0] = 0 # user-initiated launch resets the cap
|
||||
if _launch_browser_with_cfg(cfg):
|
||||
browser_last_launch_ts[0] = time.monotonic()
|
||||
|
||||
def _stop_browser() -> None:
|
||||
cfg = _browser_cfg()
|
||||
browser_should_run[0] = False
|
||||
browser_relaunch_count[0] = 0
|
||||
ok, msg = browser.stop(dispose=cfg.disposable_profile)
|
||||
_log(msg)
|
||||
_refresh_browser_status()
|
||||
@@ -2637,6 +2661,34 @@ def _main_inner() -> None:
|
||||
fg_color=ACCENT2, hover_color=ACCENT, text_color=TEXT,
|
||||
).pack(side="left")
|
||||
|
||||
# Emergency disengage — for crashes where the kill-switch lingers and
|
||||
# the operator needs internet back fast without restarting the app.
|
||||
def _emergency_disengage_now() -> None:
|
||||
from tkinter import messagebox
|
||||
from .firewall import emergency_disengage as _ed, is_engaged as _ie
|
||||
if not _ie():
|
||||
messagebox.showinfo("Kill-switch", "No Proxy God firewall rules detected — nothing to disengage.")
|
||||
return
|
||||
if not messagebox.askyesno(
|
||||
"Emergency disengage",
|
||||
"Remove ALL Proxy God firewall rules and restore normal outbound traffic?\n\n"
|
||||
"Use this only when the chain has dropped and the kill-switch is "
|
||||
"blocking everything.",
|
||||
):
|
||||
return
|
||||
_ed()
|
||||
messagebox.showinfo("Kill-switch", "Firewall rules removed.")
|
||||
_log("Emergency disengage: firewall rules removed by operator.")
|
||||
|
||||
ks_btn_row = ctk.CTkFrame(sec_sec, fg_color="transparent")
|
||||
ks_btn_row.pack(fill="x", padx=12, pady=(0, 8))
|
||||
_btn(
|
||||
ks_btn_row, "⚠ Emergency disengage firewall now",
|
||||
_emergency_disengage_now,
|
||||
w=320, h=28,
|
||||
fg_color="#7f1d1d", hover_color="#991b1b",
|
||||
).pack(side="left")
|
||||
|
||||
def _apply_point_and_shoot() -> None:
|
||||
"""Simple safe defaults: secure + low-friction launch profile."""
|
||||
use_manual_var.set(True)
|
||||
@@ -2921,9 +2973,18 @@ def _main_inner() -> None:
|
||||
if browser_should_run[0] and not browser.is_running():
|
||||
cfg = _browser_cfg()
|
||||
now = time.monotonic()
|
||||
max_retries = max(0, int(svc.settings.max_browser_relaunches))
|
||||
if cfg.auto_relaunch and (now - browser_last_launch_ts[0]) >= 3.0:
|
||||
if _launch_browser_with_cfg(cfg):
|
||||
if browser_relaunch_count[0] >= max_retries:
|
||||
browser_should_run[0] = False
|
||||
_log(
|
||||
f"Browser auto-relaunch disabled after "
|
||||
f"{browser_relaunch_count[0]} attempts — fix the profile "
|
||||
"or restart manually."
|
||||
)
|
||||
elif _launch_browser_with_cfg(cfg):
|
||||
browser_last_launch_ts[0] = now
|
||||
browser_relaunch_count[0] += 1
|
||||
root.after(80, _pump)
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user