Skip to content

Commit b735ac6

Browse files
committed
Add abort-on-stack-trace scriptlet
This new scriplet has become necessary as a countermeasure to new bypass mechanisms by some websites, as discussed with filter list maintainers. Also related discussion: AdguardTeam/Scriptlets#82 Scriptlet: abort-on-stack-trace Alias: aost Argument 1: The property to trap in order to launch the stack trace matching code, ex. Math.random Argument 2: The string (needle) to match against the stack trace. If the empty string, always match. There is a special string which can be used to match inline script context, <inline-script>. Argument 3: Whether to log, and if so how: Empty string: do not log 1: log stack trace for all access to trapped property 2: log stack trace for defused access to trapped property 3: log stack trace for non-defused access to trapped property
1 parent 45bd8f5 commit b735ac6

File tree

1 file changed

+95
-0
lines changed

1 file changed

+95
-0
lines changed

assets/resources/scriptlets.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,101 @@
191191
})();
192192

193193

194+
/// abort-on-stack-trace.js
195+
/// alias aost.js
196+
// Status is currently experimental
197+
(function() {
198+
let chain = '{{1}}';
199+
let needle = '{{2}}';
200+
let logLevel = '{{3}}';
201+
const reRegexEscape = /[.*+?^${}()|[\]\\]/g;
202+
if ( needle === '' || needle === '{{2}}' ) {
203+
needle = '^';
204+
} else if ( /^\/.+\/$/.test(needle) ) {
205+
needle = needle.slice(1,-1);
206+
} else {
207+
needle = needle.replace(reRegexEscape, '\\$&');
208+
}
209+
const reNeedle = new RegExp(needle, 'im');
210+
const magic = String.fromCharCode(Math.random() * 26 + 97) +
211+
Math.floor(
212+
(0.25 + Math.random() * 0.75) * Number.MAX_SAFE_INTEGER
213+
).toString(36).slice(-8);
214+
const log = console.log.bind(console);
215+
const ErrorCtor = self.Error;
216+
const mustAbort = function(err) {
217+
let docURL = self.location.href;
218+
const pos = docURL.indexOf('#');
219+
if ( pos !== -1 ) {
220+
docURL = docURL.slice(0, pos);
221+
}
222+
const reDocURL = new RegExp(docURL.replace(reRegexEscape, '\\$&'), 'g');
223+
const stack = err.stack
224+
.replace(/^.*?\b[gs]et\b[^\n\r]+?[\n\r]*/m, '')
225+
.replace(reDocURL, '<inline-script>');
226+
const r = reNeedle.test(stack);
227+
if (
228+
logLevel === '1' ||
229+
logLevel === '2' && r ||
230+
logLevel === '3' && !r
231+
) {
232+
log(stack);
233+
}
234+
return r;
235+
};
236+
const makeProxy = function(owner, chain) {
237+
const pos = chain.indexOf('.');
238+
if ( pos === -1 ) {
239+
let v = owner[chain];
240+
Object.defineProperty(owner, chain, {
241+
get: function() {
242+
if ( mustAbort(new ErrorCtor(magic)) ) {
243+
throw new ReferenceError(magic);
244+
}
245+
return v;
246+
},
247+
set: function(a) {
248+
if ( mustAbort(new ErrorCtor(magic)) ) {
249+
throw new ReferenceError(magic);
250+
}
251+
v = a;
252+
},
253+
});
254+
return;
255+
}
256+
const prop = chain.slice(0, pos);
257+
let v = owner[prop];
258+
chain = chain.slice(pos + 1);
259+
if ( v ) {
260+
makeProxy(v, chain);
261+
return;
262+
}
263+
const desc = Object.getOwnPropertyDescriptor(owner, prop);
264+
if ( desc && desc.set !== undefined ) { return; }
265+
Object.defineProperty(owner, prop, {
266+
get: function() { return v; },
267+
set: function(a) {
268+
v = a;
269+
if ( a instanceof Object ) {
270+
makeProxy(a, chain);
271+
}
272+
}
273+
});
274+
};
275+
const owner = window;
276+
makeProxy(owner, chain);
277+
const oe = window.onerror;
278+
window.onerror = function(msg, src, line, col, error) {
279+
if ( typeof msg === 'string' && msg.indexOf(magic) !== -1 ) {
280+
return true;
281+
}
282+
if ( oe instanceof Function ) {
283+
return oe(msg, src, line, col, error);
284+
}
285+
}.bind();
286+
})();
287+
288+
194289
/// addEventListener-defuser.js
195290
/// alias aeld.js
196291
(function() {

0 commit comments

Comments
 (0)