Skip to content

Commit

Permalink
Improve trusted-replace-argument scriptlet
Browse files Browse the repository at this point in the history
As discussed with filter list maintainers.
  • Loading branch information
gorhill committed Dec 6, 2024
1 parent 36db7f8 commit 3417fe3
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 33 deletions.
4 changes: 2 additions & 2 deletions src/js/resources/attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ export function removeAttr(
if ( rawToken === '' ) { return; }
const safe = safeSelf();
const logPrefix = safe.makeLogPrefix('remove-attr', rawToken, rawSelector, behavior);
const tokens = rawToken.split(/\s*\|\s*/);
const tokens = safe.String_split.call(rawToken, /\s*\|\s*/);
const selector = tokens
.map(a => `${rawSelector}[${CSS.escape(a)}]`)
.join(',');
Expand Down Expand Up @@ -289,7 +289,7 @@ export function removeAttr(
subtree: true,
});
};
runAt(( ) => { start(); }, behavior.split(/\s+/));
runAt(( ) => { start(); }, safe.String_split.call(behavior, /\s+/));
}
registerScriptlet(removeAttr, {
name: 'remove-attr.js',
Expand Down
16 changes: 12 additions & 4 deletions src/js/resources/cookie.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ registerScriptlet(getSafeCookieValuesFn, {
/******************************************************************************/

export function getAllCookiesFn() {
return document.cookie.split(/\s*;\s*/).map(s => {
const safe = safeSelf();
return safe.String_split.call(document.cookie, /\s*;\s*/).map(s => {
const pos = s.indexOf('=');
if ( pos === 0 ) { return; }
if ( pos === -1 ) { return `${s.trim()}=`; }
Expand All @@ -64,14 +65,18 @@ export function getAllCookiesFn() {
}
registerScriptlet(getAllCookiesFn, {
name: 'get-all-cookies.fn',
dependencies: [
safeSelf,
],
});

/******************************************************************************/

export function getCookieFn(
name = ''
) {
for ( const s of document.cookie.split(/\s*;\s*/) ) {
const safe = safeSelf();
for ( const s of safe.String_split.call(document.cookie, /\s*;\s*/) ) {
const pos = s.indexOf('=');
if ( pos === -1 ) { continue; }
if ( s.slice(0, pos) !== name ) { continue; }
Expand All @@ -80,6 +85,9 @@ export function getCookieFn(
}
registerScriptlet(getCookieFn, {
name: 'get-cookie.fn',
dependencies: [
safeSelf,
],
});

/******************************************************************************/
Expand Down Expand Up @@ -349,7 +357,7 @@ export function removeCookie(
}, ms);
};
const remove = ( ) => {
document.cookie.split(';').forEach(cookieStr => {
safe.String_split.call(document.cookie, ';').forEach(cookieStr => {
const pos = cookieStr.indexOf('=');
if ( pos === -1 ) { return; }
const cookieName = cookieStr.slice(0, pos).trim();
Expand Down Expand Up @@ -387,7 +395,7 @@ export function removeCookie(
window.addEventListener('beforeunload', remove);
if ( typeof extraArgs.when !== 'string' ) { return; }
const supportedEventTypes = [ 'scroll', 'keydown' ];
const eventTypes = extraArgs.when.split(/\s/);
const eventTypes = safe.String_split.call(extraArgs.when, /\s/);
for ( const type of eventTypes ) {
if ( supportedEventTypes.includes(type) === false ) { continue; }
document.addEventListener(type, ( ) => {
Expand Down
4 changes: 2 additions & 2 deletions src/js/resources/proxy-apply.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function proxyApplyFn(
}
reflect() {
const r = Reflect.construct(this.callFn, this.callArgs);
this.callFn = this.callArgs = undefined;
this.callFn = this.callArgs = this.private = undefined;
proxyApplyFn.ctorContexts.push(this);
return r;
}
Expand All @@ -75,7 +75,7 @@ export function proxyApplyFn(
}
reflect() {
const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs);
this.callFn = this.thisArg = this.callArgs = undefined;
this.callFn = this.thisArg = this.callArgs = this.private = undefined;
proxyApplyFn.applyContexts.push(this);
return r;
}
Expand Down
32 changes: 23 additions & 9 deletions src/js/resources/replace-argument.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,25 +71,39 @@ export function trustedReplaceArgument(
const reCondition = extraArgs.condition
? safe.patternToRegex(extraArgs.condition)
: /^/;
proxyApplyFn(propChain, function(context) {
const getArg = context => {
if ( argposRaw === 'this' ) { return context.thisArg; }
const { callArgs } = context;
if ( argposRaw === '' ) {
safe.uboLog(logPrefix, `Arguments:\n${callArgs.join('\n')}`);
return context.reflect();
}
const argpos = argoffset >= 0 ? argoffset : callArgs.length - argoffset;
if ( argpos < 0 || argpos >= callArgs.length ) {
if ( argpos < 0 || argpos >= callArgs.length ) { return; }
context.private = { argpos };
return callArgs[argpos];
};
const setArg = (context, value) => {
if ( argposRaw === 'this' ) {
if ( value !== context.thisArg ) {
context.thisArg = value;
}
} else if ( context.private ) {
context.callArgs[context.private.argpos] = value;
}
};
proxyApplyFn(propChain, function(context) {
if ( argposRaw === '' ) {
safe.uboLog(logPrefix, `Arguments:\n${context.callArgs.join('\n')}`);
return context.reflect();
}
const argBefore = callArgs[argpos];
const argBefore = getArg(context);
if ( safe.RegExp_test.call(reCondition, argBefore) === false ) {
return context.reflect();
}
const argAfter = replacer && typeof argBefore === 'string'
? argBefore.replace(replacer.re, replacer.replacement)
: value;
callArgs[argpos] = argAfter;
safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${argAfter}`);
if ( argAfter !== argBefore ) {
setArg(context, argAfter);
safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${argAfter}`);
}
return context.reflect();
});
}
Expand Down
1 change: 1 addition & 0 deletions src/js/resources/safe-self.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export function safeSelf() {
'RegExp_exec': self.RegExp.prototype.exec,
'Request_clone': self.Request.prototype.clone,
'String_fromCharCode': String.fromCharCode,
'String_split': String.prototype.split,
'XMLHttpRequest': self.XMLHttpRequest,
'addEventListener': self.EventTarget.prototype.addEventListener,
'removeEventListener': self.EventTarget.prototype.removeEventListener,
Expand Down
38 changes: 22 additions & 16 deletions src/js/resources/scriptlets.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ function abortCurrentScriptCore(
const reContext = safe.patternToRegex(context);
const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
const thisScript = document.currentScript;
const chain = target.split('.');
const chain = safe.String_split.call(target, '.');
let owner = window;
let prop;
for (;;) {
Expand Down Expand Up @@ -406,6 +406,7 @@ builtinScriptlets.push({
dependencies: [
'matches-stack-trace.fn',
'object-find-owner.fn',
'safe-self.fn',
],
});
// When no "prune paths" argument is provided, the scriptlet is
Expand All @@ -422,11 +423,12 @@ function objectPruneFn(
extraArgs = {}
) {
if ( typeof rawPrunePaths !== 'string' ) { return; }
const safe = safeSelf();
const prunePaths = rawPrunePaths !== ''
? rawPrunePaths.split(/ +/)
? safe.String_split.call(rawPrunePaths, / +/)
: [];
const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== ''
? rawNeedlePaths.split(/ +/)
? safe.String_split.call(rawNeedlePaths, / +/)
: [];
if ( stackNeedleDetails.matchAll !== true ) {
if ( matchesStackTrace(stackNeedleDetails, extraArgs.logstack) === false ) {
Expand Down Expand Up @@ -547,7 +549,7 @@ function matchesStackTrace(
// Normalize stack trace
const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/;
const lines = [];
for ( let line of error.stack.split(/[\n\r]+/) ) {
for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) {
if ( line.includes(exceptionToken) ) { continue; }
line = line.trim();
const match = safe.RegExp_exec.call(reLine, line);
Expand Down Expand Up @@ -594,8 +596,8 @@ function parsePropertiesToMatch(propsToMatch, implicit = '') {
const needles = new Map();
if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; }
const options = { canNegate: true };
for ( const needle of propsToMatch.split(/\s+/) ) {
const [ prop, pattern ] = needle.split(':');
for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) {
const [ prop, pattern ] = safe.String_split.call(needle, ':');
if ( prop === '' ) { continue; }
if ( pattern !== undefined ) {
needles.set(prop, safe.initPattern(pattern, options));
Expand Down Expand Up @@ -1643,7 +1645,7 @@ function noFetchIf(
const safe = safeSelf();
const logPrefix = safe.makeLogPrefix('prevent-fetch', propsToMatch, responseBody, responseType);
const needles = [];
for ( const condition of propsToMatch.split(/\s+/) ) {
for ( const condition of safe.String_split.call(propsToMatch, /\s+/) ) {
if ( condition === '' ) { continue; }
const pos = condition.indexOf(':');
let key, value;
Expand Down Expand Up @@ -1797,7 +1799,7 @@ function removeClass(
if ( rawToken === '' ) { return; }
const safe = safeSelf();
const logPrefix = safe.makeLogPrefix('remove-class', rawToken, rawSelector, behavior);
const tokens = rawToken.split(/\s*\|\s*/);
const tokens = safe.String_split.call(rawToken, /\s*\|\s*/);
const selector = tokens
.map(a => `${rawSelector}.${CSS.escape(a)}`)
.join(',');
Expand Down Expand Up @@ -2510,12 +2512,12 @@ function m3uPrune(
}
text = before.trim() + '\n' + after.trim();
reM3u.lastIndex = before.length + 1;
toLog.push('Discarding', ...discard.split(/\n+/).map(s => `\t${s}`));
toLog.push('Discarding', ...safe.String_split.call(discard, /\n+/).map(s => `\t${s}`));
if ( reM3u.global === false ) { break; }
}
return text;
}
const lines = text.split(/\n\r|\n|\r/);
const lines = safe.String_split.call(text, /\n\r|\n|\r/);
for ( let i = 0; i < lines.length; i++ ) {
if ( lines[i] === undefined ) { continue; }
if ( pruneSpliceoutBlock(lines, i) ) { continue; }
Expand Down Expand Up @@ -2758,13 +2760,17 @@ function hrefSanitizer(
builtinScriptlets.push({
name: 'call-nothrow.js',
fn: callNothrow,
dependencies: [
'safe-self.fn',
],
});
function callNothrow(
chain = ''
) {
if ( typeof chain !== 'string' ) { return; }
if ( chain === '' ) { return; }
const parts = chain.split('.');
const safe = safeSelf();
const parts = safe.String_split.call(chain, '.');
let owner = window, prop;
for (;;) {
prop = parts.shift();
Expand Down Expand Up @@ -3095,7 +3101,7 @@ function trustedClickElement(
const logPrefix = safe.makeLogPrefix('trusted-click-element', selectors, extraMatch, delay);

if ( extraMatch !== '' ) {
const assertions = extraMatch.split(',').map(s => {
const assertions = safe.String_split.call(extraMatch, ',').map(s => {
const pos1 = s.indexOf(':');
const s1 = pos1 !== -1 ? s.slice(0, pos1) : s;
const not = s1.startsWith('!');
Expand Down Expand Up @@ -3163,7 +3169,7 @@ function trustedClickElement(
return shadowRoot && querySelectorEx(inside, shadowRoot);
};

const selectorList = selectors.split(/\s*,\s*/)
const selectorList = safe.String_split.call(selectors, /\s*,\s*/)
.filter(s => {
try {
void querySelectorEx(s);
Expand Down Expand Up @@ -3290,10 +3296,10 @@ function trustedPruneInboundObject(
const extraArgs = safe.getExtraArgs(Array.from(arguments), 4);
const needlePaths = [];
if ( rawPrunePaths !== '' ) {
needlePaths.push(...rawPrunePaths.split(/ +/));
needlePaths.push(...safe.String_split.call(rawPrunePaths, / +/));
}
if ( rawNeedlePaths !== '' ) {
needlePaths.push(...rawNeedlePaths.split(/ +/));
needlePaths.push(...safe.String_split.call(rawNeedlePaths, / +/));
}
const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true });
const mustProcess = root => {
Expand Down Expand Up @@ -3455,7 +3461,7 @@ function trustedSuppressNativeMethod(
if ( stack !== '' ) { return; }
const safe = safeSelf();
const logPrefix = safe.makeLogPrefix('trusted-suppress-native-method', methodPath, signature, how);
const signatureArgs = signature.split(/\s*\|\s*/).map(v => {
const signatureArgs = safe.String_split.call(signature, /\s*\|\s*/).map(v => {
if ( /^".*"$/.test(v) ) {
return { type: 'pattern', re: safe.patternToRegex(v.slice(1, -1)) };
}
Expand Down
1 change: 1 addition & 0 deletions src/js/resources/set-constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export function validateConstantFn(trusted, raw, extraArgs = {}) {
} else if ( raw.startsWith('{') && raw.endsWith('}') ) {
try { value = safe.JSON_parse(raw).value; } catch(ex) { return; }
}
return raw;
} else {
return;
}
Expand Down

0 comments on commit 3417fe3

Please sign in to comment.