-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve
trusted-replace-argument
scriptlet
As discussed with filter list maintainers, added ability to partially replace an argument using the `repl:` prefix. Updated documentation: --- @Scriptlet trusted-replace-argument.js @description Replace an argument passed to a method. Requires a trusted source. @param propChain The property chain to the function which argument must be replaced when called. @param argposRaw The zero-based position of the argument in the argument list. Use a negative number for a position relative to the last argument. @param argraw The replacement value, validated using the same heuristic as with the `set-constant.js` scriptlet. If the replacement value matches `json:...`, the value will be the json-parsed string after `json:`. If the replacement value matches `repl:/.../.../`, the target argument will be replaced according the regex-replacement directive following `repl:` @param [, condition, pattern] Optional. The replacement will occur only when pattern matches the target argument. --- Aditionally, more scriptlets moved into their own files.
- Loading branch information
Showing
16 changed files
with
723 additions
and
516 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/******************************************************************************* | ||
uBlock Origin - a comprehensive, efficient content blocker | ||
Copyright (C) 2019-present Raymond Hill | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with this program. If not, see {http://www.gnu.org/licenses/}. | ||
Home: https://github.com/gorhill/uBlock | ||
*/ | ||
|
||
import { createArglistParser } from './shared.js'; | ||
import { registerScriptlet } from './base.js'; | ||
|
||
/******************************************************************************/ | ||
|
||
export function parseReplaceFn(s) { | ||
if ( s.charCodeAt(0) !== 0x2F /* / */ ) { return; } | ||
const parser = createArglistParser('/'); | ||
parser.nextArg(s, 1); | ||
let pattern = s.slice(parser.argBeg, parser.argEnd); | ||
if ( parser.transform ) { | ||
pattern = parser.normalizeArg(pattern); | ||
} | ||
if ( pattern === '' ) { return; } | ||
parser.nextArg(s, parser.separatorEnd); | ||
let replacement = s.slice(parser.argBeg, parser.argEnd); | ||
if ( parser.separatorEnd === parser.separatorBeg ) { return; } | ||
if ( parser.transform ) { | ||
replacement = parser.normalizeArg(replacement); | ||
} | ||
const flags = s.slice(parser.separatorEnd); | ||
try { | ||
return { re: new RegExp(pattern, flags), replacement }; | ||
} catch(_) { | ||
} | ||
} | ||
registerScriptlet(parseReplaceFn, { | ||
name: 'parse-replace.fn', | ||
dependencies: [ | ||
createArglistParser, | ||
], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/******************************************************************************* | ||
uBlock Origin - a comprehensive, efficient content blocker | ||
Copyright (C) 2019-present Raymond Hill | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with this program. If not, see {http://www.gnu.org/licenses/}. | ||
Home: https://github.com/gorhill/uBlock | ||
*/ | ||
|
||
import { registerScriptlet } from './base.js'; | ||
|
||
/******************************************************************************/ | ||
|
||
export function proxyApplyFn( | ||
target = '', | ||
handler = '' | ||
) { | ||
let context = globalThis; | ||
let prop = target; | ||
for (;;) { | ||
const pos = prop.indexOf('.'); | ||
if ( pos === -1 ) { break; } | ||
context = context[prop.slice(0, pos)]; | ||
if ( context instanceof Object === false ) { return; } | ||
prop = prop.slice(pos+1); | ||
} | ||
const fn = context[prop]; | ||
if ( typeof fn !== 'function' ) { return; } | ||
if ( proxyApplyFn.CtorContext === undefined ) { | ||
proxyApplyFn.ctorContexts = []; | ||
proxyApplyFn.CtorContext = class { | ||
constructor(...args) { | ||
this.init(...args); | ||
} | ||
init(callFn, callArgs) { | ||
this.callFn = callFn; | ||
this.callArgs = callArgs; | ||
return this; | ||
} | ||
reflect() { | ||
const r = Reflect.construct(this.callFn, this.callArgs); | ||
this.callFn = this.callArgs = undefined; | ||
proxyApplyFn.ctorContexts.push(this); | ||
return r; | ||
} | ||
static factory(...args) { | ||
return proxyApplyFn.ctorContexts.length !== 0 | ||
? proxyApplyFn.ctorContexts.pop().init(...args) | ||
: new proxyApplyFn.CtorContext(...args); | ||
} | ||
}; | ||
proxyApplyFn.applyContexts = []; | ||
proxyApplyFn.ApplyContext = class { | ||
constructor(...args) { | ||
this.init(...args); | ||
} | ||
init(callFn, thisArg, callArgs) { | ||
this.callFn = callFn; | ||
this.thisArg = thisArg; | ||
this.callArgs = callArgs; | ||
return this; | ||
} | ||
reflect() { | ||
const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); | ||
this.callFn = this.thisArg = this.callArgs = undefined; | ||
proxyApplyFn.applyContexts.push(this); | ||
return r; | ||
} | ||
static factory(...args) { | ||
return proxyApplyFn.applyContexts.length !== 0 | ||
? proxyApplyFn.applyContexts.pop().init(...args) | ||
: new proxyApplyFn.ApplyContext(...args); | ||
} | ||
}; | ||
} | ||
const fnStr = fn.toString(); | ||
const toString = (function toString() { return fnStr; }).bind(null); | ||
const proxyDetails = { | ||
apply(target, thisArg, args) { | ||
return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); | ||
}, | ||
get(target, prop) { | ||
if ( prop === 'toString' ) { return toString; } | ||
return Reflect.get(target, prop); | ||
}, | ||
}; | ||
if ( fn.prototype?.constructor === fn ) { | ||
proxyDetails.construct = function(target, args) { | ||
return handler(proxyApplyFn.CtorContext.factory(target, args)); | ||
}; | ||
} | ||
context[prop] = new Proxy(fn, proxyDetails); | ||
} | ||
registerScriptlet(proxyApplyFn, { | ||
name: 'proxy-apply.fn', | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/******************************************************************************* | ||
uBlock Origin - a comprehensive, efficient content blocker | ||
Copyright (C) 2019-present Raymond Hill | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with this program. If not, see {http://www.gnu.org/licenses/}. | ||
Home: https://github.com/gorhill/uBlock | ||
*/ | ||
|
||
import { parseReplaceFn } from './parse-replace.js'; | ||
import { proxyApplyFn } from './proxy-apply.js'; | ||
import { registerScriptlet } from './base.js'; | ||
import { safeSelf } from './safe-self.js'; | ||
import { validateConstantFn } from './set-constant.js'; | ||
|
||
/** | ||
* @scriptlet trusted-replace-argument.js | ||
* | ||
* @description | ||
* Replace an argument passed to a method. Requires a trusted source. | ||
* | ||
* @param propChain | ||
* The property chain to the function which argument must be replaced when | ||
* called. | ||
* | ||
* @param argposRaw | ||
* The zero-based position of the argument in the argument list. Use a negative | ||
* number for a position relative to the last argument. | ||
* | ||
* @param argraw | ||
* The replacement value, validated using the same heuristic as with the | ||
* `set-constant.js` scriptlet. | ||
* If the replacement value matches `json:...`, the value will be the | ||
* json-parsed string after `json:`. | ||
* If the replacement value matches `repl:/.../.../`, the target argument will | ||
* be replaced according the regex-replacement directive following `repl:` | ||
* | ||
* @param [, condition, pattern] | ||
* Optional. The replacement will occur only when pattern matches the target | ||
* argument. | ||
* | ||
* */ | ||
|
||
export function trustedReplaceArgument( | ||
propChain = '', | ||
argposRaw = '', | ||
argraw = '' | ||
) { | ||
if ( propChain === '' ) { return; } | ||
const safe = safeSelf(); | ||
const logPrefix = safe.makeLogPrefix('trusted-replace-argument', propChain, argposRaw, argraw); | ||
const argoffset = parseInt(argposRaw, 10) || 0; | ||
const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); | ||
const replacer = argraw.startsWith('repl:/') && | ||
parseReplaceFn(argraw.slice(5)) || undefined; | ||
const value = replacer === undefined && | ||
validateConstantFn(true, argraw, extraArgs) || undefined; | ||
const reCondition = extraArgs.condition | ||
? safe.patternToRegex(extraArgs.condition) | ||
: /^/; | ||
proxyApplyFn(propChain, function(context) { | ||
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 ) { | ||
return context.reflect(); | ||
} | ||
const argBefore = callArgs[argpos]; | ||
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}`); | ||
return context.reflect(); | ||
}); | ||
} | ||
registerScriptlet(trustedReplaceArgument, { | ||
name: 'trusted-replace-argument.js', | ||
requiresTrust: true, | ||
dependencies: [ | ||
parseReplaceFn, | ||
proxyApplyFn, | ||
safeSelf, | ||
validateConstantFn, | ||
], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.