From bae45d5d9ccd07637f0d8efba45018df43e00193 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras <1228153+chrmod@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:13:14 +0100 Subject: [PATCH] Adblocker: fix $replace and service worker blocking (#2066) --- src/background/adblocker.js | 46 +++++++++++++++----------------- src/background/custom-filters.js | 10 ++++--- tests/e2e/spec/advanced.spec.js | 12 +++++++++ 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/background/adblocker.js b/src/background/adblocker.js index 8d28af9ae..25f6694e2 100644 --- a/src/background/adblocker.js +++ b/src/background/adblocker.js @@ -44,9 +44,9 @@ function getEnabledEngines(config) { list.push(engines.FIXES_ENGINE); } - if (config.customFilters.enabled) { - list.push(engines.CUSTOM_ENGINE); - } + // Custom filters should be always added as + // they have own settings which defines if they are enabled + list.push(engines.CUSTOM_ENGINE); return list; } @@ -58,7 +58,7 @@ function pause(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } -async function reloadMainEngine() { +export async function reloadMainEngine() { // Delay the reload to avoid UI freezes in Firefox and Safari if (__PLATFORM__ !== 'chromium') await pause(1000); @@ -83,13 +83,11 @@ async function reloadMainEngine() { `[adblocker] Main engine reloaded with: ${enabledEngines.join(', ')}`, ); } else { - engines.create(engines.MAIN_ENGINE); + await engines.create(engines.MAIN_ENGINE); console.info('[adblocker] Main engine reloaded with no filters'); } } -engines.addChangeListener(engines.CUSTOM_ENGINE, reloadMainEngine); - let updating = false; async function updateEngines() { if (updating) return; @@ -367,9 +365,16 @@ function isTrusted(request, type) { } if (__PLATFORM__ === 'firefox') { + function isExtensionRequest(details) { + return ( + (details.tabId === -1 && details.url.startsWith('moz-extension://')) || + details.originUrl?.startsWith('moz-extension://') + ); + } + chrome.webRequest.onBeforeRequest.addListener( (details) => { - if (details.tabId < 0 || details.type === 'main_frame') return; + if (details.type === 'main_frame' || isExtensionRequest(details)) return; if (setup.pending) { console.error('[adblocker] not ready for network requests blocking'); @@ -403,7 +408,7 @@ if (__PLATFORM__ === 'firefox') { chrome.webRequest.onHeadersReceived.addListener( (details) => { - if (details.tabId < 0 || details.type === 'main_frame') return; + if (isExtensionRequest(details)) return; if (setup.pending) { console.error('[adblocker] not ready for network headers modification'); @@ -411,22 +416,12 @@ if (__PLATFORM__ === 'firefox') { } const request = Request.fromRequestDetails(details); - const cspPolicies = []; - const htmlFilters = []; - if (!isTrusted(request, details.type)) { - const engine = engines.get(engines.MAIN_ENGINE); + if (isTrusted(request, details.type)) return; - htmlFilters.push(...engine.getHtmlFilters(request)); - - if (details.type === 'main_frame') { - const policies = engine.getCSPDirectives(request); - if (policies !== undefined) { - cspPolicies.push(...policies); - } - } - } + const engine = engines.get(engines.MAIN_ENGINE); + const htmlFilters = engine.getHtmlFilters(request); if (htmlFilters.length !== 0) { request.modified = true; updateTabStats(details.tabId, [request]); @@ -437,9 +432,10 @@ if (__PLATFORM__ === 'firefox') { ); } - if (cspPolicies.length !== 0) { - return updateResponseHeadersWithCSP(details, cspPolicies); - } + if (details.type !== 'main_frame') return; + const cspPolicies = engine.getCSPDirectives(request); + if (!cspPolicies || cspPolicies.length === 0) return; + return updateResponseHeadersWithCSP(details, cspPolicies); }, { urls: ['http://*/*', 'https://*/*'] }, ['blocking', 'responseHeaders'], diff --git a/src/background/custom-filters.js b/src/background/custom-filters.js index b2ef544f6..a47e83e64 100644 --- a/src/background/custom-filters.js +++ b/src/background/custom-filters.js @@ -27,7 +27,7 @@ import * as OptionsObserver from '/utils/options-observer.js'; import Options from '/store/options.js'; import CustomFilters from '/store/custom-filters.js'; -import { setup } from '/background/adblocker.js'; +import { setup, reloadMainEngine } from '/background/adblocker.js'; const convert = __PLATFORM__ === 'chromium' @@ -176,6 +176,10 @@ async function update(text, { trustedScriptlets }) { result.errors = errors; + // Update main engine with custom filters + await reloadMainEngine(); + + // Update DNR rules for Chromium and Safari if (__PLATFORM__ === 'chromium' || __PLATFORM__ === 'safari') { const dnrResult = await Promise.allSettled( [...networkFilters].map((filter) => convert(filter)), @@ -221,7 +225,7 @@ OptionsObserver.addListener('customFilters', async (value, lastValue) => { // If we cannot initialize engine, we need to update it if (!(await engines.init(engines.CUSTOM_ENGINE))) { - update((await store.resolve(CustomFilters)).text, { + await update((await store.resolve(CustomFilters)).text, { trustedScriptlets, }); } @@ -232,7 +236,7 @@ OptionsObserver.addListener('customFilters', async (value, lastValue) => { return; } - update(enabled ? (await store.resolve(CustomFilters)).text : '', { + await update(enabled ? (await store.resolve(CustomFilters)).text : '', { trustedScriptlets, }); } diff --git a/tests/e2e/spec/advanced.spec.js b/tests/e2e/spec/advanced.spec.js index 1fb77bab4..2b315ccfe 100644 --- a/tests/e2e/spec/advanced.spec.js +++ b/tests/e2e/spec/advanced.spec.js @@ -96,6 +96,18 @@ describe('Advanced Features', function () { }); }); + // Scope for Firefox webRequest API tests + if (browser.isFirefox) { + it('adds $replace network filter', async function () { + await setCustomFilters([ + `||${PAGE_DOMAIN}^$replace=/.*<\\/title>/<title>hello world<\\/title>/`, + ]); + + await browser.url(PAGE_URL, { wait: 'networkIdle' }); + await expect(await browser.getTitle()).toBe('hello world'); + }); + } + it('removes custom filters in settings page', async function () { await setCustomFilters([]); await setPrivacyToggle('custom-filters', false);