Add `stackNeedle` argument to `json-prune` scriptlet

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/2757
This commit is contained in:
Raymond Hill 2023-07-27 09:41:56 -04:00
parent 6d7674e69f
commit 4649ae4d78
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
1 changed files with 74 additions and 53 deletions

View File

@ -46,6 +46,7 @@ function safeSelf() {
return scriptletGlobals.get('safeSelf'); return scriptletGlobals.get('safeSelf');
} }
const safe = { const safe = {
'Error': self.Error,
'Object_defineProperty': Object.defineProperty.bind(Object), 'Object_defineProperty': Object.defineProperty.bind(Object),
'RegExp': self.RegExp, 'RegExp': self.RegExp,
'RegExp_test': self.RegExp.prototype.test, 'RegExp_test': self.RegExp.prototype.test,
@ -620,6 +621,7 @@ builtinScriptlets.push({
name: 'object-prune.fn', name: 'object-prune.fn',
fn: objectPrune, fn: objectPrune,
dependencies: [ dependencies: [
'matches-stack-trace.fn',
'pattern-to-regex.fn', 'pattern-to-regex.fn',
], ],
}); });
@ -632,9 +634,10 @@ builtinScriptlets.push({
function objectPrune( function objectPrune(
obj, obj,
rawPrunePaths, rawPrunePaths,
rawNeedlePaths rawNeedlePaths,
stackNeedle = ''
) { ) {
if ( typeof rawPrunePaths !== 'string' ) { return; } if ( typeof rawPrunePaths !== 'string' ) { return obj; }
const prunePaths = rawPrunePaths !== '' const prunePaths = rawPrunePaths !== ''
? rawPrunePaths.split(/ +/) ? rawPrunePaths.split(/ +/)
: []; : [];
@ -648,6 +651,11 @@ function objectPrune(
log = console.log.bind(console); log = console.log.bind(console);
reLogNeedle = patternToRegex(rawNeedlePaths); reLogNeedle = patternToRegex(rawNeedlePaths);
} }
if ( stackNeedle !== '' ) {
if ( matchesStackTrace(patternToRegex(stackNeedle), log ? '1' : 0) === false ) {
return obj;
}
}
const findOwner = function(root, path, prune = false) { const findOwner = function(root, path, prune = false) {
let owner = root; let owner = root;
let chain = path; let chain = path;
@ -802,6 +810,60 @@ function setLocalStorageItemCore(
} }
} }
/******************************************************************************/
builtinScriptlets.push({
name: 'matches-stack-trace.fn',
fn: matchesStackTrace,
dependencies: [
'safe-self.fn',
],
});
function matchesStackTrace(
reNeedle,
logLevel = 0
) {
if ( reNeedle === undefined ) { return false; }
const safe = safeSelf();
const exceptionToken = getExceptionToken();
const error = new safe.Error(exceptionToken);
const docURL = new URL(self.location.href);
docURL.hash = '';
// Normalize stack trace
const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/;
const lines = [];
for ( let line of error.stack.split(/[\n\r]+/) ) {
if ( line.includes(exceptionToken) ) { continue; }
line = line.trim();
const match = safe.RegExp_exec.call(reLine, line);
if ( match === null ) { continue; }
let url = match[2];
if ( url.startsWith('(') ) { url = url.slice(1); }
if ( url === docURL.href ) {
url = 'inlineScript';
} else if ( url.startsWith('<anonymous>') ) {
url = 'injectedScript';
}
let fn = match[1] !== undefined
? match[1].slice(0, -1)
: line.slice(0, match.index).trim();
if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); }
let rowcol = match[3];
lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim());
}
lines[0] = `stackDepth:${lines.length-1}`;
const stack = lines.join('\t');
const r = safe.RegExp_test.call(reNeedle, stack);
if (
logLevel === '1' ||
logLevel === '2' && r ||
logLevel === '3' && !r
) {
safe.uboLog(stack.replace(/\t/g, '\n'));
}
return r;
}
/******************************************************************************* /*******************************************************************************
Injectable scriptlets Injectable scriptlets
@ -931,8 +993,8 @@ builtinScriptlets.push({
fn: abortOnStackTrace, fn: abortOnStackTrace,
dependencies: [ dependencies: [
'get-exception-token.fn', 'get-exception-token.fn',
'matches-stack-trace.fn',
'pattern-to-regex.fn', 'pattern-to-regex.fn',
'safe-self.fn',
], ],
}); });
// Status is currently experimental // Status is currently experimental
@ -942,64 +1004,21 @@ 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 ErrorCtor = self.Error;
const mustAbort = function(err) {
let docURL = self.location.href;
const pos = docURL.indexOf('#');
if ( pos !== -1 ) {
docURL = docURL.slice(0, pos);
}
// Normalize stack trace
const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/;
const lines = [];
for ( let line of err.stack.split(/[\n\r]+/) ) {
if ( line.includes(exceptionToken) ) { continue; }
line = line.trim();
const match = safe.RegExp_exec.call(reLine, line);
if ( match === null ) { continue; }
let url = match[2];
if ( url.startsWith('(') ) { url = url.slice(1); }
if ( url === docURL ) {
url = 'inlineScript';
} else if ( url.startsWith('<anonymous>') ) {
url = 'injectedScript';
}
let fn = match[1] !== undefined
? match[1].slice(0, -1)
: line.slice(0, match.index).trim();
if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); }
let rowcol = match[3];
lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim());
}
lines[0] = `stackDepth:${lines.length-1}`;
const stack = lines.join('\t');
const r = safe.RegExp_test.call(reNeedle, stack);
if (
logLevel === '1' ||
logLevel === '2' && r ||
logLevel === '3' && !r
) {
safe.uboLog(stack.replace(/\t/g, '\n'));
}
return r;
};
const makeProxy = function(owner, chain) { const makeProxy = function(owner, chain) {
const pos = chain.indexOf('.'); const pos = chain.indexOf('.');
if ( pos === -1 ) { if ( pos === -1 ) {
let v = owner[chain]; let v = owner[chain];
Object.defineProperty(owner, chain, { Object.defineProperty(owner, chain, {
get: function() { get: function() {
if ( mustAbort(new ErrorCtor(exceptionToken)) ) { if ( matchesStackTrace(reNeedle, logLevel) ) {
throw new ReferenceError(exceptionToken); throw new ReferenceError(getExceptionToken());
} }
return v; return v;
}, },
set: function(a) { set: function(a) {
if ( mustAbort(new ErrorCtor(exceptionToken)) ) { if ( matchesStackTrace(reNeedle, logLevel) ) {
throw new ReferenceError(exceptionToken); throw new ReferenceError(getExceptionToken());
} }
v = a; v = a;
}, },
@ -1114,21 +1133,23 @@ builtinScriptlets.push({
// - Add support for "remove everything if needle matches" case // - Add support for "remove everything if needle matches" case
function jsonPrune( function jsonPrune(
rawPrunePaths = '', rawPrunePaths = '',
rawNeedlePaths = '' rawNeedlePaths = '',
stackNeedle = ''
) { ) {
JSON.parse = new Proxy(JSON.parse, { JSON.parse = new Proxy(JSON.parse, {
apply: function(target, thisArg, args) { apply: function(target, thisArg, args) {
return objectPrune( return objectPrune(
Reflect.apply(target, thisArg, args), Reflect.apply(target, thisArg, args),
rawPrunePaths, rawPrunePaths,
rawNeedlePaths rawNeedlePaths,
stackNeedle
); );
}, },
}); });
Response.prototype.json = new Proxy(Response.prototype.json, { Response.prototype.json = new Proxy(Response.prototype.json, {
apply: function(target, thisArg, args) { apply: function(target, thisArg, args) {
return Reflect.apply(target, thisArg, args).then(o => return Reflect.apply(target, thisArg, args).then(o =>
objectPrune(o, rawPrunePaths, rawNeedlePaths) objectPrune(o, rawPrunePaths, rawNeedlePaths, stackNeedle)
); );
}, },
}); });