Skip to content

Commit 652c0ea

Browse files
ghostery-adblocker-bot[bot]Ghostery Adblocker Bot
andauthored
Update scriptlets (#49)
Co-authored-by: Ghostery Adblocker Bot <ghostery-adblocker-bot@users.noreply.github.com>
1 parent 4defe56 commit 652c0ea

2 files changed

Lines changed: 269 additions & 15 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"main": "index.js",
66
"type": "module",
77
"scripts": {
8-
"build": "deno build.ts --tagName 1.70.1b0 > ubo.js",
8+
"build": "deno build.ts --tagName 1.70.1b4 > ubo.js",
99
"test": "node --test"
1010
},
1111
"author": {

ubo.js

Lines changed: 268 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20113,7 +20113,7 @@ preventAddEventListener(...args);
2011320113

2011420114
scriptlets['prevent-dialog.js'] = {
2011520115
aliases: [],
20116-
20116+
world: 'ISOLATED',
2011720117
requiresTrust: false,
2011820118
func: function (scriptletGlobals = {}, ...args) {
2011920119
function safeSelf() {
@@ -21369,7 +21369,7 @@ trustedPreventFetch(...args);
2136921369
};
2137021370

2137121371

21372-
scriptlets['prevent-innerHTML.js'] = {
21372+
scriptlets['freeze-element-property.js'] = {
2137321373
aliases: [],
2137421374

2137521375
requiresTrust: false,
@@ -21564,14 +21564,28 @@ function safeSelf() {
2156421564
}
2156521565
return safe;
2156621566
}
21567-
function preventInnerHTML(
21567+
function freezeElementProperty(
21568+
property = '',
2156821569
selector = '',
2156921570
pattern = ''
2157021571
) {
2157121572
const safe = safeSelf();
21572-
const logPrefix = safe.makeLogPrefix('prevent-innerHTML', selector, pattern);
21573+
const logPrefix = safe.makeLogPrefix('freeze-element-property', property, selector, pattern);
2157321574
const matcher = safe.initPattern(pattern, { canNegate: true });
21574-
const current = safe.Object_getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
21575+
const owner = (( ) => {
21576+
if ( Object.hasOwn(Element.prototype, property) ) {
21577+
return Element.prototype;
21578+
}
21579+
if ( Object.hasOwn(HTMLElement.prototype, property) ) {
21580+
return HTMLElement.prototype;
21581+
}
21582+
if ( Object.hasOwn(Node.prototype, property) ) {
21583+
return Node.prototype;
21584+
}
21585+
return null;
21586+
})();
21587+
if ( owner === null ) { return; }
21588+
const current = safe.Object_getOwnPropertyDescriptor(owner, property);
2157521589
if ( current === undefined ) { return; }
2157621590
const shouldPreventSet = (elem, a) => {
2157721591
if ( selector !== '' ) {
@@ -21580,15 +21594,15 @@ function preventInnerHTML(
2158021594
}
2158121595
return safe.testPattern(matcher, `${a}`);
2158221596
};
21583-
Object.defineProperty(Element.prototype, 'innerHTML', {
21597+
Object.defineProperty(owner, property, {
2158421598
get: function() {
2158521599
return current.get
2158621600
? current.get.call(this)
2158721601
: current.value;
2158821602
},
2158921603
set: function(a) {
2159021604
if ( shouldPreventSet(this, a) ) {
21591-
safe.uboLog(logPrefix, 'Prevented');
21605+
safe.uboLog(logPrefix, 'Assignment prevented');
2159221606
} else if ( current.set ) {
2159321607
current.set.call(this, a);
2159421608
}
@@ -21599,12 +21613,12 @@ function preventInnerHTML(
2159921613
},
2160021614
});
2160121615
};
21602-
preventInnerHTML(...args);
21616+
freezeElementProperty(...args);
2160321617
},
2160421618
};
2160521619

2160621620

21607-
scriptlets['prevent-textContent.js'] = {
21621+
scriptlets['prevent-innerHTML.js'] = {
2160821622
aliases: [],
2160921623

2161021624
requiresTrust: false,
@@ -21799,14 +21813,28 @@ function safeSelf() {
2179921813
}
2180021814
return safe;
2180121815
}
21802-
function preventInnerHTML(
21816+
function freezeElementProperty(
21817+
property = '',
2180321818
selector = '',
2180421819
pattern = ''
2180521820
) {
2180621821
const safe = safeSelf();
21807-
const logPrefix = safe.makeLogPrefix('prevent-innerHTML', selector, pattern);
21822+
const logPrefix = safe.makeLogPrefix('freeze-element-property', property, selector, pattern);
2180821823
const matcher = safe.initPattern(pattern, { canNegate: true });
21809-
const current = safe.Object_getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
21824+
const owner = (( ) => {
21825+
if ( Object.hasOwn(Element.prototype, property) ) {
21826+
return Element.prototype;
21827+
}
21828+
if ( Object.hasOwn(HTMLElement.prototype, property) ) {
21829+
return HTMLElement.prototype;
21830+
}
21831+
if ( Object.hasOwn(Node.prototype, property) ) {
21832+
return Node.prototype;
21833+
}
21834+
return null;
21835+
})();
21836+
if ( owner === null ) { return; }
21837+
const current = safe.Object_getOwnPropertyDescriptor(owner, property);
2181021838
if ( current === undefined ) { return; }
2181121839
const shouldPreventSet = (elem, a) => {
2181221840
if ( selector !== '' ) {
@@ -21815,15 +21843,15 @@ function preventInnerHTML(
2181521843
}
2181621844
return safe.testPattern(matcher, `${a}`);
2181721845
};
21818-
Object.defineProperty(Element.prototype, 'innerHTML', {
21846+
Object.defineProperty(owner, property, {
2181921847
get: function() {
2182021848
return current.get
2182121849
? current.get.call(this)
2182221850
: current.value;
2182321851
},
2182421852
set: function(a) {
2182521853
if ( shouldPreventSet(this, a) ) {
21826-
safe.uboLog(logPrefix, 'Prevented');
21854+
safe.uboLog(logPrefix, 'Assignment prevented');
2182721855
} else if ( current.set ) {
2182821856
current.set.call(this, a);
2182921857
}
@@ -21833,12 +21861,238 @@ function preventInnerHTML(
2183321861
current.value = a;
2183421862
},
2183521863
});
21864+
}
21865+
function preventInnerHTML(
21866+
selector = '',
21867+
pattern = ''
21868+
) {
21869+
freezeElementProperty('innerHTML', selector, pattern);
2183621870
};
2183721871
preventInnerHTML(...args);
2183821872
},
2183921873
};
2184021874

2184121875

21876+
scriptlets['prevent-navigation.js'] = {
21877+
aliases: [],
21878+
world: 'ISOLATED',
21879+
requiresTrust: false,
21880+
func: function (scriptletGlobals = {}, ...args) {
21881+
function safeSelf() {
21882+
if ( scriptletGlobals.safeSelf ) {
21883+
return scriptletGlobals.safeSelf;
21884+
}
21885+
const self = globalThis;
21886+
const safe = {
21887+
'Array_from': Array.from,
21888+
'Error': self.Error,
21889+
'Function_toStringFn': self.Function.prototype.toString,
21890+
'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg),
21891+
'Math_floor': Math.floor,
21892+
'Math_max': Math.max,
21893+
'Math_min': Math.min,
21894+
'Math_random': Math.random,
21895+
'Object': Object,
21896+
'Object_defineProperty': Object.defineProperty.bind(Object),
21897+
'Object_defineProperties': Object.defineProperties.bind(Object),
21898+
'Object_fromEntries': Object.fromEntries.bind(Object),
21899+
'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object),
21900+
'Object_hasOwn': Object.hasOwn.bind(Object),
21901+
'Object_toString': Object.prototype.toString,
21902+
'RegExp': self.RegExp,
21903+
'RegExp_test': self.RegExp.prototype.test,
21904+
'RegExp_exec': self.RegExp.prototype.exec,
21905+
'Request_clone': self.Request.prototype.clone,
21906+
'String': self.String,
21907+
'String_fromCharCode': String.fromCharCode,
21908+
'String_split': String.prototype.split,
21909+
'XMLHttpRequest': self.XMLHttpRequest,
21910+
'addEventListener': self.EventTarget.prototype.addEventListener,
21911+
'removeEventListener': self.EventTarget.prototype.removeEventListener,
21912+
'fetch': self.fetch,
21913+
'JSON': self.JSON,
21914+
'JSON_parseFn': self.JSON.parse,
21915+
'JSON_stringifyFn': self.JSON.stringify,
21916+
'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args),
21917+
'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args),
21918+
'log': console.log.bind(console),
21919+
// Properties
21920+
logLevel: 0,
21921+
// Methods
21922+
makeLogPrefix(...args) {
21923+
return this.sendToLogger && `[${args.join(' \u205D ')}]` || '';
21924+
},
21925+
uboLog(...args) {
21926+
if ( this.sendToLogger === undefined ) { return; }
21927+
if ( args === undefined || args[0] === '' ) { return; }
21928+
return this.sendToLogger('info', ...args);
21929+
21930+
},
21931+
uboErr(...args) {
21932+
if ( this.sendToLogger === undefined ) { return; }
21933+
if ( args === undefined || args[0] === '' ) { return; }
21934+
return this.sendToLogger('error', ...args);
21935+
},
21936+
escapeRegexChars(s) {
21937+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
21938+
},
21939+
initPattern(pattern, options = {}) {
21940+
if ( pattern === '' ) {
21941+
return { matchAll: true, expect: true };
21942+
}
21943+
const expect = (options.canNegate !== true || pattern.startsWith('!') === false);
21944+
if ( expect === false ) {
21945+
pattern = pattern.slice(1);
21946+
}
21947+
const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern);
21948+
if ( match !== null ) {
21949+
return {
21950+
re: new this.RegExp(
21951+
match[1],
21952+
match[2] || options.flags
21953+
),
21954+
expect,
21955+
};
21956+
}
21957+
if ( options.flags !== undefined ) {
21958+
return {
21959+
re: new this.RegExp(this.escapeRegexChars(pattern),
21960+
options.flags
21961+
),
21962+
expect,
21963+
};
21964+
}
21965+
return { pattern, expect };
21966+
},
21967+
testPattern(details, haystack) {
21968+
if ( details.matchAll ) { return true; }
21969+
if ( details.re ) {
21970+
return this.RegExp_test.call(details.re, haystack) === details.expect;
21971+
}
21972+
return haystack.includes(details.pattern) === details.expect;
21973+
},
21974+
patternToRegex(pattern, flags = undefined, verbatim = false) {
21975+
if ( pattern === '' ) { return /^/; }
21976+
const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern);
21977+
if ( match === null ) {
21978+
const reStr = this.escapeRegexChars(pattern);
21979+
return new RegExp(verbatim ? `^${reStr}$` : reStr, flags);
21980+
}
21981+
try {
21982+
return new RegExp(match[1], match[2] || undefined);
21983+
}
21984+
catch {
21985+
}
21986+
return /^/;
21987+
},
21988+
getExtraArgs(args, offset = 0) {
21989+
const entries = args.slice(offset).reduce((out, v, i, a) => {
21990+
if ( (i & 1) === 0 ) {
21991+
const rawValue = a[i+1];
21992+
const value = /^\d+$/.test(rawValue)
21993+
? parseInt(rawValue, 10)
21994+
: rawValue;
21995+
out.push([ a[i], value ]);
21996+
}
21997+
return out;
21998+
}, []);
21999+
return this.Object_fromEntries(entries);
22000+
},
22001+
onIdle(fn, options) {
22002+
if ( self.requestIdleCallback ) {
22003+
return self.requestIdleCallback(fn, options);
22004+
}
22005+
return self.requestAnimationFrame(fn);
22006+
},
22007+
offIdle(id) {
22008+
if ( self.requestIdleCallback ) {
22009+
return self.cancelIdleCallback(id);
22010+
}
22011+
return self.cancelAnimationFrame(id);
22012+
}
22013+
};
22014+
scriptletGlobals.safeSelf = safe;
22015+
if ( scriptletGlobals.bcSecret === undefined ) { return safe; }
22016+
// This is executed only when the logger is opened
22017+
safe.logLevel = scriptletGlobals.logLevel || 1;
22018+
let lastLogType = '';
22019+
let lastLogText = '';
22020+
let lastLogTime = 0;
22021+
safe.toLogText = (type, ...args) => {
22022+
if ( args.length === 0 ) { return; }
22023+
const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`;
22024+
if ( text === lastLogText && type === lastLogType ) {
22025+
if ( (Date.now() - lastLogTime) < 5000 ) { return; }
22026+
}
22027+
lastLogType = type;
22028+
lastLogText = text;
22029+
lastLogTime = Date.now();
22030+
return text;
22031+
};
22032+
try {
22033+
const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret);
22034+
let bcBuffer = [];
22035+
safe.sendToLogger = (type, ...args) => {
22036+
const text = safe.toLogText(type, ...args);
22037+
if ( text === undefined ) { return; }
22038+
if ( bcBuffer === undefined ) {
22039+
return bc.postMessage({ what: 'messageToLogger', type, text });
22040+
}
22041+
bcBuffer.push({ type, text });
22042+
};
22043+
bc.onmessage = ev => {
22044+
const msg = ev.data;
22045+
switch ( msg ) {
22046+
case 'iamready!':
22047+
if ( bcBuffer === undefined ) { break; }
22048+
bcBuffer.forEach(({ type, text }) =>
22049+
bc.postMessage({ what: 'messageToLogger', type, text })
22050+
);
22051+
bcBuffer = undefined;
22052+
break;
22053+
case 'setScriptletLogLevelToOne':
22054+
safe.logLevel = 1;
22055+
break;
22056+
case 'setScriptletLogLevelToTwo':
22057+
safe.logLevel = 2;
22058+
break;
22059+
}
22060+
};
22061+
bc.postMessage('areyouready?');
22062+
} catch {
22063+
safe.sendToLogger = (type, ...args) => {
22064+
const text = safe.toLogText(type, ...args);
22065+
if ( text === undefined ) { return; }
22066+
safe.log(`uBO ${text}`);
22067+
};
22068+
}
22069+
return safe;
22070+
}
22071+
function preventNavigation(
22072+
pattern = ''
22073+
) {
22074+
const safe = safeSelf();
22075+
const logPrefix = safe.makeLogPrefix('prevent-navigation', pattern);
22076+
const needle = pattern === 'location.href' ? self.location.href : pattern;
22077+
const matcher = safe.initPattern(needle, { canNegate: true });
22078+
self.navigation.addEventListener('navigate', ev => {
22079+
if ( ev.userInitiated ) { return; }
22080+
const { url } = ev.destination;
22081+
if ( pattern === '' ) {
22082+
safe.uboLog(logPrefix, `Navigation to ${url}`);
22083+
return;
22084+
}
22085+
if ( safe.testPattern(matcher, url) ) {
22086+
ev.preventDefault();
22087+
safe.uboLog(logPrefix, `Prevented navigation to ${url}`);
22088+
}
22089+
});
22090+
};
22091+
preventNavigation(...args);
22092+
},
22093+
};
22094+
22095+
2184222096
scriptlets['prevent-setTimeout.js'] = {
2184322097
aliases: ["no-setTimeout-if.js","nostif.js","setTimeout-defuser.js"],
2184422098

0 commit comments

Comments
 (0)