Add ability for scriptlets to share local data

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/1741

As a result of the new capability, usage of RegExp API in `aost`
scriptlet has been shielded from the webpage tampering with the
API.
This commit is contained in:
Raymond Hill 2023-03-26 12:31:36 -04:00
parent c7c748862e
commit 5c9c87e485
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
2 changed files with 37 additions and 14 deletions

View File

@ -22,6 +22,9 @@
web page context. web page context.
*/ */
// Externally added to the private namespace in which scriptlets execute.
/* global sriptletGlobals */
'use strict'; 'use strict';
export const builtinScriptlets = []; export const builtinScriptlets = [];
@ -34,6 +37,25 @@ export const builtinScriptlets = [];
*******************************************************************************/ *******************************************************************************/
builtinScriptlets.push({
name: 'safe-self.fn',
fn: safeSelf,
});
function safeSelf() {
if ( sriptletGlobals.has('safeSelf') ) {
return sriptletGlobals.get('safeSelf');
}
const safe = {
'RegExp': self.RegExp,
'RegExp_test': self.RegExp.prototype.test,
'RegExp_exec': self.RegExp.prototype.exec,
};
sriptletGlobals.set('safeSelf', safe);
return safe;
}
/******************************************************************************/
builtinScriptlets.push({ builtinScriptlets.push({
name: 'pattern-to-regex.fn', name: 'pattern-to-regex.fn',
fn: patternToRegex, fn: patternToRegex,
@ -257,6 +279,7 @@ builtinScriptlets.push({
aliases: [ 'aost.js' ], aliases: [ 'aost.js' ],
fn: abortOnStackTrace, fn: abortOnStackTrace,
dependencies: [ dependencies: [
'safe-self.fn',
'pattern-to-regex.fn', 'pattern-to-regex.fn',
'get-exception-token.fn', 'get-exception-token.fn',
], ],
@ -268,6 +291,7 @@ function abortOnStackTrace(
logLevel = '' logLevel = ''
) { ) {
if ( typeof chain !== 'string' ) { return; } if ( typeof chain !== 'string' ) { return; }
const safe = safeSelf();
const reNeedle = patternToRegex(needle); const reNeedle = patternToRegex(needle);
const exceptionToken = getExceptionToken(); const exceptionToken = getExceptionToken();
const log = console.log.bind(console); const log = console.log.bind(console);
@ -279,11 +303,12 @@ function abortOnStackTrace(
docURL = docURL.slice(0, pos); docURL = docURL.slice(0, pos);
} }
// Normalize stack trace // Normalize stack trace
const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/;
const lines = []; const lines = [];
for ( let line of err.stack.split(/[\n\r]+/) ) { for ( let line of err.stack.split(/[\n\r]+/) ) {
if ( line.includes(exceptionToken) ) { continue; } if ( line.includes(exceptionToken) ) { continue; }
line = line.trim(); line = line.trim();
let match = /(.*?@)?(\S+)(:\d+):\d+\)?$/.exec(line); let match = safe.RegExp_exec.call(reLine, line);
if ( match === null ) { continue; } if ( match === null ) { continue; }
let url = match[2]; let url = match[2];
if ( url.startsWith('(') ) { url = url.slice(1); } if ( url.startsWith('(') ) { url = url.slice(1); }
@ -301,7 +326,7 @@ function abortOnStackTrace(
} }
lines[0] = `stackDepth:${lines.length-1}`; lines[0] = `stackDepth:${lines.length-1}`;
const stack = lines.join('\t'); const stack = lines.join('\t');
const r = reNeedle.test(stack); const r = safe.RegExp_test.call(reNeedle, stack);
if ( if (
logLevel === '1' || logLevel === '1' ||
logLevel === '2' && r || logLevel === '2' && r ||

View File

@ -379,22 +379,20 @@ scriptletFilteringEngine.retrieve = function(request, options = {}) {
if ( cacheDetails.code === '' ) { return; } if ( cacheDetails.code === '' ) { return; }
const out = [ cacheDetails.code ]; const out = [
if ( µb.hiddenSettings.debugScriptlets ) {
out.unshift('debugger;');
}
out.unshift(
'(function() {', '(function() {',
'// >>>> start of private namespace', '// >>>> start of private namespace',
'' '',
); µb.hiddenSettings.debugScriptlets ? 'debugger;' : ';',
out.push( '',
// For use by scriptlets to share local data among themselves
'const sriptletGlobals = new Map();',
'',
cacheDetails.code,
'', '',
'// <<<< end of private namespace', '// <<<< end of private namespace',
'})();' '})();',
); ];
return out.join('\n'); return out.join('\n');
}; };