Skip to content

Commit

Permalink
feat(parser): add doNotParseValues in EncryptStorage constructor options
Browse files Browse the repository at this point in the history
  • Loading branch information
michelonsouza committed May 14, 2024
1 parent af712a1 commit 0a2c1eb
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 7 deletions.
Binary file modified .DS_Store
Binary file not shown.
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Using the [`crypto-js`](https://github.com/brix/crypto-js) library as an encrypt

> **HELP THIS PROJECT**: Your Github `star` can help this project. Leave a `star`, it costs nothing.
> **⚠️ IMPORTANT**: Nothing on the front end is entirely secure. The library's proposal is to make it difficult for the user to see the data through the console, but as the secret key is on the front end, if the user searches hard enough, he will end up finding it. Just to make it clear that nothing is completely secure on the front end. Thank you for your attention.
> **⚠️ IMPORTANT**: Nothing on the front end is entirely secure. The library's proposal is to make it difficult for the user to see the data through the console, but as the secret key is on the front end, if the user searches hard enough, he will end up finding it. Just to make it clear that nothing is completely secure on the front end. Thank you for your attention.
- [Encrypt Storage](#encrypt-storage)
- [Features](#features)
Expand Down Expand Up @@ -46,6 +46,7 @@ Using the [`crypto-js`](https://github.com/brix/crypto-js) library as an encrypt
- [_decryptValue_](#decryptvalue)
- [_hash_](#hash)
- [_md5Hash_](#md5hash)
- [NextJS](#nextjs)
- [AsyncEncryptStorage](#asyncencryptstorage)
- [AWS Amplify](#aws-amplify)
- [State Management Persisters](#state-management-persisters)
Expand Down Expand Up @@ -629,6 +630,41 @@ result of `hashed value`:
const value = '284e512750fb7d41f1cc5284a2c56a13';
```

### NextJS

When used in NextJS, validation must be done.

example:

```typescript
// utils/storage.(ts|js)
import { EncryptStorage } from 'encrypt-storage';

const encryptStorage = (): EncryptStorage | null => {
const isInClientSide =
typeof window !== 'undefined' && typeof window?.self !== 'undefined';

if (isInClientSide) {
return new EncryptStorage(
String(process.env.NEXT_PUBLIC_STORAGE_SECRET),
// options,
);
}

return null;
};
```

usage:

```typescript
'use client';
import { encryptStorage } from '../utils/storage.ts';

// ...rest of code
encryptStorage()?.setItem('any-key', { name: 'John Doe', age: 40 });
```

### AsyncEncryptStorage

EncryptStorage can also be used asynchronously, simply using its corresponding version already exported by the library.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "encrypt-storage",
"version": "2.12.23",
"version": "2.13.00",
"description": "Wrapper for encrypted localStorage and sessionStorage in browser",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
24 changes: 19 additions & 5 deletions src/encrypt-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export class EncryptStorage implements EncryptStorageInterface {

readonly #doNotEncryptValues: boolean;

readonly #doNotParseValues: boolean;

readonly #notifyHandler: NotifyHandler | undefined;

/**
Expand All @@ -43,6 +45,7 @@ export class EncryptStorage implements EncryptStorageInterface {
encAlgorithm = 'AES',
doNotEncryptValues = false,
notifyHandler,
doNotParseValues = false,
} = options || {};

secret.set(this, secretKey);
Expand All @@ -51,6 +54,7 @@ export class EncryptStorage implements EncryptStorageInterface {
this.#notifyHandler = notifyHandler;
this.#stateManagementUse = stateManagementUse;
this.#doNotEncryptValues = doNotEncryptValues;
this.#doNotParseValues = doNotParseValues;
this.#encryptation = getEncryptation(encAlgorithm, secret.get(this));
this.storage = typeof window === 'object' ? window[storageType] : null;
}
Expand All @@ -75,8 +79,13 @@ export class EncryptStorage implements EncryptStorageInterface {
public setItem(key: string, value: any, doNotEncrypt = false): void {
const encryptValues = this.#doNotEncryptValues || doNotEncrypt;
const storageKey = this.#getKey(key);
const valueToString =
let valueToString =
typeof value === 'object' ? JSON.stringify(value) : String(value);

if (this.#doNotParseValues) {
valueToString = value;
}

const encryptedValue = encryptValues
? valueToString
: this.#encryptation.encrypt(valueToString);
Expand Down Expand Up @@ -138,7 +147,9 @@ export class EncryptStorage implements EncryptStorageInterface {
}

try {
const value = JSON.parse(decryptedValue) as T;
const value = this.#doNotParseValues
? decryptedValue
: JSON.parse(decryptedValue);

if (this.#notifyHandler && !this.#multiple) {
this.#notifyHandler({
Expand All @@ -148,7 +159,7 @@ export class EncryptStorage implements EncryptStorageInterface {
});
}

return value;
return value as T;
} catch (error) {
if (this.#notifyHandler && !this.#multiple) {
this.#notifyHandler({
Expand Down Expand Up @@ -361,15 +372,18 @@ export class EncryptStorage implements EncryptStorageInterface {
}

public encryptValue(value: any): string {
const encryptedValue = this.#encryptation.encrypt(JSON.stringify(value));
const parsedValue = this.#doNotParseValues ? value : JSON.stringify(value);
const encryptedValue = this.#encryptation.encrypt(parsedValue);

return encryptedValue;
}

public decryptValue<T = any>(value: string): T {
const decryptedValue = this.#encryptation.decrypt(value);

return JSON.parse(decryptedValue) as T;
return (
this.#doNotParseValues ? decryptedValue : JSON.parse(decryptedValue)
) as T;
}

public hash(value: string): string {
Expand Down
42 changes: 42 additions & 0 deletions src/experiments/test-1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const makeSut = (
stateManagementUse,
noOptions,
encAlgorithm,
doNotParseValues = false,
notifyHandler = mockNotify.mockedFn,
secretKey = faker.random.alphaNumeric(10),
} = params;
Expand All @@ -39,6 +40,7 @@ export const makeSut = (
stateManagementUse,
encAlgorithm,
notifyHandler,
doNotParseValues,
};
return new EncryptStorage(secretKey, options);
};
Expand Down Expand Up @@ -97,6 +99,20 @@ export const test1 = () =>
expect(storagedDecrypetdValue).toEqual(value);
});

it('should localStorage.getItem returns correct decrypted value with doNotParseValues option', () => {
const safeStorage = makeSut({
doNotParseValues: true,
});
const values = ['[]', '{}', 'null', 'undefined', 'true', 'false', '123'];
const key = faker.random.word();
const value = faker.random.arrayElement(values);

safeStorage.setItem(key, value);
const storagedDecrypetdValue = safeStorage.getItem(key);

expect(storagedDecrypetdValue).toEqual(value);
});

it('should localStorage.getItem returns correct value but not decrypt', () => {
const safeStorage = makeSut();
const key = faker.random.word();
Expand Down Expand Up @@ -450,6 +466,18 @@ export const test1 = () =>
expect(result).not.toEqual(value);
});

it('should encrypt value and return encrypted value with doNotParseValues option', () => {
const safeStorage = makeSut({ doNotParseValues: true });
const value = {
name: faker.random.word(),
id: faker.datatype.uuid(),
};
const stringfyValue = JSON.stringify(value);
const result = safeStorage.encryptValue(stringfyValue);

expect(result).not.toEqual(stringfyValue);
});

it('should decrypt string and return decrypted value', () => {
const safeStorage = makeSut();
const value = faker.random.word();
Expand All @@ -472,6 +500,20 @@ export const test1 = () =>
expect(decryptedValue).toEqual(value);
});

it('should decrypt value and return decrypted value with doNotParseValues option', () => {
const safeStorage = makeSut({ doNotParseValues: true });
const value = {
name: faker.random.word(),
id: faker.datatype.uuid(),
};
const stringfyValue = JSON.stringify(value);
const encryptedValue = safeStorage.encryptValue(stringfyValue);
const decryptedValue =
safeStorage.decryptValue<typeof value>(encryptedValue);

expect(decryptedValue).toEqual(stringfyValue);
});

it('should test encryptStorage without options', () => {
const safeStorage = makeSut({ noOptions: true });
const key = faker.random.word();
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface EncryptStorageOptions {
storageType?: StorageType;
encAlgorithm?: EncAlgorithm;
doNotEncryptValues?: boolean;
doNotParseValues?: boolean;
notifyHandler?: NotifyHandler;
}

Expand Down

0 comments on commit 0a2c1eb

Please sign in to comment.