From 7ba85472d7dcdaaf3ee0deeec357209cb1e16393 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 23 Jun 2023 08:27:07 -0400 Subject: [PATCH] [mv3] Properly distinguish trust level when compiling scriptlets Since in uBOL filter lists from various sources are combined into a single list, there must be a way to turn on/off trust level inside the resulting combined filter list so as to be able to validate the trust level of filters requiring trust. This commit adds new parser directives understood only by MV3 compiler to turn on/off trust flag internally. --- platform/mv3/make-rulesets.js | 18 ++++++++++++++---- platform/mv3/make-scriptlets.js | 4 ++-- src/js/static-dnr-filtering.js | 12 ++++++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 7f603d9d8..611d80245 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -27,7 +27,7 @@ import fs from 'fs/promises'; import https from 'https'; import path from 'path'; import process from 'process'; -import { createHash } from 'crypto'; +import { createHash, randomBytes } from 'crypto'; import redirectResourcesMap from './js/redirect-resources.js'; import { dnrRulesetFromRawLists } from './js/static-dnr-filtering.js'; import * as sfp from './js/static-filtering-parser.js'; @@ -182,6 +182,9 @@ async function fetchAsset(assetDetails) { continue; } fetchedURLs.add(part.url); + if ( part.url.startsWith('https://ublockorigin.github.io/uAssets/filters/') ) { + newParts.push(`!#trusted on ${assetDetails.secret}`); + } newParts.push( fetchList(part.url, cacheDir).then(details => { const { url } = details; @@ -198,6 +201,7 @@ async function fetchAsset(assetDetails) { return { url, content: '' }; }) ); + newParts.push(`!#trusted off ${assetDetails.secret}`); } parts = await Promise.all(newParts); parts = sfp.utils.preparser.expandIncludes(parts, env); @@ -808,7 +812,7 @@ async function processScriptletFilters(assetDetails, mapin) { makeScriptlet.init(); for ( const details of mapin.values() ) { - makeScriptlet.compile(details, assetDetails.isTrusted); + makeScriptlet.compile(details); } const stats = await makeScriptlet.commit( assetDetails.id, @@ -851,7 +855,7 @@ async function rulesetFromURLs(assetDetails) { const results = await dnrRulesetFromRawLists( [ { name: assetDetails.id, text: assetDetails.text } ], - { env, extensionPaths } + { env, extensionPaths, secret: assetDetails.secret } ); const netStats = await processNetworkFilters( @@ -975,6 +979,10 @@ async function main() { JSON.parse(text) ); + // This will be used to sign our inserted `!#trusted on` directives + const secret = createHash('sha256').update(randomBytes(16)).digest('hex').slice(0,16); + log(`Secret: ${secret}`); + // Assemble all default lists as the default ruleset const contentURLs = [ 'https://ublockorigin.github.io/uAssets/filters/filters.min.txt', @@ -991,9 +999,9 @@ async function main() { id: 'default', name: 'Ads, trackers, miners, and more' , enabled: true, + secret, urls: contentURLs, homeURL: 'https://github.com/uBlockOrigin/uAssets', - isTrusted: true, }); // Regional rulesets @@ -1063,6 +1071,7 @@ async function main() { name: 'EasyList/uBO – Cookie Notices', group: 'annoyances', enabled: false, + secret, urls: [ 'https://ublockorigin.github.io/uAssets/thirdparties/easylist-cookies.txt', 'https://ublockorigin.github.io/uAssets/filters/annoyances-cookies.txt', @@ -1074,6 +1083,7 @@ async function main() { name: 'AdGuard/uBO – Overlays', group: 'annoyances', enabled: false, + secret, urls: [ 'https://filters.adtidy.org/extension/ublock/filters/19.txt', 'https://ublockorigin.github.io/uAssets/filters/annoyances-others.txt', diff --git a/platform/mv3/make-scriptlets.js b/platform/mv3/make-scriptlets.js index 7dfeb89b6..63cf9f27f 100644 --- a/platform/mv3/make-scriptlets.js +++ b/platform/mv3/make-scriptlets.js @@ -78,7 +78,7 @@ export function reset() { /******************************************************************************/ -export function compile(details, isTrusted) { +export function compile(details) { if ( details.args[0].endsWith('.js') === false ) { details.args[0] += '.js'; } @@ -88,7 +88,7 @@ export function compile(details, isTrusted) { const scriptletToken = details.args[0]; const resourceEntry = resourceDetails.get(scriptletToken); if ( resourceEntry === undefined ) { return; } - if ( resourceEntry.requiresTrust && isTrusted !== true ) { + if ( resourceEntry.requiresTrust && details.isTrusted !== true ) { console.log(`Rejecting ${scriptletToken}: source is not trusted`); return; } diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index 27194c6dc..2ccd2249f 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -122,6 +122,9 @@ function addExtendedToDNR(context, parser) { let details = context.scriptletFilters.get(argsToken); if ( details === undefined ) { context.scriptletFilters.set(argsToken, details = { args }); + if ( context.isTrusted ) { + details.isTrusted = true; + } } if ( not ) { if ( details.excludeMatches === undefined ) { @@ -247,6 +250,15 @@ function addToDNR(context, list) { parser.parse(line); + if ( parser.isComment() ) { + if ( line === `!#trusted on ${context.secret}` ) { + context.isTrusted = true; + } else if ( line === `!#trusted off ${context.secret}` ) { + context.isTrusted = false; + } + continue; + } + if ( parser.isFilter() === false ) { continue; } if ( parser.hasError() ) { continue; }