const { app, BrowserWindow, WebContentsView, ipcMain, session } = require('electron'); const path = require('path'); const fs = require('fs'); const os = require('os'); const cheerio = require('cheerio'); const { ElectronBlocker, adsAndTrackingLists } = require('@cliqz/adblocker-electron'); const { autoUpdater } = require('electron-updater'); // Disable Trusted Types CSP enforcement engine-wide. // YouTube sends `Content-Security-Policy: require-trusted-types-for 'script'`, // which blocks the cliqz adblocker's scriptlet injection (it uses plain // `script.text = ...`) → 52+ console errors and broken anti-adblock neutralizers. // Stripping the CSP header via webRequest doesn't work — the adblocker's own // onHeadersReceived hook overwrites ours (Electron allows only one listener // per session). Disabling the Blink feature is the cleanest fix; safe in a // kiosk single-user context. app.commandLine.appendSwitch('disable-blink-features', 'TrustedDOMTypes'); const CONFIG_PATH = path.join(os.homedir(), '.ESH-Media.json'); const BLOCKER_CACHE_PATH = path.join(os.homedir(), '.ESH-Media-adblock-v3.bin'); const DEFAULT_TRUSTED_DOMAINS = [ // Google ecosystem (OAuth) 'google.com', 'accounts.google.com', 'googleapis.com', 'googleusercontent.com', 'gstatic.com', 'youtube.com', 'ytimg.com', 'googlevideo.com', // Yandex 'yandex.ru', 'yandex.com', 'passport.yandex.ru', 'passport.yandex.com', 'yastatic.net', // GitHub 'github.com', 'github.io', 'githubassets.com', 'githubusercontent.com', // VK / Mail.ru 'vk.com', 'vk.ru', 'vkuser.net', 'mail.ru', 'my.mail.ru', // Microsoft (login.live.com etc., некоторые сайты через них) 'live.com', 'microsoft.com', 'microsoftonline.com', 'office.com', // Apple 'apple.com', 'icloud.com', // Facebook (для соцлогина) 'facebook.com', 'fb.com', ]; const DEFAULT_CONFIG = { apps: [], proxy: { host: '127.0.0.1', port: '7890' }, trustedDomains: DEFAULT_TRUSTED_DOMAINS }; let blockerPromise = null; let cachedTrustedDomains = DEFAULT_TRUSTED_DOMAINS; function loadTrustedDomainsFromDisk() { try { if (fs.existsSync(CONFIG_PATH)) { const cfg = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); if (Array.isArray(cfg.trustedDomains) && cfg.trustedDomains.length) { cachedTrustedDomains = cfg.trustedDomains; } } } catch (_) {} } function isTrustedDomain(hostname) { if (!hostname) return false; const h = hostname.toLowerCase(); return cachedTrustedDomains.some(d => { const dom = d.toLowerCase().replace(/^\./, ''); return h === dom || h.endsWith('.' + dom); }); } function getBlocker() { if (blockerPromise) return blockerPromise; blockerPromise = (async () => { // Load from cache first (avoids re-downloading on every startup) if (fs.existsSync(BLOCKER_CACHE_PATH)) { try { const data = fs.readFileSync(BLOCKER_CACHE_PATH); const b = ElectronBlocker.deserialize(new Uint8Array(data)); console.log('[adblock] loaded from cache'); return b; } catch (e) { console.warn('[adblock] cache invalid, re-downloading:', e.message); } } // Download filter lists (EasyList + EasyPrivacy + uBlock Origin + Russian ad networks) console.log('[adblock] downloading filter lists...'); const fetchFn = (url, opts) => getProxySession().fetch(url, opts); const russianLists = [ 'https://filters.adtidy.org/extension/ublock/filters/1.txt', // AdGuard Russian 'https://easylist-downloads.adblockplus.org/ruadlist+easylist.txt', // RuAdList ]; const b = await ElectronBlocker.fromLists(fetchFn, [...adsAndTrackingLists, ...russianLists]); // Whitelist domains that need ALL requests passed through unfiltered. // Tracking-list false positives on these break critical functionality: // • Google: OAuth/login integrity checks fail without gstatic + analytics endpoints // → "Возможно, этот браузер или приложение небезопасны" error // • Yandex/Mail/Microsoft/Apple: same OAuth-style integrity flows // • TMDB: movie search API and poster CDN const whitelist = [ '@@||api.themoviedb.org^', '@@||image.tmdb.org^', '@@||themoviedb.org^', '@@||google.com^', '@@||googleapis.com^', '@@||googleusercontent.com^', '@@||gstatic.com^', '@@||youtube.com^', '@@||ytimg.com^', '@@||googlevideo.com^', '@@||google-analytics.com^', '@@||googletagmanager.com^', '@@||yandex.ru^', '@@||yandex.com^', '@@||yastatic.net^', '@@||mc.yandex.ru^', '@@||github.com^', '@@||githubassets.com^', '@@||githubusercontent.com^', '@@||vk.com^', '@@||vk.ru^', '@@||vkuser.net^', '@@||mail.ru^', '@@||my.mail.ru^', '@@||imgsmail.ru^', '@@||microsoft.com^', '@@||microsoftonline.com^', '@@||live.com^', '@@||office.com^', '@@||apple.com^', '@@||icloud.com^', '@@||facebook.com^', '@@||fbcdn.net^', ]; b.updateFromDiff({ added: whitelist }); fs.writeFileSync(BLOCKER_CACHE_PATH, Buffer.from(b.serialize())); console.log('[adblock] filter lists downloaded and cached'); return b; })(); return blockerPromise; } function enableBlockingInSession(sess) { getBlocker() .then(b => { b.enableBlockingInSession(sess); // Remove the cliqz preload script that the blocker just registered on this // session. The preload injects inline