From 69c2afca81bc5a67553b72dede1030132908ea5d Mon Sep 17 00:00:00 2001 From: Local Dev Date: Thu, 30 Jul 2026 23:11:28 +0200 Subject: [PATCH] Theseus: media-device privacy fix + region/language pickers, AF under Privacy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Privacy fix: enumerateDevices() leaked speaker (audiooutput) labels + device/ group IDs even with camera/mic blocked. New "Hide media devices" (default on) blanks every device's label/deviceId/groupId and collapses to one per kind, matching Firefox — closes the WebRTC device-fingerprinting leak. - Anti-fingerprinting moved into the Privacy section (own sidebar item removed). - Location spoof: pick a world region (Europe/Asia/N&S America/Africa/Middle East/Australia) → representative coordinates, or Manual for exact lat/lon. - Language spoof: pick from the top-10 world languages, or Manual for any locale. --- main.js | 32 +++++++++++++++++----- settings.html | 74 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 83 insertions(+), 23 deletions(-) diff --git a/main.js b/main.js index 6021e8e..4fabaae 100644 --- a/main.js +++ b/main.js @@ -115,12 +115,13 @@ const SETTINGS_DEFAULTS = { webrtcMode: "public_only", // WebRTC IP policy: default | public_only | public_private | disable_udp blockCamera: true, // deny camera by default (also hides camera labels from fingerprinting) blockMicrophone: true, // deny microphone by default (also hides mic labels) + hideMediaDevices: true, // blank all enumerateDevices info (esp. speaker labels/ids) like Firefox restoreSession: true, // reopen last session's tabs on launch backgroundThrottle: true, // throttle inactive tabs / the window when unfocused // Anti-fingerprinting — each: show (real) | hide (neutral) | spoof (auto decoy) | manual (user value) timezoneMode: "show", timezoneValue: "Europe/Berlin", // IANA zone for manual - languageMode: "show", languageValue: "en-US", // locale for manual - locationMode: "hide", locationLat: "40.7128", locationLon: "-74.0060", // coords for manual (default: deny) + languageMode: "show", languageSpoof: "en-US", languageValue: "en-US", // spoof = top-10 pick, manual = free text + locationMode: "hide", locationRegion: "europe", locationLat: "40.7128", locationLon: "-74.0060", // spoof by region, or manual coords searchEngine: "duckduckgo",// default search engine (built-in id or a custom id) enabledEngines: DEFAULT_ENABLED.slice(), // which built-in engines show in the dropdown customEngines: [], // user-added: [{ id, name, url-with-%s }] @@ -178,17 +179,28 @@ function effTimezone() { function effLocale() { switch (settings.languageMode) { case "hide": return "en-US"; - case "spoof": return SPOOF.lang; + case "spoof": return settings.languageSpoof || SPOOF.lang; // chosen from the top-languages list case "manual": return settings.languageValue || "en-US"; default: return null; } } +// Representative coordinates per world region — used when the spoofed location is +// set to a region rather than exact coordinates (a major city stands in for each). +const REGIONS = { + europe: { lat: 52.5200, lon: 13.4050 }, // Berlin + asia: { lat: 35.6762, lon: 139.6503 }, // Tokyo + north_america: { lat: 40.7128, lon: -74.0060 }, // New York + south_america: { lat: -23.5505, lon: -46.6333 }, // São Paulo + africa: { lat: -1.2921, lon: 36.8219 }, // Nairobi + middle_east: { lat: 25.2048, lon: 55.2708 }, // Dubai + australia: { lat: -33.8688, lon: 151.2093 }, // Sydney +}; // Geolocation: null = show (real, allowed); "deny" = hide (blocked); -// {lat,lon} = spoof/manual (allowed, but coordinates are overridden in-page). +// {lat,lon} = spoof (region-based) / manual (exact) — overridden in-page. function effLocation() { const m = settings.locationMode; if (m === "hide") return "deny"; - if (m === "spoof") return { lat: SPOOF.lat, lon: SPOOF.lon }; + if (m === "spoof") { const r = REGIONS[settings.locationRegion] || REGIONS.europe; return { lat: r.lat, lon: r.lon }; } if (m === "manual") return { lat: Number(settings.locationLat) || 0, lon: Number(settings.locationLon) || 0 }; return null; // show } @@ -219,6 +231,12 @@ async function applyFingerprint(wc) { const pos = `{coords:{latitude:${geo.lat},longitude:${geo.lon},accuracy:100,altitude:null,altitudeAccuracy:null,heading:null,speed:null},timestamp:Date.now()}`; src += `try{const p=()=>(${pos});if(navigator.geolocation){navigator.geolocation.getCurrentPosition=(ok)=>{try{ok(p())}catch(e){}};navigator.geolocation.watchPosition=(ok)=>{try{ok(p())}catch(e){}return 0};}}catch(e){}`; } + // Media-device privacy: Chromium leaks audiooutput (speaker) labels + deviceIds + // via enumerateDevices even when camera/mic are blocked. Like Firefox, blank + // every device's label/deviceId/groupId and collapse to one entry per kind. + if (settings.hideMediaDevices) { + src += `try{const md=navigator.mediaDevices;if(md&&md.enumerateDevices){const o=md.enumerateDevices.bind(md);md.enumerateDevices=async()=>{let l=[];try{l=await o()}catch(e){}const ks=[...new Set(l.map(d=>d.kind))];return ks.map(kind=>({deviceId:'',kind:kind,label:'',groupId:'',toJSON(){return{deviceId:'',kind:kind,label:'',groupId:''}}}))};}}catch(e){}`; + } if (src) { const res = await wc.debugger.sendCommand("Page.addScriptToEvaluateOnNewDocument", { source: src }); wc._langScript = res.identifier; @@ -826,8 +844,8 @@ ipcMain.handle("settings-set", (_e, key, val) => { if (key === "webrtcMode") applyWebRTCPolicy(); if (key === "theme") applyTheme(); if (key === "backgroundThrottle") applyThrottle(); - if (["timezoneMode", "timezoneValue", "languageMode", "languageValue", - "locationMode", "locationLat", "locationLon"].includes(key)) { applyFingerprintAll(); applyAcceptLanguage(); } + if (["timezoneMode", "timezoneValue", "languageMode", "languageSpoof", "languageValue", + "locationMode", "locationRegion", "locationLat", "locationLon", "hideMediaDevices"].includes(key)) { applyFingerprintAll(); applyAcceptLanguage(); } return settings; }); ipcMain.handle("set-chrome-height", (_e, h) => { diff --git a/settings.html b/settings.html index b7638cd..c48c997 100644 --- a/settings.html +++ b/settings.html @@ -18,6 +18,8 @@ section{max-width:640px} h1{font-size:1.4rem;margin:0 0 .2rem} .lede{color:var(--mut);margin:0 0 1.8rem;font-size:13.5px} + h2.sub{font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--dim);margin:1.8rem 0 .2rem;border-top:1px solid var(--line);padding-top:1.4rem} + .subd{color:var(--mut);margin:0 0 1rem;font-size:13px} .row{display:flex;align-items:center;gap:16px;background:var(--panel);border:1px solid var(--line); border-radius:12px;padding:14px 18px;margin-bottom:10px} .row .txt{flex:1} @@ -71,7 +73,6 @@ General Performance Privacy - Anti-fingerprinting
@@ -133,11 +134,13 @@
Block microphone
Deny microphone by default — also hides its name from fingerprinting.
- - -