Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,5 @@ temp/
*.key
*.pem
secrets/

.kiro/
50 changes: 50 additions & 0 deletions packages/core-sdk/eslint.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,63 @@ module.exports = [
ecmaVersion: 2020,
sourceType: 'module',
},
globals: {
// Node.js globals
Buffer: 'readonly',
process: 'readonly',
// Web Crypto API globals
TextEncoder: 'readonly',
TextDecoder: 'readonly',
CryptoKey: 'readonly',
BufferSource: 'readonly',
},
},
plugins: {
'@typescript-eslint': tseslint,
},
rules: {
...tseslint.configs.recommended.rules,
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
},
},
{
files: ['**/__tests__/**/*.ts', '**/*.test.ts'],
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
},
globals: {
// Node.js globals
Buffer: 'readonly',
process: 'readonly',
require: 'readonly',
// Web Crypto API globals
TextEncoder: 'readonly',
TextDecoder: 'readonly',
CryptoKey: 'readonly',
BufferSource: 'readonly',
// Jest globals
describe: 'readonly',
it: 'readonly',
test: 'readonly',
expect: 'readonly',
beforeEach: 'readonly',
afterEach: 'readonly',
beforeAll: 'readonly',
afterAll: 'readonly',
jest: 'readonly',
},
},
plugins: {
'@typescript-eslint': tseslint,
},
rules: {
...tseslint.configs.recommended.rules,
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-require-imports': 'off',
},
},
];
34 changes: 23 additions & 11 deletions packages/core-sdk/src/storage/__tests__/manager.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { webcrypto } from 'crypto';

if (!globalThis.crypto) {
// @ts-ignore
// @ts-expect-error - Polyfill for Node.js environment
globalThis.crypto = webcrypto;
}
if (!globalThis.btoa) {
Expand Down Expand Up @@ -48,7 +48,8 @@ describe('SecureStorageManager', () => {
});

it('should store encrypted payloads (no plaintext secrets)', async () => {
await manager.unlock(password);
const unlockResult = await manager.unlock(password);
expect(unlockResult).toBe(true);
await manager.saveAccount(accountData);

const storedData = await storage.get('account');
Expand All @@ -65,7 +66,8 @@ describe('SecureStorageManager', () => {
});

it('should restore original data after unlock -> save -> lock -> unlock -> get', async () => {
await manager.unlock(password);
const unlockResult1 = await manager.unlock(password);
expect(unlockResult1).toBe(true);
await manager.saveAccount(accountData);
await manager.saveSessionKeys(sessionKeysData);

Expand All @@ -82,7 +84,8 @@ describe('SecureStorageManager', () => {
await expect(newManager.saveAccount(accountData)).rejects.toThrow('Storage manager is locked');

// Unlock with correct password
await newManager.unlock(password);
const unlockResult2 = await newManager.unlock(password);
expect(unlockResult2).toBe(true);
expect(newManager.isUnlocked).toBe(true);

const restoredAccount = await newManager.getAccount();
Expand All @@ -93,18 +96,25 @@ describe('SecureStorageManager', () => {
});

it('should fail gracefully with the wrong password', async () => {
await manager.unlock(password);
const unlockResult = await manager.unlock(password);
expect(unlockResult).toBe(true);
await manager.saveAccount(accountData);

manager.lock();
const newManager = new SecureStorageManager(storage);
await newManager.unlock('wrong_password');

await expect(newManager.getAccount()).rejects.toThrow('Invalid password or corrupted data');
const wrongPasswordResult = await newManager.unlock('wrong_password');

// Wrong password should return false
expect(wrongPasswordResult).toBe(false);
// Manager should remain locked
expect(newManager.isUnlocked).toBe(false);
// Attempting to access data while locked should throw
await expect(newManager.getAccount()).rejects.toThrow('Storage manager is locked');
});

it('should return null for non-existent items', async () => {
await manager.unlock(password);
const unlockResult = await manager.unlock(password);
expect(unlockResult).toBe(true);

const account = await manager.getAccount();
expect(account).toBeNull();
Expand All @@ -114,8 +124,10 @@ describe('SecureStorageManager', () => {
});

it('should not throw on unlock if already unlocked', async () => {
await manager.unlock(password);
await manager.unlock(password); // Should return early
const unlockResult1 = await manager.unlock(password);
expect(unlockResult1).toBe(true);
const unlockResult2 = await manager.unlock(password); // Should return true immediately
expect(unlockResult2).toBe(true);
expect(manager.isUnlocked).toBe(true);
});
});
Loading