From 1c9da227d714250c0b6319e2f635998f9869a70d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 21 Oct 2023 09:31:50 -0400 Subject: [PATCH] Add `trusted-prune-inbound-object` scriptlet As per discussion with filter list maintainers. To perform object pruning for any given call which has an object as argument (hence "inbound"). Since `json-prune-stringify` scriptlet is a specific form of pruning inbound objects, it has been removed. The arguments for `trusted-prune-inbound-object` in order are: - The name of the property to trap. Must be a function, and must exist when the scriptlet tries to install the trap. - The position of the object to prune in the argument list when the trapped function is called. The position is 1-based and must be an integer greater than 0. - The properties to prune (as with `json-prune`) - The properties which must all be present for pruning to occur (as with `json-prune`) - Varargs: - `, dontOverwrite, 1`: do not modify the target inbound object Examples: Remove `title` and `name` properties before passing the object to `JSON.stringify` call: example.org##+js(trusted-prune-inbound-object, JSON.stringify, 1, title name) Remove `status` property before passing the object to `Object.keys` call but do not modify caller's instance of the object: example.org##+js(trusted-prune-inbound-object, Object.keys, 1, status, , dontOverwrite, 1) --- assets/resources/scriptlets.js | 92 +++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 36 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 2e91549b5..f43c9b2c4 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1429,42 +1429,6 @@ function jsonPrune( }); } -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'json-prune-stringify.js', - fn: jsonPruneStringify, - dependencies: [ - 'object-prune.fn', - 'safe-self.fn', - ], -}); -function jsonPruneStringify( - rawPrunePaths = '', - rawNeedlePaths = '', - stackNeedle = '' -) { - const safe = safeSelf(); - const stackNeedleDetails = safe.initPattern(stackNeedle, { canNegate: true }); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); - JSON.stringify = new Proxy(JSON.stringify, { - apply: function(target, thisArg, args) { - const objBefore = args[0]; - if ( objBefore instanceof Object ) { - const objAfter = objectPruneFn( - objBefore, - rawPrunePaths, - rawNeedlePaths, - stackNeedleDetails, - extraArgs - ); - args[0] = objAfter || objBefore; - } - return Reflect.apply(target, thisArg, args); - }, - }); -} - /******************************************************************************* * * json-prune-fetch-response.js @@ -4071,3 +4035,59 @@ function trustedClickElement( } /******************************************************************************/ + +builtinScriptlets.push({ + name: 'trusted-prune-inbound-object.js', + requiresTrust: true, + fn: trustedPruneInboundObject, + dependencies: [ + 'object-prune.fn', + 'safe-self.fn', + ], +}); +function trustedPruneInboundObject( + entryPoint = '', + argPos = '', + rawPrunePaths = '', + rawNeedlePaths = '' +) { + if ( entryPoint === '' ) { return; } + let context = globalThis; + let prop = entryPoint; + 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); + } + if ( typeof context[prop] !== 'function' ) { return; } + const argIndex = parseInt(argPos); + if ( isNaN(argIndex) ) { return; } + if ( argIndex < 1 ) { return; } + const safe = safeSelf(); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 4); + context[prop] = new Proxy(context[prop], { + apply: function(target, thisArg, args) { + const targetArg = argIndex <= args.length + ? args[argIndex-1] + : undefined; + if ( targetArg instanceof Object ) { + const objBefore = extraArgs.dontOverwrite + ? safe.JSON_parse(safe.JSON_stringify(targetArg)) + : targetArg; + const objAfter = objectPruneFn( + objBefore, + rawPrunePaths, + rawNeedlePaths, + { matchAll: true }, + extraArgs + ); + args[argIndex-1] = objAfter || objBefore; + } + return Reflect.apply(target, thisArg, args); + }, + }); +} + +/******************************************************************************/