Skip to content

Commit

Permalink
AG-17608 Improve storage item scriptlets — add regexp support for ite…
Browse files Browse the repository at this point in the history
…ms removing #256

Squashed commit of the following:

commit 7d5d6f2
Merge: df0ecb7 612f03d
Author: Adam Wróblewski <[email protected]>
Date:   Wed Nov 15 13:43:54 2023 +0100

    Merge branch 'master' into feature/AG-17608

commit df0ecb7
Author: Adam Wróblewski <[email protected]>
Date:   Wed Nov 15 13:32:31 2023 +0100

    Update docs and add example
    Fix detecting regexp

commit a14c86d
Author: Slava Leleka <[email protected]>
Date:   Wed Nov 15 14:27:22 2023 +0300

    Update description

commit 122693a
Merge: f94268f b50f9c9
Author: Adam Wróblewski <[email protected]>
Date:   Tue Nov 14 19:17:58 2023 +0100

    Merge branch 'master' into feature/AG-17608

commit f94268f
Author: Adam Wróblewski <[email protected]>
Date:   Tue Nov 14 10:39:30 2023 +0100

    Improve storage item scriptlets — add regexp support for items removing
  • Loading branch information
AdamWr committed Nov 15, 2023
1 parent 612f03d commit 391e4a7
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 45 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- regular expression support for removing items in `set-local-storage-item` and `set-session-storage-item` scriptlets
[#256](https://github.com/AdguardTeam/Scriptlets/issues/256)
- ability to set proxy trap in `set-constant` scriptlet [#330](https://github.com/AdguardTeam/Scriptlets/issues/330)

## [v1.9.91] - 2023-11-13
Expand Down
15 changes: 14 additions & 1 deletion src/helpers/storage-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { nativeIsNaN } from './number-utils';
import { logMessage } from './log-message';
import { isValidStrPattern, toRegExp } from './string-utils';

/**
* Sets item to a specified storage, if storage isn't full.
Expand Down Expand Up @@ -29,7 +30,19 @@ export const setStorageItem = (source: Source, storage: Storage, key: string, va
*/
export const removeStorageItem = (source: Source, storage: Storage, key: string): void => {
try {
storage.removeItem(key);
if (key.startsWith('/')
&& (key.endsWith('/') || key.endsWith('/i'))
&& isValidStrPattern(key)) {
const regExpKey = toRegExp(key);
const storageKeys = Object.keys(storage);
storageKeys.forEach((storageKey) => {
if (regExpKey.test(storageKey)) {
storage.removeItem(storageKey);
}
});
} else {
storage.removeItem(key);
}
} catch (e) {
const message = `Unable to remove storage item due to: ${(e as Error).message}`;
logMessage(source, message);
Expand Down
14 changes: 13 additions & 1 deletion src/scriptlets/set-local-storage-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {
setStorageItem,
removeStorageItem,
getLimitedStorageItemValue,
// following helpers are needed for helpers above
isValidStrPattern,
toRegExp,
escapeRegExp,
} from '../helpers/index';

/* eslint-disable max-len */
Expand All @@ -26,7 +30,8 @@ import {
* example.com#%#//scriptlet('set-local-storage-item', 'key', 'value')
* ```
*
* - `key` — required, key name to be set.
* - `key` — required, key name to be set. Should be a string for setting,
* but it also can be a regular expression for removing items from localStorage.
* - `value` — required, key value; possible values:
* - positive decimal integer `<= 32767`
* - one of the predefined constants in any case variation:
Expand All @@ -52,6 +57,9 @@ import {
*
* ! Removes the item with key 'foo' from local storage
* example.org#%#//scriptlet('set-local-storage-item', 'foo', '$remove$')
*
* ! Removes from local storage all items whose key matches the regular expression `/mp_.*_mixpanel/`
* example.org#%#//scriptlet('set-local-storage-item', '/mp_.*_mixpanel/', '$remove$')
* ```
*
* @added v1.4.3.
Expand Down Expand Up @@ -96,4 +104,8 @@ setLocalStorageItem.injections = [
setStorageItem,
removeStorageItem,
getLimitedStorageItemValue,
// following helpers are needed for helpers above
isValidStrPattern,
toRegExp,
escapeRegExp,
];
14 changes: 13 additions & 1 deletion src/scriptlets/set-session-storage-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {
setStorageItem,
removeStorageItem,
getLimitedStorageItemValue,
// following helpers are needed for helpers above
isValidStrPattern,
toRegExp,
escapeRegExp,
} from '../helpers/index';

/* eslint-disable max-len */
Expand All @@ -26,7 +30,8 @@ import {
* example.com#%#//scriptlet('set-session-storage-item', 'key', 'value')
* ```
*
* - `key` — required, key name to be set.
* - `key` — required, key name to be set. Should be a string for setting,
* but it also can be a regular expression for removing items from localStorage.
* - `value` — required, key value; possible values:
* - positive decimal integer `<= 32767`
* - one of the predefined constants in any case variation:
Expand All @@ -52,6 +57,9 @@ import {
*
* ! Removes the item with key 'foo' from session storage
* example.org#%#//scriptlet('set-session-storage-item', 'foo', '$remove$')
*
* ! Removes from session storage all items whose key matches the regular expression `/mp_.*_mixpanel/`
* example.org#%#//scriptlet('set-session-storage-item', '/mp_.*_mixpanel/', '$remove$')
* ```
*
* @added v1.4.3.
Expand Down Expand Up @@ -96,4 +104,8 @@ setSessionStorageItem.injections = [
setStorageItem,
removeStorageItem,
getLimitedStorageItemValue,
// following helpers are needed for helpers above
isValidStrPattern,
toRegExp,
escapeRegExp,
];
62 changes: 62 additions & 0 deletions tests/scriptlets/set-local-storage-item.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,66 @@ if (isSafariBrowser()) {
assert.strictEqual(window.localStorage.getItem(iName), null, 'localStorage item has been removed');
clearStorageItem(iName);
});

test('Remove item from localStorage - regexp', (assert) => {
const iName = '/__test-.*_regexp/';
const iValue = '$remove$';
const firstRegexpStorageItem = '__test-first_item_remove_regexp';
const secondRegexpStorageItem = '__test-second_item_remove_regexp';

localStorage.setItem(firstRegexpStorageItem, '1');
localStorage.setItem(secondRegexpStorageItem, '2');

runScriptlet(name, [iName, iValue]);
assert.strictEqual(window.hit, 'FIRED', 'Hit was fired');
assert.strictEqual(
window.localStorage.getItem(firstRegexpStorageItem),
null,
'localStorage item has been removed',
);
assert.strictEqual(
window.localStorage.getItem(secondRegexpStorageItem),
null,
'localStorage item has been removed',
);
clearStorageItem(firstRegexpStorageItem);
clearStorageItem(secondRegexpStorageItem);
});

test('Remove item from localStorage - regexp with flag i', (assert) => {
const iName = '/^__test-.*_regexp_case-insensitive/i';
const iValue = '$remove$';
const caseInsensitiveRegexpStorageItem = '__test-first_item_remove_regexp_CASE-inSensitive';

localStorage.setItem(caseInsensitiveRegexpStorageItem, 'abc');

runScriptlet(name, [iName, iValue]);
assert.strictEqual(window.hit, 'FIRED', 'Hit was fired');
assert.strictEqual(
window.localStorage.getItem(caseInsensitiveRegexpStorageItem),
null,
'localStorage item has been removed',
);
clearStorageItem(caseInsensitiveRegexpStorageItem);
});

test('Remove item from localStorage - not regexp, starts with forward slash', (assert) => {
const iName = '/__test-';
const iValue = '$remove$';

localStorage.setItem(iName, '1');
// should not be removed
localStorage.setItem('/__test-2', '2');

runScriptlet(name, [iName, iValue]);
assert.strictEqual(window.hit, 'FIRED', 'Hit was fired');
assert.strictEqual(window.localStorage.getItem(iName), null, 'localStorage item has been removed');
assert.strictEqual(
window.localStorage.getItem('/__test-2'),
'2',
'not matched localStorage item should not be removed',
);
clearStorageItem(iName);
clearStorageItem('/__test-2');
});
}
146 changes: 104 additions & 42 deletions tests/scriptlets/set-session-storage-item.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,46 +136,108 @@ if (isSafariBrowser()) {
assert.strictEqual(window.sessionStorage.getItem(cName), 'off', 'sessionStorage item has been set');
clearStorageItem(cName);
});
}

test('Set sessionStorage key with invalid value', (assert) => {
let cName = '__test-item_arrayItem';
let cValue = '["item"]';
runScriptlet(name, [cName, cValue]);
assert.strictEqual(window.hit, undefined, 'Hit was not fired');
assert.strictEqual(window.sessionStorage.getItem(cName), null, 'sessionStorage item has not been set');
clearStorageItem(cName);

cName = '__test-item_object';
cValue = '{"key":value"}';
runScriptlet(name, [cName, cValue]);
assert.strictEqual(window.hit, undefined, 'Hit was not fired');
assert.strictEqual(window.sessionStorage.getItem(cName), null, 'sessionStorage item has not been set');
clearStorageItem(cName);

cName = '__test-item_str';
cValue = 'test_string';
runScriptlet(name, [cName, cValue]);
assert.strictEqual(window.hit, undefined, 'Hit was not fired');
assert.strictEqual(window.sessionStorage.getItem(cName), null, 'sessionStorage item has not been set');
clearStorageItem(cName);

cName = '__test-item_bigInt';
cValue = '999999';
runScriptlet(name, [cName, cValue]);
assert.strictEqual(window.hit, undefined, 'Hit was not fired');
assert.strictEqual(window.sessionStorage.getItem(cName), null, 'sessionStorage item has not been set');
clearStorageItem(cName);
});

test('Remove item from sessionStorage', (assert) => {
const cName = '__test-item_remove';
const cValue = '$remove$';

sessionStorage.setItem(cName, 'true');

runScriptlet(name, [cName, cValue]);
assert.strictEqual(window.hit, 'FIRED', 'Hit was fired');
assert.strictEqual(window.sessionStorage.getItem(cName), null, 'sessionStorage item has been removed');
clearStorageItem(cName);
});
test('Set sessionStorage key with invalid value', (assert) => {
let cName = '__test-item_arrayItem';
let cValue = '["item"]';
runScriptlet(name, [cName, cValue]);
assert.strictEqual(window.hit, undefined, 'Hit was not fired');
assert.strictEqual(window.sessionStorage.getItem(cName), null, 'sessionStorage item has not been set');
clearStorageItem(cName);

cName = '__test-item_object';
cValue = '{"key":value"}';
runScriptlet(name, [cName, cValue]);
assert.strictEqual(window.hit, undefined, 'Hit was not fired');
assert.strictEqual(window.sessionStorage.getItem(cName), null, 'sessionStorage item has not been set');
clearStorageItem(cName);

cName = '__test-item_str';
cValue = 'test_string';
runScriptlet(name, [cName, cValue]);
assert.strictEqual(window.hit, undefined, 'Hit was not fired');
assert.strictEqual(window.sessionStorage.getItem(cName), null, 'sessionStorage item has not been set');
clearStorageItem(cName);

cName = '__test-item_bigInt';
cValue = '999999';
runScriptlet(name, [cName, cValue]);
assert.strictEqual(window.hit, undefined, 'Hit was not fired');
assert.strictEqual(window.sessionStorage.getItem(cName), null, 'sessionStorage item has not been set');
clearStorageItem(cName);
});

test('Remove item from sessionStorage', (assert) => {
const cName = '__test-item_remove';
const cValue = '$remove$';

sessionStorage.setItem(cName, 'true');

runScriptlet(name, [cName, cValue]);
assert.strictEqual(window.hit, 'FIRED', 'Hit was fired');
assert.strictEqual(window.sessionStorage.getItem(cName), null, 'sessionStorage item has been removed');
clearStorageItem(cName);
});

test('Remove item from sessionStorage - regexp', (assert) => {
const iName = '/__test-.*_regexp/';
const iValue = '$remove$';
const firstRegexpStorageItem = '__test-first_item_remove_regexp';
const secondRegexpStorageItem = '__test-second_item_remove_regexp';

sessionStorage.setItem(firstRegexpStorageItem, '1');
sessionStorage.setItem(secondRegexpStorageItem, '2');

runScriptlet(name, [iName, iValue]);
assert.strictEqual(window.hit, 'FIRED', 'Hit was fired');
assert.strictEqual(
window.sessionStorage.getItem(firstRegexpStorageItem),
null,
'sessionStorage item has been removed',
);
assert.strictEqual(
window.sessionStorage.getItem(secondRegexpStorageItem),
null,
'sessionStorage item has been removed',
);
clearStorageItem(firstRegexpStorageItem);
clearStorageItem(secondRegexpStorageItem);
});

test('Remove item from sessionStorage - regexp with flag i', (assert) => {
const iName = '/^__test-.*_regexp_case-insensitive/i';
const iValue = '$remove$';
const caseInsensitiveRegexpStorageItem = '__test-first_item_remove_regexp_CASE-inSensitive';

sessionStorage.setItem(caseInsensitiveRegexpStorageItem, 'abc');

runScriptlet(name, [iName, iValue]);
assert.strictEqual(window.hit, 'FIRED', 'Hit was fired');
assert.strictEqual(
window.sessionStorage.getItem(caseInsensitiveRegexpStorageItem),
null,
'sessionStorage item has been removed',
);
clearStorageItem(caseInsensitiveRegexpStorageItem);
});

test('Remove item from sessionStorage - not regexp, starts with forward slash', (assert) => {
const iName = '/__test-';
const iValue = '$remove$';

sessionStorage.setItem(iName, '1');
// should not be removed
sessionStorage.setItem('/__test-2', '2');

runScriptlet(name, [iName, iValue]);
assert.strictEqual(window.hit, 'FIRED', 'Hit was fired');
assert.strictEqual(window.sessionStorage.getItem(iName), null, 'sessionStorage item has been removed');
assert.strictEqual(
window.sessionStorage.getItem('/__test-2'),
'2',
'not matched sessionStorage item should not be removed',
);
clearStorageItem(iName);
clearStorageItem('/__test-2');
});
}

0 comments on commit 391e4a7

Please sign in to comment.