Rule `protruyen.com#%#//scriptlet("abort-current-inline-script", "Object", "$._Eu")` crash `Object[`${prop}`]`
tachibana-shin opened this issue · 1 comments
Sorry I don't have time to investigate further. Specific problems are as follows:
In the ABPVN filter there is a filter rule as follows:
protruyen.com#%#//scriptlet("abort-current-inline-script", "Object", "$._Eu")
after i enabled the filter and went to protruyen.com i noticed that a code was forwarded to the site calling max stack call
or
i checked and found a code injected:
Code brave inject to website:
(function() {
const scriptletGlobals = new Map();
let deAmpEnabled = true;
try {
(function() {
const target = 'Object';
if (target === '' || target === '{{1}}') {
return;
}
const reRegexEscape = /[.*+?^${}()|[\]\\]/g;
const needle = '$._Eu';
const reNeedle = (()=>{
if (needle === '' || needle === '{{2}}') {
return /^/;
}
if (/^\/.+\/$/.test(needle)) {
return new RegExp(needle.slice(1, -1));
}
return new RegExp(needle.replace(reRegexEscape, '\\$&'));
}
)();
const context = '{{3}}';
const reContext = (()=>{
if (context === '' || context === '{{3}}') {
return;
}
if (/^\/.+\/$/.test(context)) {
return new RegExp(context.slice(1, -1));
}
return new RegExp(context.replace(reRegexEscape, '\\$&'));
}
)();
const thisScript = document.currentScript;
const chain = target.split('.');
let owner = window;
let prop;
for (; ; ) {
prop = chain.shift();
if (chain.length === 0) {
break;
}
owner = owner[prop];
if (owner instanceof Object === false) {
return;
}
}
let value;
let desc = Object.getOwnPropertyDescriptor(owner, prop);
if (desc instanceof Object === false || desc.get instanceof Function === false) {
value = owner[prop];
desc = undefined;
}
const magic = String.fromCharCode(Date.now() % 26 + 97) + Math.floor(Math.random() * 982451653 + 982451653).toString(36);
const scriptTexts = new WeakMap();
const getScriptText = elem=>{
let text = elem.textContent;
if (text.trim() !== '') {
return text;
}
if (scriptTexts.has(elem)) {
return scriptTexts.get(elem);
}
const [,mime,content] = /^data:([^,]*),(.+)$/.exec(elem.src.trim()) || ['', '', ''];
try {
switch (true) {
case mime.endsWith(';base64'):
text = self.atob(content);
break;
default:
text = self.decodeURIComponent(content);
break;
}
} catch (ex) {}
scriptTexts.set(elem, text);
return text;
}
;
const validate = ()=>{
const e = document.currentScript;
if (e instanceof HTMLScriptElement === false) {
return;
}
if (e === thisScript) {
return;
}
if (reContext !== undefined && reContext.test(e.src) === false) {
return;
}
if (reNeedle.test(getScriptText(e)) === false) {
return;
}
throw new ReferenceError(magic);
}
; // owner = Window, prop = "Object", desc = undefined, value = Object // owner[prop]
Object.defineProperty(owner, prop, {
get: function() {
validate(); // VM8:82 Uncaught RangeError: Maximum call stack size exceeded
return desc instanceof Object ? desc.get.call(owner) : value; // or this
// because desc is undefined. But `Object` is `window.Object` -> call this function recursive
},
set: function(a) {
validate();
if (desc instanceof Object) {
desc.set.call(owner, a);
} else {
value = a;
}
}
});
const oe = window.onerror;
window.onerror = function(msg) {
if (typeof msg === 'string' && msg.includes(magic)) {
return true;
}
if (oe instanceof Function) {
return oe.apply(this, arguments);
}
}
.bind();
}
)();
} catch (e) {}
}
)()
and i try accessing anything from Object
same error happens. And I conclude that this is due to the code that adblock-rust
injected
Solution
My suggestion is that instead of using general-purpose test instanceof Object
will store Object
to check primitive value:
(function() {
const scriptletGlobals = new Map();
let deAmpEnabled = true;
try {
(function() {
const target = 'Object';
if (target === '' || target === '{{1}}') {
return;
}
const reRegexEscape = /[.*+?^${}()|[\]\\]/g;
const needle = '$._Eu';
const reNeedle = (()=>{
if (needle === '' || needle === '{{2}}') {
return /^/;
}
if (/^\/.+\/$/.test(needle)) {
return new RegExp(needle.slice(1, -1));
}
return new RegExp(needle.replace(reRegexEscape, '\\$&'));
}
)();
const context = '{{3}}';
const reContext = (()=>{
if (context === '' || context === '{{3}}') {
return;
}
if (/^\/.+\/$/.test(context)) {
return new RegExp(context.slice(1, -1));
}
return new RegExp(context.replace(reRegexEscape, '\\$&'));
}
)();
const thisScript = document.currentScript;
const chain = target.split('.');
let owner = window;
let prop;
for (; ; ) {
prop = chain.shift();
if (chain.length === 0) {
break;
}
owner = owner[prop];
if (owner instanceof Object === false) {
return;
}
}
let value;
let desc = Object.getOwnPropertyDescriptor(owner, prop);
if (desc instanceof Object === false || desc.get instanceof Function === false) {
value = owner[prop];
desc = undefined;
}
const magic = String.fromCharCode(Date.now() % 26 + 97) + Math.floor(Math.random() * 982451653 + 982451653).toString(36);
const scriptTexts = new WeakMap();
const getScriptText = elem=>{
let text = elem.textContent;
if (text.trim() !== '') {
return text;
}
if (scriptTexts.has(elem)) {
return scriptTexts.get(elem);
}
const [,mime,content] = /^data:([^,]*),(.+)$/.exec(elem.src.trim()) || ['', '', ''];
try {
switch (true) {
case mime.endsWith(';base64'):
text = self.atob(content);
break;
default:
text = self.decodeURIComponent(content);
break;
}
} catch (ex) {}
scriptTexts.set(elem, text);
return text;
}
;
const validate = ()=>{
const e = document.currentScript;
if (e instanceof HTMLScriptElement === false) {
return;
}
if (e === thisScript) {
return;
}
if (reContext !== undefined && reContext.test(e.src) === false) {
return;
}
if (reNeedle.test(getScriptText(e)) === false) {
return;
}
throw new ReferenceError(magic);
}
; // owner = Window, prop = "Object", desc = undefined, value = Object // owner[prop]
+ const rawObj = Object
Object.defineProperty(owner, prop, {
get: function() {
validate(); // VM8:82 Uncaught RangeError: Maximum call stack size exceeded
- return desc instanceof Object ? desc.get.call(owner) : value; // or this
+ return desc instanceof rawObj ? desc.get.call(owner) : value; // or this
// because desc is undefined. But `Object` is `window.Object` -> call this function recursive
},
set: function(a) {
validate();
if (desc instanceof Object) {
desc.set.call(owner, a);
} else {
value = a;
}
}
});
const oe = window.onerror;
window.onerror = function(msg) {
if (typeof msg === 'string' && msg.includes(magic)) {
return true;
}
if (oe instanceof Function) {
return oe.apply(this, arguments);
}
}
.bind();
}
)();
} catch (e) {}
}
)()
As mentioned in brave/brave-browser#31098 - the issue is between the protruyen.com##+js(acs, Object, $._Eu)
filter in ABPVN and the site itself, so there's not much we can do here.