mirror of https://github.com/gorhill/uBlock.git
Support negated pattern for stack test in scriptlets
Prepend pattern with `!` to test for unmatched patterns in stack trace. This applies to sctiplet parameters which purpose is to test against the stack, i.e. `aost` and `json-prune`. Additionally, dropped support for JSON notation in favor of optional variable arguments notation. Related discussion: - https://github.com/uBlockOrigin/uBlock-discussions/discussions/789#discussioncomment-6520330
This commit is contained in:
parent
bb7779ba75
commit
84cc69aa10
|
@ -59,6 +59,36 @@ function safeSelf() {
|
||||||
if ( `${args[0]}` === '' ) { return; }
|
if ( `${args[0]}` === '' ) { return; }
|
||||||
this.log('[uBO]', ...args);
|
this.log('[uBO]', ...args);
|
||||||
},
|
},
|
||||||
|
'initPattern': function(pattern, options = {}) {
|
||||||
|
if ( pattern === '' ) {
|
||||||
|
return { matchAll: true };
|
||||||
|
}
|
||||||
|
const expect = (options.canNegate && pattern.startsWith('!') === false);
|
||||||
|
if ( expect === false ) {
|
||||||
|
pattern = pattern.slice(1);
|
||||||
|
}
|
||||||
|
const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern);
|
||||||
|
if ( match !== null ) {
|
||||||
|
return {
|
||||||
|
re: new this.RegExp(
|
||||||
|
match[1],
|
||||||
|
match[2] || options.flags
|
||||||
|
),
|
||||||
|
expect,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
re: new this.RegExp(pattern.replace(
|
||||||
|
/[.*+?^${}()|[\]\\]/g, '\\$&'),
|
||||||
|
options.flags
|
||||||
|
),
|
||||||
|
expect,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
'testPattern': function(details, haystack) {
|
||||||
|
if ( details.matchAll ) { return true; }
|
||||||
|
return this.RegExp_test.call(details.re, haystack) === details.expect;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
scriptletGlobals.set('safeSelf', safe);
|
scriptletGlobals.set('safeSelf', safe);
|
||||||
return safe;
|
return safe;
|
||||||
|
@ -636,7 +666,7 @@ function objectPrune(
|
||||||
obj,
|
obj,
|
||||||
rawPrunePaths,
|
rawPrunePaths,
|
||||||
rawNeedlePaths,
|
rawNeedlePaths,
|
||||||
stackNeedle = ''
|
stackNeedleDetails = { matchAll: true }
|
||||||
) {
|
) {
|
||||||
if ( typeof rawPrunePaths !== 'string' ) { return obj; }
|
if ( typeof rawPrunePaths !== 'string' ) { return obj; }
|
||||||
const prunePaths = rawPrunePaths !== ''
|
const prunePaths = rawPrunePaths !== ''
|
||||||
|
@ -657,9 +687,8 @@ function objectPrune(
|
||||||
log = console.log.bind(console);
|
log = console.log.bind(console);
|
||||||
reLogNeedle = patternToRegex(rawNeedlePaths);
|
reLogNeedle = patternToRegex(rawNeedlePaths);
|
||||||
}
|
}
|
||||||
if ( stackNeedle !== '' ) {
|
if ( stackNeedleDetails.matchAll !== true ) {
|
||||||
const reStackNeedle = patternToRegex(stackNeedle);
|
if ( matchesStackTrace(stackNeedleDetails, extraArgs.logstack) === false ) {
|
||||||
if ( matchesStackTrace(reStackNeedle, extraArgs.logstack) === false ) {
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -828,10 +857,9 @@ builtinScriptlets.push({
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
function matchesStackTrace(
|
function matchesStackTrace(
|
||||||
reNeedle,
|
needleDetails,
|
||||||
logLevel = 0
|
logLevel = 0
|
||||||
) {
|
) {
|
||||||
if ( reNeedle === undefined ) { return false; }
|
|
||||||
const safe = safeSelf();
|
const safe = safeSelf();
|
||||||
const exceptionToken = getExceptionToken();
|
const exceptionToken = getExceptionToken();
|
||||||
const error = new safe.Error(exceptionToken);
|
const error = new safe.Error(exceptionToken);
|
||||||
|
@ -861,7 +889,7 @@ function matchesStackTrace(
|
||||||
}
|
}
|
||||||
lines[0] = `stackDepth:${lines.length-1}`;
|
lines[0] = `stackDepth:${lines.length-1}`;
|
||||||
const stack = lines.join('\t');
|
const stack = lines.join('\t');
|
||||||
const r = safe.RegExp_test.call(reNeedle, stack);
|
const r = safe.testPattern(needleDetails, stack);
|
||||||
if (
|
if (
|
||||||
logLevel === 1 ||
|
logLevel === 1 ||
|
||||||
logLevel === 2 && r ||
|
logLevel === 2 && r ||
|
||||||
|
@ -1004,15 +1032,16 @@ builtinScriptlets.push({
|
||||||
'get-extra-args.fn',
|
'get-extra-args.fn',
|
||||||
'matches-stack-trace.fn',
|
'matches-stack-trace.fn',
|
||||||
'pattern-to-regex.fn',
|
'pattern-to-regex.fn',
|
||||||
|
'safe-self.fn',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
// Status is currently experimental
|
|
||||||
function abortOnStackTrace(
|
function abortOnStackTrace(
|
||||||
chain = '',
|
chain = '',
|
||||||
needle = ''
|
needle = ''
|
||||||
) {
|
) {
|
||||||
if ( typeof chain !== 'string' ) { return; }
|
if ( typeof chain !== 'string' ) { return; }
|
||||||
const reNeedle = patternToRegex(needle);
|
const safe = safeSelf();
|
||||||
|
const needleDetails = safe.initPattern(needle, { canNegate: true });
|
||||||
const extraArgs = getExtraArgs(Array.from(arguments), 2);
|
const extraArgs = getExtraArgs(Array.from(arguments), 2);
|
||||||
const makeProxy = function(owner, chain) {
|
const makeProxy = function(owner, chain) {
|
||||||
const pos = chain.indexOf('.');
|
const pos = chain.indexOf('.');
|
||||||
|
@ -1020,13 +1049,13 @@ function abortOnStackTrace(
|
||||||
let v = owner[chain];
|
let v = owner[chain];
|
||||||
Object.defineProperty(owner, chain, {
|
Object.defineProperty(owner, chain, {
|
||||||
get: function() {
|
get: function() {
|
||||||
if ( matchesStackTrace(reNeedle, extraArgs.log) ) {
|
if ( matchesStackTrace(needleDetails, extraArgs.log) ) {
|
||||||
throw new ReferenceError(getExceptionToken());
|
throw new ReferenceError(getExceptionToken());
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
},
|
},
|
||||||
set: function(a) {
|
set: function(a) {
|
||||||
if ( matchesStackTrace(reNeedle, extraArgs.log) ) {
|
if ( matchesStackTrace(needleDetails, extraArgs.log) ) {
|
||||||
throw new ReferenceError(getExceptionToken());
|
throw new ReferenceError(getExceptionToken());
|
||||||
}
|
}
|
||||||
v = a;
|
v = a;
|
||||||
|
@ -1132,6 +1161,7 @@ builtinScriptlets.push({
|
||||||
fn: jsonPrune,
|
fn: jsonPrune,
|
||||||
dependencies: [
|
dependencies: [
|
||||||
'object-prune.fn',
|
'object-prune.fn',
|
||||||
|
'safe-self.fn',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
// When no "prune paths" argument is provided, the scriptlet is
|
// When no "prune paths" argument is provided, the scriptlet is
|
||||||
|
@ -1145,6 +1175,8 @@ function jsonPrune(
|
||||||
rawNeedlePaths = '',
|
rawNeedlePaths = '',
|
||||||
stackNeedle = ''
|
stackNeedle = ''
|
||||||
) {
|
) {
|
||||||
|
const safe = safeSelf();
|
||||||
|
const stackNeedleDetails = safe.initPattern(stackNeedle, { canNegate: true });
|
||||||
const extraArgs = Array.from(arguments).slice(3);
|
const extraArgs = Array.from(arguments).slice(3);
|
||||||
JSON.parse = new Proxy(JSON.parse, {
|
JSON.parse = new Proxy(JSON.parse, {
|
||||||
apply: function(target, thisArg, args) {
|
apply: function(target, thisArg, args) {
|
||||||
|
@ -1152,7 +1184,7 @@ function jsonPrune(
|
||||||
Reflect.apply(target, thisArg, args),
|
Reflect.apply(target, thisArg, args),
|
||||||
rawPrunePaths,
|
rawPrunePaths,
|
||||||
rawNeedlePaths,
|
rawNeedlePaths,
|
||||||
stackNeedle,
|
stackNeedleDetails,
|
||||||
...extraArgs
|
...extraArgs
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1164,7 +1196,7 @@ function jsonPrune(
|
||||||
o,
|
o,
|
||||||
rawPrunePaths,
|
rawPrunePaths,
|
||||||
rawNeedlePaths,
|
rawNeedlePaths,
|
||||||
stackNeedle,
|
stackNeedleDetails,
|
||||||
...extraArgs
|
...extraArgs
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -41,7 +41,6 @@ const VERSION = 1;
|
||||||
|
|
||||||
const duplicates = new Set();
|
const duplicates = new Set();
|
||||||
const scriptletCache = new µb.MRUCache(32);
|
const scriptletCache = new µb.MRUCache(32);
|
||||||
const reEscapeScriptArg = /[\\'"]/g;
|
|
||||||
|
|
||||||
const scriptletDB = new StaticExtFilteringHostnameDB(1, VERSION);
|
const scriptletDB = new StaticExtFilteringHostnameDB(1, VERSION);
|
||||||
|
|
||||||
|
@ -206,21 +205,11 @@ const patchScriptlet = function(content, arglist) {
|
||||||
if ( content.startsWith('function') && content.endsWith('}') ) {
|
if ( content.startsWith('function') && content.endsWith('}') ) {
|
||||||
content = `(${content})({{args}});`;
|
content = `(${content})({{args}});`;
|
||||||
}
|
}
|
||||||
if ( arglist.length === 0 ) {
|
|
||||||
return content.replace('{{args}}', '');
|
|
||||||
}
|
|
||||||
if ( arglist.length === 1 ) {
|
|
||||||
if ( arglist[0].startsWith('{') && arglist[0].endsWith('}') ) {
|
|
||||||
return content.replace('{{args}}', arglist[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for ( let i = 0; i < arglist.length; i++ ) {
|
for ( let i = 0; i < arglist.length; i++ ) {
|
||||||
content = content.replace(`{{${i+1}}}`, arglist[i]);
|
content = content.replace(`{{${i+1}}}`, arglist[i]);
|
||||||
}
|
}
|
||||||
return content.replace('{{args}}',
|
return content.replace('{{args}}',
|
||||||
arglist.map(a => `'${a.replace(reEscapeScriptArg, '\\$&')}'`)
|
JSON.stringify(arglist).slice(1,-1).replace(/\$/g, '$$$')
|
||||||
.join(', ')
|
|
||||||
.replace(/\$/g, '$$$')
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2266,27 +2266,9 @@ export class AstFilterParser {
|
||||||
const parentEnd = this.nodes[parent+NODE_END_INDEX];
|
const parentEnd = this.nodes[parent+NODE_END_INDEX];
|
||||||
if ( parentEnd === parentBeg ) { return 0; }
|
if ( parentEnd === parentBeg ) { return 0; }
|
||||||
const s = this.getNodeString(parent);
|
const s = this.getNodeString(parent);
|
||||||
let next = 0;
|
|
||||||
// json-based arg?
|
|
||||||
const match = this.rePatternScriptletJsonArgs.exec(s);
|
|
||||||
if ( match !== null ) {
|
|
||||||
next = this.allocTypedNode(
|
|
||||||
NODE_TYPE_EXT_PATTERN_SCRIPTLET_ARG,
|
|
||||||
parentBeg,
|
|
||||||
parentEnd
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
void JSON.parse(s);
|
|
||||||
} catch(ex) {
|
|
||||||
this.addNodeFlags(next, NODE_FLAG_ERROR);
|
|
||||||
this.addFlags(AST_FLAG_HAS_ERROR);
|
|
||||||
}
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
// positional args
|
|
||||||
const head = this.allocHeadNode();
|
|
||||||
const argsEnd = s.length;
|
const argsEnd = s.length;
|
||||||
let prev = head;
|
const head = this.allocHeadNode();
|
||||||
|
let prev = head, next = 0;
|
||||||
let decorationBeg = 0;
|
let decorationBeg = 0;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
Loading…
Reference in New Issue