Build comprehensive privacy suite and hardened browser controls.
Adds VPN-aware leak handling, chain testing UX improvements, hardened Firefox launch/profile management, privacy/device hardening modules, and tray/status upgrades so the app is production-ready as the new baseline. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
113
proxy_chain_manager/mac_spoof.py
Normal file
113
proxy_chain_manager/mac_spoof.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""Randomize and restore NIC MAC addresses (Admin required)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import random
|
||||
import re
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .firewall import is_admin
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
_MAC_RE = re.compile(r"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$")
|
||||
|
||||
|
||||
@dataclass
|
||||
class NicMac:
|
||||
name: str
|
||||
mac: str
|
||||
description: str = ""
|
||||
|
||||
|
||||
def _run(args: list[str], timeout: float = 20.0) -> tuple[int, str, str]:
|
||||
try:
|
||||
r = subprocess.run(
|
||||
args,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
|
||||
)
|
||||
return r.returncode, r.stdout or "", r.stderr or ""
|
||||
except Exception as e:
|
||||
return 1, "", str(e)
|
||||
|
||||
|
||||
def list_nics() -> list[NicMac]:
|
||||
"""Physical/up adapters with current MAC."""
|
||||
script = (
|
||||
"Get-NetAdapter | Where-Object { $_.Status -ne 'Disabled' } | "
|
||||
"Select-Object Name, MacAddress, InterfaceDescription | "
|
||||
"ConvertTo-Json -Compress"
|
||||
)
|
||||
code, out, _ = _run(["powershell", "-NoProfile", "-Command", script])
|
||||
if code != 0 or not out.strip():
|
||||
return []
|
||||
import json
|
||||
|
||||
try:
|
||||
data = json.loads(out)
|
||||
except json.JSONDecodeError:
|
||||
return []
|
||||
rows = data if isinstance(data, list) else [data]
|
||||
nics: list[NicMac] = []
|
||||
for row in rows:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
name = str(row.get("Name", "")).strip()
|
||||
mac = str(row.get("MacAddress", "")).strip().replace("-", ":")
|
||||
desc = str(row.get("InterfaceDescription", "")).strip()
|
||||
if name and mac and mac != "00:00:00:00:00:00":
|
||||
nics.append(NicMac(name=name, mac=mac, description=desc))
|
||||
return nics
|
||||
|
||||
|
||||
def random_mac() -> str:
|
||||
"""Locally administered unicast MAC."""
|
||||
b = [random.randint(0, 255) for _ in range(6)]
|
||||
b[0] = (b[0] | 0x02) & 0xFE
|
||||
return ":".join(f"{x:02X}" for x in b)
|
||||
|
||||
|
||||
def set_mac(adapter: str, mac: str) -> tuple[bool, str]:
|
||||
if not is_admin():
|
||||
return False, "Administrator required to change MAC."
|
||||
mac = mac.replace("-", ":").upper()
|
||||
if not _MAC_RE.match(mac.replace(":", "-")):
|
||||
return False, f"Invalid MAC: {mac}"
|
||||
ps = (
|
||||
f'$a = Get-NetAdapter -Name "{adapter}" -ErrorAction Stop; '
|
||||
f'Set-NetAdapter -Name $a.Name -MacAddress "{mac}" -Confirm:$false'
|
||||
)
|
||||
code, _, err = _run(["powershell", "-NoProfile", "-Command", ps])
|
||||
if code != 0:
|
||||
return False, err or "Set-NetAdapter failed"
|
||||
log.info("MAC set %s → %s", adapter, mac)
|
||||
return True, f"{adapter} → {mac}"
|
||||
|
||||
|
||||
def spoof_all_physical(snapshot: dict[str, str] | None = None) -> tuple[dict[str, str], list[str]]:
|
||||
"""Randomize MAC on non-virtual adapters. Returns (original_map, log lines)."""
|
||||
originals: dict[str, str] = dict(snapshot or {})
|
||||
logs: list[str] = []
|
||||
skip_hints = ("virtual", "vmware", "hyper-v", "loopback", "bluetooth", "wan miniport")
|
||||
for nic in list_nics():
|
||||
low = (nic.description + nic.name).lower()
|
||||
if any(h in low for h in skip_hints):
|
||||
continue
|
||||
if nic.name not in originals:
|
||||
originals[nic.name] = nic.mac
|
||||
new_mac = random_mac()
|
||||
ok, msg = set_mac(nic.name, new_mac)
|
||||
logs.append(msg if ok else f"{nic.name}: {msg}")
|
||||
return originals, logs
|
||||
|
||||
|
||||
def restore_macs(originals: dict[str, str]) -> list[str]:
|
||||
logs: list[str] = []
|
||||
for name, mac in originals.items():
|
||||
ok, msg = set_mac(name, mac)
|
||||
logs.append(msg if ok else f"{name}: {msg}")
|
||||
return logs
|
||||
Reference in New Issue
Block a user