"""Browser identity personas + cookie policies. The "lock everything down" approach (Firefox RFP + WebRTC off + cookies blocked) keeps you safe from fingerprinting but makes the machine *look weird* — sites flag you as suspicious and ban / captcha you. Worse: very few users use that config, so RFP-on is itself a fingerprint. Two strategies are exposed: • **Blend-in personas** — mimic the most common Windows-Chrome-US-en setup. JS / cookies / WebGL all work; everything looks normal; only WebRTC is blocked (because that leaks the real IP behind the proxy). The proxy chain itself does the actual hiding. • **Hardened** — full RFP / blocked WebRTC / strict tracking / FPI. Stand-out but maximum block. Same behavior as the original profile. Cookie modes are independent of persona. Each mode expresses a complete policy: which cookies to accept, lifetime, whether to wipe on close, and whether to partition 3rd-party storage. """ from __future__ import annotations from dataclasses import dataclass # ── Personas ────────────────────────────────────────────────────────────────── PERSONAS: list[str] = [ "blend_windows_chrome", "blend_windows_chrome_laptop", "blend_windows_firefox", "blend_windows_edge", "blend_mac_safari", "blend_mac_chrome", "blend_linux_firefox", "hardened", "custom", ] PERSONA_LABELS: dict[str, str] = { "blend_windows_chrome": "Blend — Win10 + Chrome 124 (most common, US-en)", "blend_windows_chrome_laptop": "Blend — Win11 + Chrome 124 laptop 1366×768", "blend_windows_firefox": "Blend — Win10 + Firefox 128 (US-en)", "blend_windows_edge": "Blend — Win11 + Edge 124 (corporate look)", "blend_mac_safari": "Blend — macOS 14 + Safari 17 (US-en)", "blend_mac_chrome": "Blend — macOS 14 + Chrome 124 (US-en)", "blend_linux_firefox": "Blend — Ubuntu + Firefox 128 (en-US)", "hardened": "Hardened — RFP + FPI + block all (stands out)", "custom": "Custom — use individual hardening toggles", } @dataclass(frozen=True) class Persona: """A user-agent / locale / timezone / screen tuple that mimics a real, common configuration.""" key: str user_agent: str accept_language: str timezone: str screen_w: int screen_h: int platform: str locale: str PERSONA_DATA: dict[str, Persona] = { "blend_windows_chrome": Persona( key="blend_windows_chrome", user_agent=( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" ), accept_language="en-US,en;q=0.9", timezone="America/New_York", screen_w=1920, screen_h=1080, platform="Win32", locale="en-US", ), "blend_windows_chrome_laptop": Persona( key="blend_windows_chrome_laptop", user_agent=( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" ), accept_language="en-US,en;q=0.9", timezone="America/Chicago", screen_w=1366, screen_h=768, platform="Win32", locale="en-US", ), "blend_windows_firefox": Persona( key="blend_windows_firefox", user_agent=( "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) " "Gecko/20100101 Firefox/128.0" ), accept_language="en-US,en;q=0.5", timezone="America/New_York", screen_w=1920, screen_h=1080, platform="Win32", locale="en-US", ), "blend_windows_edge": Persona( key="blend_windows_edge", user_agent=( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0" ), accept_language="en-US,en;q=0.9", timezone="America/Chicago", screen_w=1920, screen_h=1080, platform="Win32", locale="en-US", ), "blend_mac_safari": Persona( key="blend_mac_safari", user_agent=( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Safari/605.1.15" ), accept_language="en-US,en;q=0.9", timezone="America/Los_Angeles", screen_w=1680, screen_h=1050, platform="MacIntel", locale="en-US", ), "blend_mac_chrome": Persona( key="blend_mac_chrome", user_agent=( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" ), accept_language="en-US,en;q=0.9", timezone="America/Los_Angeles", screen_w=1440, screen_h=900, platform="MacIntel", locale="en-US", ), "blend_linux_firefox": Persona( key="blend_linux_firefox", user_agent=( "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:128.0) " "Gecko/20100101 Firefox/128.0" ), accept_language="en-US,en;q=0.5", timezone="America/New_York", screen_w=1920, screen_h=1080, platform="Linux x86_64", locale="en-US", ), } # ── Cookie policies ─────────────────────────────────────────────────────────── @dataclass(frozen=True) class CookiePolicy: """ Complete Firefox cookie configuration expressed as a single composable unit. behavior : network.cookie.cookieBehavior 0 = accept all 1 = block 3rd-party 2 = block ALL cookies 3 = block 3rd-party from unvisited sites 4 = block cross-site tracking (ETP Strict) 5 = block cross-site + social media trackers lifetime : network.cookie.lifetimePolicy 0 = normal (respect Set-Cookie expires) 2 = session-only (all cookies expire on close) partition : Total Cookie Protection — partition 3rd-party storage by the top-level site (network.cookie.cookieBehavior.optInPartitioning) clear_cookies : privacy.clearOnShutdown.cookies clear_cache : privacy.clearOnShutdown.cache clear_history : privacy.clearOnShutdown.history clear_ls : privacy.clearOnShutdown.localStorage + indexedDB clear_formdata : privacy.clearOnShutdown.formdata sanitize : privacy.sanitize.sanitizeOnShutdown (master switch) """ behavior: int = 1 lifetime: int = 0 partition: bool = False sanitize: bool = False clear_cookies: bool = False clear_cache: bool = False clear_history: bool = False clear_ls: bool = False clear_formdata: bool = False # Every cookie mode is a complete CookiePolicy — no guesswork in profile.py. COOKIE_POLICIES: dict[str, CookiePolicy] = { # ── Permissive ────────────────────────────────────────────────────────── "accept_all": CookiePolicy( behavior=0, lifetime=0, # No clearing, no blocking — maximum site compatibility. ), "accept_all_session": CookiePolicy( behavior=0, lifetime=2, sanitize=True, clear_cookies=True, clear_ls=True, # Accept all cookies while browsing but wipe them when the # browser closes. Good for signup / form-filling sessions. ), # ── Standard 3rd-party blocking ──────────────────────────────────────── "block_third_party": CookiePolicy( behavior=1, lifetime=0, # Block 3rd-party, accept 1st-party with normal lifetime. # Recommended baseline — most sites work fine. ), "block_third_party_session": CookiePolicy( behavior=1, lifetime=2, sanitize=True, clear_cookies=True, clear_cache=True, clear_ls=True, # Block 3rd-party AND treat all accepted cookies as session-only. # Nothing persists. Use for anonymous sessions. ), # ── Tracker-focused blocking ──────────────────────────────────────────── "block_third_party_trackers": CookiePolicy( behavior=4, lifetime=0, # Firefox ETP Strict mode: only blocks known trackers, not ALL # 3rd-party cookies. Best blend-in option — same as Firefox default. ), "block_social_trackers": CookiePolicy( behavior=5, lifetime=0, # Block cross-site cookies AND social-media tracking cookies # (Facebook, Twitter pixels, etc.). Stricter than ETP Strict but # still allows most logins. ), "block_unvisited": CookiePolicy( behavior=3, lifetime=0, # Block 3rd-party from sites the user has never visited. Very light # — almost invisible to sites; good when you need max compatibility. ), # ── Partitioned storage (Total Cookie Protection) ─────────────────────── "partitioned_tcp": CookiePolicy( behavior=1, lifetime=0, partition=True, # Block 3rd-party AND enable Total Cookie Protection: each site # gets its own isolated cookie jar so cross-site tracking via # cookie sync is impossible. Recommended for blend-in + privacy. ), "partitioned_session": CookiePolicy( behavior=1, lifetime=2, partition=True, sanitize=True, clear_cookies=True, clear_cache=True, clear_ls=True, clear_formdata=True, # Partitioned + session-only + full wipe on close. Leaves no # persistent state on disk after the browser exits. ), # ── Maximum wipe ─────────────────────────────────────────────────────── "session_only": CookiePolicy( behavior=0, lifetime=2, sanitize=True, clear_cookies=True, # All cookies accepted but expire when the browser closes. # Note: localStorage is NOT cleared here — only cookies. ), "ghost_mode": CookiePolicy( behavior=1, lifetime=2, sanitize=True, clear_cookies=True, clear_cache=True, clear_history=True, clear_ls=True, clear_formdata=True, partition=True, # Maximum ephemeral session: block 3rd-party, TCP partitioning, # session-only lifetime, full wipe of cookies + cache + history + # localStorage/IndexedDB + formdata on every close. Zero disk trace. ), "block_all": CookiePolicy( behavior=2, lifetime=0, # Block ALL cookies. Most login-dependent sites will break. # Only useful for read-only scraping sessions. ), } COOKIE_MODES: list[str] = list(COOKIE_POLICIES) COOKIE_LABELS: dict[str, str] = { "accept_all": "Accept all — maximum compatibility (cookies persist)", "accept_all_session": "Accept all, session only — wipe cookies on close", "block_third_party": "Block 3rd-party — recommended baseline (persists 1st-party)", "block_third_party_session": "Block 3rd-party + session — nothing persists", "block_third_party_trackers":"Block trackers only — ETP Strict (Firefox default-strict)", "block_social_trackers": "Block social + cross-site trackers (Facebook pixel, etc.)", "block_unvisited": "Block 3rd-party from unvisited sites — lightest option", "partitioned_tcp": "Partitioned (TCP) — each site gets isolated 3rd-party jar", "partitioned_session": "Partitioned + session — isolated jars, full wipe on close", "session_only": "Session only — all cookies expire on close (no partition)", "ghost_mode": "Ghost mode — partitioned + session + full disk wipe on close", "block_all": "Block ALL cookies — breaks most logins", } # ── Helpers ─────────────────────────────────────────────────────────────────── def get_cookie_policy(mode: str) -> CookiePolicy: return COOKIE_POLICIES.get(mode, COOKIE_POLICIES["block_third_party"]) # Legacy shims kept for code that still calls these directly. def cookie_behavior_value(mode: str) -> int: return get_cookie_policy(mode).behavior def cookie_session_only(mode: str) -> bool: return get_cookie_policy(mode).lifetime == 2 def get_persona(key: str) -> Persona | None: return PERSONA_DATA.get(key) def is_blend_persona(key: str) -> bool: return key.startswith("blend_")