|
1 | 1 |
|
2 | 2 | /** |
3 | 3 | * AdGuard Scriptlets |
4 | | - * Version 1.9.91 |
| 4 | + * Version 1.9.96 |
5 | 5 | */ |
6 | 6 |
|
7 | 7 | (function () { |
|
2026 | 2026 | */ |
2027 | 2027 | var removeStorageItem = function removeStorageItem(source, storage, key) { |
2028 | 2028 | try { |
2029 | | - storage.removeItem(key); |
| 2029 | + if (key.startsWith('/') && (key.endsWith('/') || key.endsWith('/i')) && isValidStrPattern(key)) { |
| 2030 | + var regExpKey = toRegExp(key); |
| 2031 | + var storageKeys = Object.keys(storage); |
| 2032 | + storageKeys.forEach(function (storageKey) { |
| 2033 | + if (regExpKey.test(storageKey)) { |
| 2034 | + storage.removeItem(storageKey); |
| 2035 | + } |
| 2036 | + }); |
| 2037 | + } else { |
| 2038 | + storage.removeItem(key); |
| 2039 | + } |
2030 | 2040 | } catch (e) { |
2031 | 2041 | var message = "Unable to remove storage item due to: ".concat(e.message); |
2032 | 2042 | logMessage(source, message); |
|
3724 | 3734 | * ### Syntax |
3725 | 3735 | * |
3726 | 3736 | * ```text |
3727 | | - * example.org#%#//scriptlet('set-constant', property, value[, stack]) |
| 3737 | + * example.org#%#//scriptlet('set-constant', property, value[, stack,[ valueWrapper[, setProxyTrap]]]) |
3728 | 3738 | * ``` |
3729 | 3739 | * |
3730 | 3740 | * - `property` — required, path to a property (joined with `.` if needed). The property must be attached to `window`. |
|
3755 | 3765 | * - `asCallback` – function returning callback, that would return value |
3756 | 3766 | * - `asResolved` – Promise that would resolve with value |
3757 | 3767 | * - `asRejected` – Promise that would reject with value |
| 3768 | + * - `setProxyTrap` – optional, boolean, if set to true, proxy trap will be set on the object |
3758 | 3769 | * |
3759 | 3770 | * ### Examples |
3760 | 3771 | * |
|
3794 | 3805 | * ✔ document.fifth.catch((reason) => reason === 42) // promise rejects with specified number |
3795 | 3806 | * ``` |
3796 | 3807 | * |
| 3808 | + * ```adblock |
| 3809 | + * ! Any access to `window.foo.bar` will return `false` and the proxy trap will be set on the `foo` object |
| 3810 | + * ! It may be required in the case when `foo` object is overwritten by website script |
| 3811 | + * ! Related to this issue - https://github.com/AdguardTeam/Scriptlets/issues/330 |
| 3812 | + * example.org#%#//scriptlet('set-constant', 'foo.bar', 'false', '', '', 'true') |
| 3813 | + * |
| 3814 | + * ✔ window.foo.bar === false |
| 3815 | + * ``` |
| 3816 | + * |
3797 | 3817 | * @added v1.0.4. |
3798 | 3818 | */ |
3799 | 3819 | /* eslint-enable max-len */ |
3800 | 3820 | function setConstant$1(source, property, value) { |
3801 | 3821 | var stack = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ''; |
3802 | 3822 | var valueWrapper = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : ''; |
| 3823 | + var setProxyTrap = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false; |
3803 | 3824 | var uboAliases = ['set-constant.js', 'ubo-set-constant.js', 'set.js', 'ubo-set.js', 'ubo-set-constant', 'ubo-set']; |
3804 | 3825 |
|
3805 | 3826 | /** |
|
3961 | 3982 | // Get properties which should be checked and remove first one |
3962 | 3983 | // because it's current object |
3963 | 3984 | var propertiesToCheck = property.split('.').slice(1); |
3964 | | - if (!isProxyTrapSet) { |
| 3985 | + if (setProxyTrap && !isProxyTrapSet) { |
3965 | 3986 | isProxyTrapSet = true; |
3966 | 3987 | a = new Proxy(a, { |
3967 | 3988 | get: function get(target, propertyKey, val) { |
|
6532 | 6553 | * example.com#%#//scriptlet('set-local-storage-item', 'key', 'value') |
6533 | 6554 | * ``` |
6534 | 6555 | * |
6535 | | - * - `key` — required, key name to be set. |
| 6556 | + * - `key` — required, key name to be set. Should be a string for setting, |
| 6557 | + * but it also can be a regular expression for removing items from localStorage. |
6536 | 6558 | * - `value` — required, key value; possible values: |
6537 | 6559 | * - positive decimal integer `<= 32767` |
6538 | 6560 | * - one of the predefined constants in any case variation: |
|
6558 | 6580 | * |
6559 | 6581 | * ! Removes the item with key 'foo' from local storage |
6560 | 6582 | * example.org#%#//scriptlet('set-local-storage-item', 'foo', '$remove$') |
| 6583 | + * |
| 6584 | + * ! Removes from local storage all items whose key matches the regular expression `/mp_.*_mixpanel/` |
| 6585 | + * example.org#%#//scriptlet('set-local-storage-item', '/mp_.*_mixpanel/', '$remove$') |
6561 | 6586 | * ``` |
6562 | 6587 | * |
6563 | 6588 | * @added v1.4.3. |
|
6588 | 6613 | setLocalStorageItem$1.names = ['set-local-storage-item', |
6589 | 6614 | // aliases are needed for matching the related scriptlet converted into our syntax |
6590 | 6615 | 'set-local-storage-item.js', 'ubo-set-local-storage-item.js', 'ubo-set-local-storage-item']; |
6591 | | - setLocalStorageItem$1.injections = [hit, logMessage, nativeIsNaN, setStorageItem, removeStorageItem, getLimitedStorageItemValue]; |
| 6616 | + setLocalStorageItem$1.injections = [hit, logMessage, nativeIsNaN, setStorageItem, removeStorageItem, getLimitedStorageItemValue, |
| 6617 | + // following helpers are needed for helpers above |
| 6618 | + isValidStrPattern, toRegExp, escapeRegExp]; |
6592 | 6619 |
|
6593 | 6620 | /* eslint-disable max-len */ |
6594 | 6621 | /** |
|
6609 | 6636 | * example.com#%#//scriptlet('set-session-storage-item', 'key', 'value') |
6610 | 6637 | * ``` |
6611 | 6638 | * |
6612 | | - * - `key` — required, key name to be set. |
| 6639 | + * - `key` — required, key name to be set. Should be a string for setting, |
| 6640 | + * but it also can be a regular expression for removing items from localStorage. |
6613 | 6641 | * - `value` — required, key value; possible values: |
6614 | 6642 | * - positive decimal integer `<= 32767` |
6615 | 6643 | * - one of the predefined constants in any case variation: |
|
6635 | 6663 | * |
6636 | 6664 | * ! Removes the item with key 'foo' from session storage |
6637 | 6665 | * example.org#%#//scriptlet('set-session-storage-item', 'foo', '$remove$') |
| 6666 | + * |
| 6667 | + * ! Removes from session storage all items whose key matches the regular expression `/mp_.*_mixpanel/` |
| 6668 | + * example.org#%#//scriptlet('set-session-storage-item', '/mp_.*_mixpanel/', '$remove$') |
6638 | 6669 | * ``` |
6639 | 6670 | * |
6640 | 6671 | * @added v1.4.3. |
|
6665 | 6696 | setSessionStorageItem$1.names = ['set-session-storage-item', |
6666 | 6697 | // aliases are needed for matching the related scriptlet converted into our syntax |
6667 | 6698 | 'set-session-storage-item.js', 'ubo-set-session-storage-item.js', 'ubo-set-session-storage-item']; |
6668 | | - setSessionStorageItem$1.injections = [hit, logMessage, nativeIsNaN, setStorageItem, removeStorageItem, getLimitedStorageItemValue]; |
| 6699 | + setSessionStorageItem$1.injections = [hit, logMessage, nativeIsNaN, setStorageItem, removeStorageItem, getLimitedStorageItemValue, |
| 6700 | + // following helpers are needed for helpers above |
| 6701 | + isValidStrPattern, toRegExp, escapeRegExp]; |
6669 | 6702 |
|
6670 | 6703 | /* eslint-disable max-len */ |
6671 | 6704 | /** |
|
9922 | 9955 | * example.org#%#//scriptlet('trusted-prune-inbound-object', 'JSON.stringify', '', 'bar', '') |
9923 | 9956 | * ``` |
9924 | 9957 | * |
9925 | | - * @added unknown. |
| 9958 | + * @added v1.9.91. |
9926 | 9959 | */ |
9927 | 9960 | /* eslint-enable max-len */ |
9928 | 9961 | function trustedPruneInboundObject$1(source, functionName, propsToRemove, requiredInitialProps) { |
|
24458 | 24491 | function setConstant(source, property, value) { |
24459 | 24492 | var stack = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ""; |
24460 | 24493 | var valueWrapper = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : ""; |
| 24494 | + var setProxyTrap = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false; |
24461 | 24495 | var uboAliases = ["set-constant.js", "ubo-set-constant.js", "set.js", "ubo-set.js", "ubo-set-constant", "ubo-set"]; |
24462 | 24496 | if (uboAliases.includes(source.name)) { |
24463 | 24497 | if (stack.length !== 1 && !getNumberFromString(stack)) { |
|
24579 | 24613 | } |
24580 | 24614 | if (a instanceof Object) { |
24581 | 24615 | var propertiesToCheck = property.split(".").slice(1); |
24582 | | - if (!isProxyTrapSet) { |
| 24616 | + if (setProxyTrap && !isProxyTrapSet) { |
24583 | 24617 | isProxyTrapSet = true; |
24584 | 24618 | a = new Proxy(a, { |
24585 | 24619 | get: function get(target, propertyKey, val) { |
|
25258 | 25292 | } |
25259 | 25293 | function removeStorageItem(source, storage, key) { |
25260 | 25294 | try { |
25261 | | - storage.removeItem(key); |
| 25295 | + if (key.startsWith("/") && (key.endsWith("/") || key.endsWith("/i")) && isValidStrPattern(key)) { |
| 25296 | + var regExpKey = toRegExp(key); |
| 25297 | + var storageKeys = Object.keys(storage); |
| 25298 | + storageKeys.forEach(function (storageKey) { |
| 25299 | + if (regExpKey.test(storageKey)) { |
| 25300 | + storage.removeItem(storageKey); |
| 25301 | + } |
| 25302 | + }); |
| 25303 | + } else { |
| 25304 | + storage.removeItem(key); |
| 25305 | + } |
25262 | 25306 | } catch (e) { |
25263 | 25307 | var message = "Unable to remove storage item due to: ".concat(e.message); |
25264 | 25308 | logMessage(source, message); |
|
25291 | 25335 | } |
25292 | 25336 | return validValue; |
25293 | 25337 | } |
| 25338 | + function isValidStrPattern(input) { |
| 25339 | + var FORWARD_SLASH = "/"; |
| 25340 | + var str = escapeRegExp(input); |
| 25341 | + if (input[0] === FORWARD_SLASH && input[input.length - 1] === FORWARD_SLASH) { |
| 25342 | + str = input.slice(1, -1); |
| 25343 | + } |
| 25344 | + var isValid; |
| 25345 | + try { |
| 25346 | + isValid = new RegExp(str); |
| 25347 | + isValid = true; |
| 25348 | + } catch (e) { |
| 25349 | + isValid = false; |
| 25350 | + } |
| 25351 | + return isValid; |
| 25352 | + } |
| 25353 | + function toRegExp() { |
| 25354 | + var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ""; |
| 25355 | + var DEFAULT_VALUE = ".?"; |
| 25356 | + var FORWARD_SLASH = "/"; |
| 25357 | + if (input === "") { |
| 25358 | + return new RegExp(DEFAULT_VALUE); |
| 25359 | + } |
| 25360 | + var delimiterIndex = input.lastIndexOf(FORWARD_SLASH); |
| 25361 | + var flagsPart = input.substring(delimiterIndex + 1); |
| 25362 | + var regExpPart = input.substring(0, delimiterIndex + 1); |
| 25363 | + var isValidRegExpFlag = function isValidRegExpFlag(flag) { |
| 25364 | + if (!flag) { |
| 25365 | + return false; |
| 25366 | + } |
| 25367 | + try { |
| 25368 | + new RegExp("", flag); |
| 25369 | + return true; |
| 25370 | + } catch (ex) { |
| 25371 | + return false; |
| 25372 | + } |
| 25373 | + }; |
| 25374 | + var getRegExpFlags = function getRegExpFlags(regExpStr, flagsStr) { |
| 25375 | + if (regExpStr.startsWith(FORWARD_SLASH) && regExpStr.endsWith(FORWARD_SLASH) && !regExpStr.endsWith("\\/") && isValidRegExpFlag(flagsStr)) { |
| 25376 | + return flagsStr; |
| 25377 | + } |
| 25378 | + return ""; |
| 25379 | + }; |
| 25380 | + var flags = getRegExpFlags(regExpPart, flagsPart); |
| 25381 | + if (input.startsWith(FORWARD_SLASH) && input.endsWith(FORWARD_SLASH) || flags) { |
| 25382 | + var regExpInput = flags ? regExpPart : input; |
| 25383 | + return new RegExp(regExpInput.slice(1, -1), flags); |
| 25384 | + } |
| 25385 | + var escaped = input.replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); |
| 25386 | + return new RegExp(escaped); |
| 25387 | + } |
| 25388 | + function escapeRegExp(str) { |
| 25389 | + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); |
| 25390 | + } |
25294 | 25391 | var updatedArgs = args ? [].concat(source).concat(args) : [source]; |
25295 | 25392 | try { |
25296 | 25393 | setLocalStorageItem.apply(this, updatedArgs); |
|
25435 | 25532 | } |
25436 | 25533 | function removeStorageItem(source, storage, key) { |
25437 | 25534 | try { |
25438 | | - storage.removeItem(key); |
| 25535 | + if (key.startsWith("/") && (key.endsWith("/") || key.endsWith("/i")) && isValidStrPattern(key)) { |
| 25536 | + var regExpKey = toRegExp(key); |
| 25537 | + var storageKeys = Object.keys(storage); |
| 25538 | + storageKeys.forEach(function (storageKey) { |
| 25539 | + if (regExpKey.test(storageKey)) { |
| 25540 | + storage.removeItem(storageKey); |
| 25541 | + } |
| 25542 | + }); |
| 25543 | + } else { |
| 25544 | + storage.removeItem(key); |
| 25545 | + } |
25439 | 25546 | } catch (e) { |
25440 | 25547 | var message = "Unable to remove storage item due to: ".concat(e.message); |
25441 | 25548 | logMessage(source, message); |
|
25468 | 25575 | } |
25469 | 25576 | return validValue; |
25470 | 25577 | } |
| 25578 | + function isValidStrPattern(input) { |
| 25579 | + var FORWARD_SLASH = "/"; |
| 25580 | + var str = escapeRegExp(input); |
| 25581 | + if (input[0] === FORWARD_SLASH && input[input.length - 1] === FORWARD_SLASH) { |
| 25582 | + str = input.slice(1, -1); |
| 25583 | + } |
| 25584 | + var isValid; |
| 25585 | + try { |
| 25586 | + isValid = new RegExp(str); |
| 25587 | + isValid = true; |
| 25588 | + } catch (e) { |
| 25589 | + isValid = false; |
| 25590 | + } |
| 25591 | + return isValid; |
| 25592 | + } |
| 25593 | + function toRegExp() { |
| 25594 | + var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ""; |
| 25595 | + var DEFAULT_VALUE = ".?"; |
| 25596 | + var FORWARD_SLASH = "/"; |
| 25597 | + if (input === "") { |
| 25598 | + return new RegExp(DEFAULT_VALUE); |
| 25599 | + } |
| 25600 | + var delimiterIndex = input.lastIndexOf(FORWARD_SLASH); |
| 25601 | + var flagsPart = input.substring(delimiterIndex + 1); |
| 25602 | + var regExpPart = input.substring(0, delimiterIndex + 1); |
| 25603 | + var isValidRegExpFlag = function isValidRegExpFlag(flag) { |
| 25604 | + if (!flag) { |
| 25605 | + return false; |
| 25606 | + } |
| 25607 | + try { |
| 25608 | + new RegExp("", flag); |
| 25609 | + return true; |
| 25610 | + } catch (ex) { |
| 25611 | + return false; |
| 25612 | + } |
| 25613 | + }; |
| 25614 | + var getRegExpFlags = function getRegExpFlags(regExpStr, flagsStr) { |
| 25615 | + if (regExpStr.startsWith(FORWARD_SLASH) && regExpStr.endsWith(FORWARD_SLASH) && !regExpStr.endsWith("\\/") && isValidRegExpFlag(flagsStr)) { |
| 25616 | + return flagsStr; |
| 25617 | + } |
| 25618 | + return ""; |
| 25619 | + }; |
| 25620 | + var flags = getRegExpFlags(regExpPart, flagsPart); |
| 25621 | + if (input.startsWith(FORWARD_SLASH) && input.endsWith(FORWARD_SLASH) || flags) { |
| 25622 | + var regExpInput = flags ? regExpPart : input; |
| 25623 | + return new RegExp(regExpInput.slice(1, -1), flags); |
| 25624 | + } |
| 25625 | + var escaped = input.replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); |
| 25626 | + return new RegExp(escaped); |
| 25627 | + } |
| 25628 | + function escapeRegExp(str) { |
| 25629 | + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); |
| 25630 | + } |
25471 | 25631 | var updatedArgs = args ? [].concat(source).concat(args) : [source]; |
25472 | 25632 | try { |
25473 | 25633 | setSessionStorageItem.apply(this, updatedArgs); |
|
0 commit comments