Skip to content

Commit 4649ae4

Browse files
committed
Add stackNeedle argument to json-prune scriptlet
Related issue: - uBlockOrigin/uBlock-issues#2757
1 parent 6d7674e commit 4649ae4

File tree

1 file changed

+74
-53
lines changed

1 file changed

+74
-53
lines changed

assets/resources/scriptlets.js

Lines changed: 74 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ function safeSelf() {
4646
return scriptletGlobals.get('safeSelf');
4747
}
4848
const safe = {
49+
'Error': self.Error,
4950
'Object_defineProperty': Object.defineProperty.bind(Object),
5051
'RegExp': self.RegExp,
5152
'RegExp_test': self.RegExp.prototype.test,
@@ -620,6 +621,7 @@ builtinScriptlets.push({
620621
name: 'object-prune.fn',
621622
fn: objectPrune,
622623
dependencies: [
624+
'matches-stack-trace.fn',
623625
'pattern-to-regex.fn',
624626
],
625627
});
@@ -632,9 +634,10 @@ builtinScriptlets.push({
632634
function objectPrune(
633635
obj,
634636
rawPrunePaths,
635-
rawNeedlePaths
637+
rawNeedlePaths,
638+
stackNeedle = ''
636639
) {
637-
if ( typeof rawPrunePaths !== 'string' ) { return; }
640+
if ( typeof rawPrunePaths !== 'string' ) { return obj; }
638641
const prunePaths = rawPrunePaths !== ''
639642
? rawPrunePaths.split(/ +/)
640643
: [];
@@ -648,6 +651,11 @@ function objectPrune(
648651
log = console.log.bind(console);
649652
reLogNeedle = patternToRegex(rawNeedlePaths);
650653
}
654+
if ( stackNeedle !== '' ) {
655+
if ( matchesStackTrace(patternToRegex(stackNeedle), log ? '1' : 0) === false ) {
656+
return obj;
657+
}
658+
}
651659
const findOwner = function(root, path, prune = false) {
652660
let owner = root;
653661
let chain = path;
@@ -802,6 +810,60 @@ function setLocalStorageItemCore(
802810
}
803811
}
804812

813+
/******************************************************************************/
814+
815+
builtinScriptlets.push({
816+
name: 'matches-stack-trace.fn',
817+
fn: matchesStackTrace,
818+
dependencies: [
819+
'safe-self.fn',
820+
],
821+
});
822+
function matchesStackTrace(
823+
reNeedle,
824+
logLevel = 0
825+
) {
826+
if ( reNeedle === undefined ) { return false; }
827+
const safe = safeSelf();
828+
const exceptionToken = getExceptionToken();
829+
const error = new safe.Error(exceptionToken);
830+
const docURL = new URL(self.location.href);
831+
docURL.hash = '';
832+
// Normalize stack trace
833+
const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/;
834+
const lines = [];
835+
for ( let line of error.stack.split(/[\n\r]+/) ) {
836+
if ( line.includes(exceptionToken) ) { continue; }
837+
line = line.trim();
838+
const match = safe.RegExp_exec.call(reLine, line);
839+
if ( match === null ) { continue; }
840+
let url = match[2];
841+
if ( url.startsWith('(') ) { url = url.slice(1); }
842+
if ( url === docURL.href ) {
843+
url = 'inlineScript';
844+
} else if ( url.startsWith('<anonymous>') ) {
845+
url = 'injectedScript';
846+
}
847+
let fn = match[1] !== undefined
848+
? match[1].slice(0, -1)
849+
: line.slice(0, match.index).trim();
850+
if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); }
851+
let rowcol = match[3];
852+
lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim());
853+
}
854+
lines[0] = `stackDepth:${lines.length-1}`;
855+
const stack = lines.join('\t');
856+
const r = safe.RegExp_test.call(reNeedle, stack);
857+
if (
858+
logLevel === '1' ||
859+
logLevel === '2' && r ||
860+
logLevel === '3' && !r
861+
) {
862+
safe.uboLog(stack.replace(/\t/g, '\n'));
863+
}
864+
return r;
865+
}
866+
805867
/*******************************************************************************
806868
807869
Injectable scriptlets
@@ -931,8 +993,8 @@ builtinScriptlets.push({
931993
fn: abortOnStackTrace,
932994
dependencies: [
933995
'get-exception-token.fn',
996+
'matches-stack-trace.fn',
934997
'pattern-to-regex.fn',
935-
'safe-self.fn',
936998
],
937999
});
9381000
// Status is currently experimental
@@ -942,64 +1004,21 @@ function abortOnStackTrace(
9421004
logLevel = ''
9431005
) {
9441006
if ( typeof chain !== 'string' ) { return; }
945-
const safe = safeSelf();
9461007
const reNeedle = patternToRegex(needle);
947-
const exceptionToken = getExceptionToken();
948-
const ErrorCtor = self.Error;
949-
const mustAbort = function(err) {
950-
let docURL = self.location.href;
951-
const pos = docURL.indexOf('#');
952-
if ( pos !== -1 ) {
953-
docURL = docURL.slice(0, pos);
954-
}
955-
// Normalize stack trace
956-
const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/;
957-
const lines = [];
958-
for ( let line of err.stack.split(/[\n\r]+/) ) {
959-
if ( line.includes(exceptionToken) ) { continue; }
960-
line = line.trim();
961-
const match = safe.RegExp_exec.call(reLine, line);
962-
if ( match === null ) { continue; }
963-
let url = match[2];
964-
if ( url.startsWith('(') ) { url = url.slice(1); }
965-
if ( url === docURL ) {
966-
url = 'inlineScript';
967-
} else if ( url.startsWith('<anonymous>') ) {
968-
url = 'injectedScript';
969-
}
970-
let fn = match[1] !== undefined
971-
? match[1].slice(0, -1)
972-
: line.slice(0, match.index).trim();
973-
if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); }
974-
let rowcol = match[3];
975-
lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim());
976-
}
977-
lines[0] = `stackDepth:${lines.length-1}`;
978-
const stack = lines.join('\t');
979-
const r = safe.RegExp_test.call(reNeedle, stack);
980-
if (
981-
logLevel === '1' ||
982-
logLevel === '2' && r ||
983-
logLevel === '3' && !r
984-
) {
985-
safe.uboLog(stack.replace(/\t/g, '\n'));
986-
}
987-
return r;
988-
};
9891008
const makeProxy = function(owner, chain) {
9901009
const pos = chain.indexOf('.');
9911010
if ( pos === -1 ) {
9921011
let v = owner[chain];
9931012
Object.defineProperty(owner, chain, {
9941013
get: function() {
995-
if ( mustAbort(new ErrorCtor(exceptionToken)) ) {
996-
throw new ReferenceError(exceptionToken);
1014+
if ( matchesStackTrace(reNeedle, logLevel) ) {
1015+
throw new ReferenceError(getExceptionToken());
9971016
}
9981017
return v;
9991018
},
10001019
set: function(a) {
1001-
if ( mustAbort(new ErrorCtor(exceptionToken)) ) {
1002-
throw new ReferenceError(exceptionToken);
1020+
if ( matchesStackTrace(reNeedle, logLevel) ) {
1021+
throw new ReferenceError(getExceptionToken());
10031022
}
10041023
v = a;
10051024
},
@@ -1114,21 +1133,23 @@ builtinScriptlets.push({
11141133
// - Add support for "remove everything if needle matches" case
11151134
function jsonPrune(
11161135
rawPrunePaths = '',
1117-
rawNeedlePaths = ''
1136+
rawNeedlePaths = '',
1137+
stackNeedle = ''
11181138
) {
11191139
JSON.parse = new Proxy(JSON.parse, {
11201140
apply: function(target, thisArg, args) {
11211141
return objectPrune(
11221142
Reflect.apply(target, thisArg, args),
11231143
rawPrunePaths,
1124-
rawNeedlePaths
1144+
rawNeedlePaths,
1145+
stackNeedle
11251146
);
11261147
},
11271148
});
11281149
Response.prototype.json = new Proxy(Response.prototype.json, {
11291150
apply: function(target, thisArg, args) {
11301151
return Reflect.apply(target, thisArg, args).then(o =>
1131-
objectPrune(o, rawPrunePaths, rawNeedlePaths)
1152+
objectPrune(o, rawPrunePaths, rawNeedlePaths, stackNeedle)
11321153
);
11331154
},
11341155
});

0 commit comments

Comments
 (0)