replace-argument.js (4597B) - View raw
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122/******************************************************************************* 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. Use literal `this` to * replace the value used in `prototype`-based methods. * * @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); const reCondition = extraArgs.condition ? safe.patternToRegex(extraArgs.condition) : /^/; const getArg = context => { if ( argposRaw === 'this' ) { return context.thisArg; } const { callArgs } = context; const argpos = argoffset >= 0 ? argoffset : callArgs.length - argoffset; 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 = getArg(context); if ( extraArgs.condition !== undefined ) { if ( safe.RegExp_test.call(reCondition, argBefore) === false ) { return context.reflect(); } } const argAfter = replacer && typeof argBefore === 'string' ? argBefore.replace(replacer.re, replacer.replacement) : value; if ( argAfter !== argBefore ) { setArg(context, 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, ], });