Skip to content

Commit

Permalink
Move jsonPruner and isPruningNeeded to helpers
Browse files Browse the repository at this point in the history
Fix description
  • Loading branch information
AdamWr committed Jun 1, 2023
1 parent 318084d commit 8911c2e
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 178 deletions.
1 change: 1 addition & 0 deletions src/helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from './script-source-utils';
export * from './open-shadow-dom-utils';
export * from './prevent-utils';
export * from './prevent-window-open-utils';
export * from './prune-utils';
export * from './regexp-utils';
export * from './response-utils';
export * from './request-utils';
Expand Down
108 changes: 108 additions & 0 deletions src/helpers/prune-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { hit } from './hit';
import { getWildcardPropertyInChain } from './get-wildcard-property-in-chain';
import { logMessage } from './log-message';
import { toRegExp } from './string-utils';

/**
* Checks if prunning is required
*
* @param {Object} source required, scriptlet properties
* @param {Object} root object which should be pruned or logged
* @param {Array} prunePaths array with string of space-separated properties to remove
* @param {Array} requiredPaths array with string of space-separated properties
* which must be all present for the pruning to occur
* @returns {boolean} true if prunning is required
*/
export function isPruningNeeded(source, root, prunePaths, requiredPaths) {
if (!root) {
return false;
}

let shouldProcess;

// Only log hostname and matched JSON payload if only second argument is present
if (prunePaths.length === 0 && requiredPaths.length > 0) {
const rootString = JSON.stringify(root);
const matchRegex = toRegExp(requiredPaths.join(''));
const shouldLog = matchRegex.test(rootString);
if (shouldLog) {
logMessage(source, `${window.location.hostname}\n${JSON.stringify(root, null, 2)}`, true);
if (root && typeof root === 'object') {
logMessage(source, root, true, false);
}
shouldProcess = false;
return shouldProcess;
}
}

const wildcardSymbols = ['.*.', '*.', '.*', '.[].', '[].', '.[]'];

for (let i = 0; i < requiredPaths.length; i += 1) {
const requiredPath = requiredPaths[i];
const lastNestedPropName = requiredPath.split('.').pop();
const hasWildcard = wildcardSymbols.some((symbol) => requiredPath.includes(symbol));

// if the path has wildcard, getPropertyInChain should 'look through' chain props
const details = getWildcardPropertyInChain(root, requiredPath, hasWildcard);

// start value of 'shouldProcess' due to checking below
shouldProcess = !hasWildcard;

for (let i = 0; i < details.length; i += 1) {
if (hasWildcard) {
// if there is a wildcard,
// at least one (||) of props chain should be present in object
shouldProcess = !(details[i].base[lastNestedPropName] === undefined)
|| shouldProcess;
} else {
// otherwise each one (&&) of them should be there
shouldProcess = !(details[i].base[lastNestedPropName] === undefined)
&& shouldProcess;
}
}
}

return shouldProcess;
}

/**
* Prunes properties of 'root' object
*
* @param {Object} source required, scriptlet properties
* @param {Object} root object which should be pruned or logged
* @param {Array} prunePaths array with string of space-separated properties to remove
* @param {Array} requiredPaths array with string of space-separated properties
* which must be all present for the pruning to occur
* @returns {Object} pruned root
*/
export const jsonPruner = (source, root, prunePaths, requiredPaths) => {
if (prunePaths.length === 0 && requiredPaths.length === 0) {
logMessage(source, `${window.location.hostname}\n${JSON.stringify(root, null, 2)}`, true);
if (root && typeof root === 'object') {
logMessage(source, root, true, false);
}
return root;
}

try {
if (isPruningNeeded(source, root, prunePaths, requiredPaths) === false) {
return root;
}

// if pruning is needed, we check every input pathToRemove
// and delete it if root has it
prunePaths.forEach((path) => {
const ownerObjArr = getWildcardPropertyInChain(root, path, true);
ownerObjArr.forEach((ownerObj) => {
if (ownerObj !== undefined && ownerObj.base) {
delete ownerObj.base[ownerObj.prop];
hit(source);
}
});
});
} catch (e) {
logMessage(source, e);
}

return root;
};
94 changes: 8 additions & 86 deletions src/scriptlets/evaldata-prune.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ import {
getWildcardPropertyInChain,
logMessage,
toRegExp,
isPruningNeeded,
jsonPruner,
// following helpers are needed for helpers above
getNativeRegexpTest,
shouldAbortInlineOrInjectedScript,
} from '../helpers/index';

/* eslint-disable max-len */
/**
* @scriptlet evaldata-prune
*
* @description
* Removes specified properties from the result of calling eval (if payloads contains `Object`) and returns the caller.
* Removes specified properties from the result of calling eval (if payloads contains `Object`) and returns to the caller.
*
* ### Syntax
*
Expand Down Expand Up @@ -88,6 +91,7 @@ import {
*
* @added unreleased.
*/
/* eslint-enable max-len */
export function evalDataPrune(source, propsToRemove, requiredInitialProps, stack) {
if (!!stack && !matchStackTrace(stack, new Error().stack)) {
return;
Expand All @@ -99,94 +103,10 @@ export function evalDataPrune(source, propsToRemove, requiredInitialProps, stack
? requiredInitialProps.split(/ +/)
: [];

function isPruningNeeded(root) {
if (!root) {
return false;
}

let shouldProcess;

// Only log hostname and matched JSON payload if only second argument is present
if (prunePaths.length === 0 && requiredPaths.length > 0) {
const rootString = JSON.stringify(root);
const matchRegex = toRegExp(requiredPaths.join(''));
const shouldLog = matchRegex.test(rootString);
if (shouldLog) {
logMessage(source, `${window.location.hostname}\n${JSON.stringify(root, null, 2)}`, true);
if (root && typeof root === 'object') {
logMessage(source, root, true, false);
}
shouldProcess = false;
return shouldProcess;
}
}

for (let i = 0; i < requiredPaths.length; i += 1) {
const requiredPath = requiredPaths[i];
const lastNestedPropName = requiredPath.split('.').pop();

const wildcardSymbols = ['.*.', '*.', '.*', '.[].', '[].', '.[]'];
const hasWildcard = wildcardSymbols.some((symbol) => requiredPath.includes(symbol));

// if the path has wildcard, getPropertyInChain should 'look through' chain props
const details = getWildcardPropertyInChain(root, requiredPath, hasWildcard);

// start value of 'shouldProcess' due to checking below
shouldProcess = !hasWildcard;

for (let i = 0; i < details.length; i += 1) {
if (hasWildcard) {
// if there is a wildcard,
// at least one (||) of props chain should be present in object
shouldProcess = !(details[i].base[lastNestedPropName] === undefined)
|| shouldProcess;
} else {
// otherwise each one (&&) of them should be there
shouldProcess = !(details[i].base[lastNestedPropName] === undefined)
&& shouldProcess;
}
}
}

return shouldProcess;
}

const jsonPruner = (root) => {
if (prunePaths.length === 0 && requiredPaths.length === 0) {
logMessage(source, `${window.location.hostname}\n${JSON.stringify(root, null, 2)}`, true);
if (root && typeof root === 'object') {
logMessage(source, root, true, false);
}
return root;
}

try {
if (isPruningNeeded(root) === false) {
return root;
}

// if pruning is needed, we check every input pathToRemove
// and delete it if root has it
prunePaths.forEach((path) => {
const ownerObjArr = getWildcardPropertyInChain(root, path, true);
ownerObjArr.forEach((ownerObj) => {
if (ownerObj !== undefined && ownerObj.base) {
delete ownerObj.base[ownerObj.prop];
hit(source);
}
});
});
} catch (e) {
logMessage(source, e);
}

return root;
};

const evalWrapper = (target, thisArg, args) => {
let data = Reflect.apply(target, thisArg, args);
if (typeof data === 'object') {
data = jsonPruner(data, propsToRemove, requiredInitialProps);
data = jsonPruner(source, data, prunePaths, requiredPaths);
}
return data;
};
Expand All @@ -212,6 +132,8 @@ evalDataPrune.injections = [
getWildcardPropertyInChain,
logMessage,
toRegExp,
isPruningNeeded,
jsonPruner,
// following helpers are needed for helpers above
getNativeRegexpTest,
shouldAbortInlineOrInjectedScript,
Expand Down
98 changes: 6 additions & 92 deletions src/scriptlets/json-prune.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
matchStackTrace,
getWildcardPropertyInChain,
logMessage,
isPruningNeeded,
jsonPruner,
// following helpers are needed for helpers above
toRegExp,
getNativeRegexpTest,
Expand Down Expand Up @@ -108,102 +110,12 @@ export function jsonPrune(source, propsToRemove, requiredInitialProps, stack) {
? requiredInitialProps.split(/ +/)
: [];

function isPruningNeeded(root) {
if (!root) {
return false;
}

let shouldProcess;

// Only log hostname and matched JSON payload if only second argument is present
if (prunePaths.length === 0 && requiredPaths.length > 0) {
const rootString = JSON.stringify(root);
const matchRegex = toRegExp(requiredPaths.join(''));
const shouldLog = matchRegex.test(rootString);
if (shouldLog) {
logMessage(source, `${window.location.hostname}\n${JSON.stringify(root, null, 2)}`, true);
if (root && typeof root === 'object') {
logMessage(source, root, true, false);
}
shouldProcess = false;
return shouldProcess;
}
}

for (let i = 0; i < requiredPaths.length; i += 1) {
const requiredPath = requiredPaths[i];
const lastNestedPropName = requiredPath.split('.').pop();

const wildcardSymbols = ['.*.', '*.', '.*', '.[].', '[].', '.[]'];
const hasWildcard = wildcardSymbols.some((symbol) => requiredPath.includes(symbol));

// if the path has wildcard, getPropertyInChain should 'look through' chain props
const details = getWildcardPropertyInChain(root, requiredPath, hasWildcard);

// start value of 'shouldProcess' due to checking below
shouldProcess = !hasWildcard;

for (let i = 0; i < details.length; i += 1) {
if (hasWildcard) {
// if there is a wildcard,
// at least one (||) of props chain should be present in object
shouldProcess = !(details[i].base[lastNestedPropName] === undefined)
|| shouldProcess;
} else {
// otherwise each one (&&) of them should be there
shouldProcess = !(details[i].base[lastNestedPropName] === undefined)
&& shouldProcess;
}
}
}

return shouldProcess;
}

/**
* Prunes properties of 'root' object
*
* @param {Object} root
* @returns {Object} pruned root
*/
const jsonPruner = (root) => {
if (prunePaths.length === 0 && requiredPaths.length === 0) {
logMessage(source, `${window.location.hostname}\n${JSON.stringify(root, null, 2)}`, true);
if (root && typeof root === 'object') {
logMessage(source, root, true, false);
}
return root;
}

try {
if (isPruningNeeded(root) === false) {
return root;
}

// if pruning is needed, we check every input pathToRemove
// and delete it if root has it
prunePaths.forEach((path) => {
const ownerObjArr = getWildcardPropertyInChain(root, path, true);
ownerObjArr.forEach((ownerObj) => {
if (ownerObj !== undefined && ownerObj.base) {
delete ownerObj.base[ownerObj.prop];
hit(source);
}
});
});
} catch (e) {
logMessage(source, e);
}

return root;
};

const nativeJSONParse = JSON.parse;
const jsonParseWrapper = (...args) => {
// dealing with stringified json in args, which should be parsed.
// so we call nativeJSONParse as JSON.parse which is bound to JSON object
const root = nativeJSONParse.apply(JSON, args);
return jsonPruner(root);
return jsonPruner(source, root, prunePaths, requiredPaths);
};

// JSON.parse mocking
Expand All @@ -215,7 +127,7 @@ export function jsonPrune(source, propsToRemove, requiredInitialProps, stack) {
const responseJsonWrapper = function () {
const promise = nativeResponseJson.apply(this);
return promise.then((obj) => {
return jsonPruner(obj);
return jsonPruner(source, obj, prunePaths, requiredPaths);
});
};

Expand All @@ -242,6 +154,8 @@ jsonPrune.injections = [
matchStackTrace,
getWildcardPropertyInChain,
logMessage,
isPruningNeeded,
jsonPruner,
// following helpers are needed for helpers above
toRegExp,
getNativeRegexpTest,
Expand Down

0 comments on commit 8911c2e

Please sign in to comment.