style(13-03): apply Prettier formatting across repo
Mechanical reformat — no logic changes. 398 files changed, 19125 insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc (singleQuote:true, semi:true, tabWidth:2, trailingComma:all, printWidth:100). Isolated per D-13-08 for reviewability.
This commit is contained in:
@@ -9,72 +9,72 @@
|
||||
* - Stored payload shape
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeAll } from 'vitest'
|
||||
import { describe, it, expect, beforeAll } from 'vitest';
|
||||
|
||||
// Set a fixed 32-byte (64-char hex) key before importing the module
|
||||
const TEST_KEY = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2'
|
||||
const TEST_KEY = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2';
|
||||
|
||||
beforeAll(() => {
|
||||
process.env.APP_PASSWORD_ENCRYPTION_KEY = TEST_KEY
|
||||
})
|
||||
process.env.APP_PASSWORD_ENCRYPTION_KEY = TEST_KEY;
|
||||
});
|
||||
|
||||
// Dynamic import so env is set before module-level KEY evaluation
|
||||
async function getCrypto() {
|
||||
return import('../../src/broker/crypto.js')
|
||||
return import('../../src/broker/crypto.js');
|
||||
}
|
||||
|
||||
describe('encryptPassword / decryptPassword', () => {
|
||||
it('roundtrip: decrypt(encrypt(plaintext)) === plaintext', async () => {
|
||||
const { encryptPassword, decryptPassword } = await getCrypto()
|
||||
const plaintext = 'my-fastmail-app-password-abc123'
|
||||
const encrypted = encryptPassword(plaintext)
|
||||
expect(decryptPassword(encrypted)).toBe(plaintext)
|
||||
})
|
||||
const { encryptPassword, decryptPassword } = await getCrypto();
|
||||
const plaintext = 'my-fastmail-app-password-abc123';
|
||||
const encrypted = encryptPassword(plaintext);
|
||||
expect(decryptPassword(encrypted)).toBe(plaintext);
|
||||
});
|
||||
|
||||
it('different IVs produce different ciphertext for the same plaintext', async () => {
|
||||
const { encryptPassword } = await getCrypto()
|
||||
const plaintext = 'same-password'
|
||||
const enc1 = encryptPassword(plaintext)
|
||||
const enc2 = encryptPassword(plaintext)
|
||||
const { encryptPassword } = await getCrypto();
|
||||
const plaintext = 'same-password';
|
||||
const enc1 = encryptPassword(plaintext);
|
||||
const enc2 = encryptPassword(plaintext);
|
||||
// The JSON payloads must differ (different IVs → different ciphertext)
|
||||
expect(enc1).not.toBe(enc2)
|
||||
expect(enc1).not.toBe(enc2);
|
||||
// And the IVs themselves must differ
|
||||
const p1 = JSON.parse(enc1)
|
||||
const p2 = JSON.parse(enc2)
|
||||
expect(p1.iv).not.toBe(p2.iv)
|
||||
})
|
||||
const p1 = JSON.parse(enc1);
|
||||
const p2 = JSON.parse(enc2);
|
||||
expect(p1.iv).not.toBe(p2.iv);
|
||||
});
|
||||
|
||||
it('decrypting with a tampered authTag throws', async () => {
|
||||
const { encryptPassword, decryptPassword } = await getCrypto()
|
||||
const encrypted = encryptPassword('secret')
|
||||
const payload = JSON.parse(encrypted)
|
||||
const { encryptPassword, decryptPassword } = await getCrypto();
|
||||
const encrypted = encryptPassword('secret');
|
||||
const payload = JSON.parse(encrypted);
|
||||
// Flip first byte of authTag
|
||||
payload.authTag = ('ff' + payload.authTag.slice(2))
|
||||
expect(() => decryptPassword(JSON.stringify(payload))).toThrow()
|
||||
})
|
||||
payload.authTag = 'ff' + payload.authTag.slice(2);
|
||||
expect(() => decryptPassword(JSON.stringify(payload))).toThrow();
|
||||
});
|
||||
|
||||
it('decrypting with a tampered ciphertext throws', async () => {
|
||||
const { encryptPassword, decryptPassword } = await getCrypto()
|
||||
const encrypted = encryptPassword('secret')
|
||||
const payload = JSON.parse(encrypted)
|
||||
const { encryptPassword, decryptPassword } = await getCrypto();
|
||||
const encrypted = encryptPassword('secret');
|
||||
const payload = JSON.parse(encrypted);
|
||||
// Flip first byte of ciphertext
|
||||
payload.ciphertext = ('ff' + payload.ciphertext.slice(2))
|
||||
expect(() => decryptPassword(JSON.stringify(payload))).toThrow()
|
||||
})
|
||||
payload.ciphertext = 'ff' + payload.ciphertext.slice(2);
|
||||
expect(() => decryptPassword(JSON.stringify(payload))).toThrow();
|
||||
});
|
||||
|
||||
it('stored payload is valid JSON with iv, authTag, ciphertext fields', async () => {
|
||||
const { encryptPassword } = await getCrypto()
|
||||
const encrypted = encryptPassword('test-password')
|
||||
const payload = JSON.parse(encrypted)
|
||||
expect(payload).toHaveProperty('iv')
|
||||
expect(payload).toHaveProperty('authTag')
|
||||
expect(payload).toHaveProperty('ciphertext')
|
||||
const { encryptPassword } = await getCrypto();
|
||||
const encrypted = encryptPassword('test-password');
|
||||
const payload = JSON.parse(encrypted);
|
||||
expect(payload).toHaveProperty('iv');
|
||||
expect(payload).toHaveProperty('authTag');
|
||||
expect(payload).toHaveProperty('ciphertext');
|
||||
// All values are non-empty hex strings
|
||||
expect(typeof payload.iv).toBe('string')
|
||||
expect(payload.iv.length).toBeGreaterThan(0)
|
||||
expect(typeof payload.authTag).toBe('string')
|
||||
expect(payload.authTag.length).toBeGreaterThan(0)
|
||||
expect(typeof payload.ciphertext).toBe('string')
|
||||
expect(payload.ciphertext.length).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
expect(typeof payload.iv).toBe('string');
|
||||
expect(payload.iv.length).toBeGreaterThan(0);
|
||||
expect(typeof payload.authTag).toBe('string');
|
||||
expect(payload.authTag.length).toBeGreaterThan(0);
|
||||
expect(typeof payload.ciphertext).toBe('string');
|
||||
expect(payload.ciphertext.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user