Compare commits

..

5 commits

Author SHA1 Message Date
theaquamarine
2f562f648f
Merge f6163cd947 into 556bea809e 2024-11-30 09:56:47 -08:00
Raymond Hill
556bea809e
Make Firefox dev build auto-update 2024-11-29 11:01:23 -05:00
Raymond Hill
47bdec422a
New revision for dev build 2024-11-29 10:55:07 -05:00
Raymond Hill
580f2dee06
Update changelog 2024-11-29 10:54:43 -05:00
Raymond Hill
b1a00145bd
Mitigate potentially delayed execution of scriptlets in Firefox
Related issue:
https://github.com/uBlockOrigin/uBlock-issues/issues/3452

Use blob-based injection only when direct injection fails because
of a page's CSP. This is a mitigation until a better approach is
devised.

Such future better approach to investigate:

- Use `MAIN` world injection supported by contentScript.register()
  since Firefox 128
- Investigate registering script to inject ahead of time thru
  some heuristic
2024-11-29 10:13:39 -05:00
6 changed files with 115 additions and 68 deletions

View file

@ -1,3 +1,4 @@
- [Mitigate potentially delayed execution of scriptlets in Firefox](https://github.com/gorhill/uBlock/commit/b1a00145bd)
- [Improve `prevent-setTimeout`/`prevent-setInterval` scriptlets](https://github.com/gorhill/uBlock/commit/3b7fa79a68)
- [Improve `trusted-replace-argument` scriptlet](https://github.com/gorhill/uBlock/commit/adced29b5b)
- [Add `-safebase64` directive to `urlskip=` option](https://github.com/gorhill/uBlock/commit/bcc058eba7)

View file

@ -3,9 +3,9 @@
"uBlock0@raymondhill.net": {
"updates": [
{
"version": "1.61.3.1",
"version": "1.61.3.2",
"browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } },
"update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b1/uBlock0_1.61.3b1.firefox.signed.xpi"
"update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b2/uBlock0_1.61.3b2.firefox.signed.xpi"
}
]
}

2
dist/version vendored
View file

@ -1 +1 @@
1.61.3.1
1.61.3.2

View file

@ -208,19 +208,43 @@ vAPI.prefetching = (( ) => {
/******************************************************************************/
vAPI.scriptletsInjector = ((doc, details) => {
let script;
try {
script = doc.createElement('script');
script.appendChild(doc.createTextNode(details.scriptlets));
(doc.head || doc.documentElement).appendChild(script);
self.uBO_scriptletsInjected = details.filters;
} catch (ex) {
}
if ( script ) {
script.remove();
script.textContent = '';
}
}).toString();
vAPI.scriptletsInjector = (( ) => {
const parts = [
'(',
function(details) {
if ( typeof self.uBO_scriptletsInjected === 'string' ) { return; }
const doc = document;
const { location } = doc;
if ( location === null ) { return; }
const { hostname } = location;
if ( hostname !== '' && details.hostname !== hostname ) { return; }
let script;
try {
script = doc.createElement('script');
script.appendChild(doc.createTextNode(details.scriptlets));
(doc.head || doc.documentElement).appendChild(script);
self.uBO_scriptletsInjected = details.filters;
} catch (ex) {
}
if ( script ) {
script.remove();
script.textContent = '';
}
return 0;
}.toString(),
')(',
'json-slot',
');',
];
const jsonSlot = parts.indexOf('json-slot');
return (hostname, details) => {
parts[jsonSlot] = JSON.stringify({
hostname,
scriptlets: details.mainWorld,
filters: details.filters,
});
return parts.join('');
};
})();
/******************************************************************************/

View file

@ -351,25 +351,77 @@ vAPI.Net = class extends vAPI.Net {
/******************************************************************************/
vAPI.scriptletsInjector = ((doc, details) => {
let script, url;
try {
const blob = new self.Blob(
[ details.scriptlets ],
{ type: 'text/javascript; charset=utf-8' }
);
url = self.URL.createObjectURL(blob);
script = doc.createElement('script');
script.async = false;
script.src = url;
(doc.head || doc.documentElement || doc).append(script);
self.uBO_scriptletsInjected = details.filters;
} catch (ex) {
}
if ( url ) {
if ( script ) { script.remove(); }
self.URL.revokeObjectURL(url);
}
}).toString();
vAPI.scriptletsInjector = (( ) => {
const parts = [
'(',
function(details) {
if ( typeof self.uBO_scriptletsInjected === 'string' ) { return; }
const doc = document;
const { location } = doc;
if ( location === null ) { return; }
const { hostname } = location;
if ( hostname !== '' && details.hostname !== hostname ) { return; }
// Use a page world sentinel to verify that execution was
// successful
const { sentinel } = details;
let script;
try {
const code = [
`self['${sentinel}'] = true;`,
details.scriptlets,
].join('\n');
script = doc.createElement('script');
script.appendChild(doc.createTextNode(code));
(doc.head || doc.documentElement).appendChild(script);
} catch (ex) {
}
if ( script ) {
script.remove();
script.textContent = '';
script = undefined;
}
if ( self.wrappedJSObject[sentinel] ) {
delete self.wrappedJSObject[sentinel];
self.uBO_scriptletsInjected = details.filters;
return 0;
}
// https://github.com/uBlockOrigin/uBlock-issues/issues/235
// Fall back to blob injection if execution through direct
// injection failed
let url;
try {
const blob = new self.Blob(
[ details.scriptlets ],
{ type: 'text/javascript; charset=utf-8' }
);
url = self.URL.createObjectURL(blob);
script = doc.createElement('script');
script.async = false;
script.src = url;
(doc.head || doc.documentElement || doc).append(script);
self.uBO_scriptletsInjected = details.filters;
} catch (ex) {
}
if ( url ) {
if ( script ) { script.remove(); }
self.URL.revokeObjectURL(url);
}
return 0;
}.toString(),
')(',
'json-slot',
');',
];
const jsonSlot = parts.indexOf('json-slot');
return (hostname, details) => {
parts[jsonSlot] = JSON.stringify({
hostname,
scriptlets: details.mainWorld,
filters: details.filters,
sentinel: vAPI.generateSecret(3),
});
return parts.join('');
};
})();
/******************************************************************************/

View file

@ -106,36 +106,6 @@ const contentScriptRegisterer = new (class {
/******************************************************************************/
const mainWorldInjector = (( ) => {
const parts = [
'(',
function(injector, details) {
if ( typeof self.uBO_scriptletsInjected === 'string' ) { return; }
const doc = document;
if ( doc.location === null ) { return; }
const hostname = doc.location.hostname;
if ( hostname !== '' && details.hostname !== hostname ) { return; }
injector(doc, details);
return 0;
}.toString(),
')(',
vAPI.scriptletsInjector, ', ',
'json-slot',
');',
];
const jsonSlot = parts.indexOf('json-slot');
return {
assemble: function(hostname, details) {
parts[jsonSlot] = JSON.stringify({
hostname,
scriptlets: details.mainWorld,
filters: details.filters,
});
return parts.join('');
},
};
})();
const isolatedWorldInjector = (( ) => {
const parts = [
'(',
@ -334,7 +304,7 @@ export class ScriptletFilteringEngineEx extends ScriptletFilteringEngine {
const contentScript = [];
if ( scriptletDetails.mainWorld ) {
contentScript.push(mainWorldInjector.assemble(hostname, scriptletDetails));
contentScript.push(vAPI.scriptletsInjector(hostname, scriptletDetails));
}
if ( scriptletDetails.isolatedWorld ) {
contentScript.push(isolatedWorldInjector.assemble(hostname, scriptletDetails));