diff --git a/.github/workflows/sonar-analysis.yml b/.github/workflows/sonar-analysis.yml index 6e0dc40..8917b1c 100644 --- a/.github/workflows/sonar-analysis.yml +++ b/.github/workflows/sonar-analysis.yml @@ -36,6 +36,9 @@ jobs: - name: Check and Install Playwright run: yarn playwright install + - name: Unit test run + run: yarn test:coverage + # Analyze with SonarCloud - name: Analyze with SonarCloud uses: SonarSource/sonarqube-scan-action@v5.0.0 diff --git a/src/email-crypto/pwdProtectedEmail.ts b/src/email-crypto/pwdProtectedEmail.ts index ef2d3df..2f3ad64 100644 --- a/src/email-crypto/pwdProtectedEmail.ts +++ b/src/email-crypto/pwdProtectedEmail.ts @@ -48,11 +48,13 @@ export async function decryptPwdProtectedEmail( password: string, aux?: Uint8Array, ): Promise { + if (!encryptedEmail?.encEmail || !encryptedEmail?.encryptedKey) { + throw new InvalidInputEmail(); + } try { const encryptionKey = await removePasswordProtection(encryptedEmail.encryptedKey, password); return await decryptEmail(encryptedEmail.encEmail, encryptionKey, aux); } catch (error) { - if (error instanceof InvalidInputEmail) throw error; if (error instanceof EmailPasswordOpenError) throw error; if (error instanceof EmailSymmetricDecryptionError) throw error; throw new FailedToDecryptEmail(error instanceof Error ? error.message : String(error)); diff --git a/src/email-crypto/pwdProtectedEmailAndSubject.ts b/src/email-crypto/pwdProtectedEmailAndSubject.ts index 2bdf596..6cc5c63 100644 --- a/src/email-crypto/pwdProtectedEmailAndSubject.ts +++ b/src/email-crypto/pwdProtectedEmailAndSubject.ts @@ -50,11 +50,13 @@ export async function decryptPwdProtectedEmailAndSubject( password: string, aux?: Uint8Array, ): Promise { + if (!encryptedEmail?.encEmail || !encryptedEmail?.encryptedKey) { + throw new InvalidInputEmail(); + } try { const encryptionKey = await removePasswordProtection(encryptedEmail.encryptedKey, password); return await decryptEmailAndSubject(encryptedEmail.encEmail, encryptionKey, aux); } catch (error) { - if (error instanceof InvalidInputEmail) throw error; if (error instanceof EmailPasswordOpenError) throw error; if (error instanceof EmailSymmetricDecryptionError) throw error; throw new FailedToDecryptEmail(error instanceof Error ? error.message : String(error)); diff --git a/tests/email-crypto/pwdProtectedEmail.test.ts b/tests/email-crypto/pwdProtectedEmail.test.ts index dadb3c4..ffadeeb 100644 --- a/tests/email-crypto/pwdProtectedEmail.test.ts +++ b/tests/email-crypto/pwdProtectedEmail.test.ts @@ -8,7 +8,7 @@ import { EmailSymmetricDecryptionError, InvalidInputEmail, } from '../../src/email-crypto'; -import { Email, EmailAndSubject } from '../../src/types'; +import { Email, EmailAndSubject, PwdProtectedEmail, PwdProtectedEmailAndSubject } from '../../src/types'; describe('Test email crypto functions', () => { const email: Email = { @@ -52,9 +52,15 @@ describe('Test email crypto functions', () => { ); }); + it('should throw an error if input is invalid', async () => { + await expect(decryptPwdProtectedEmail({} as PwdProtectedEmail, sharedSecret)).rejects.toThrow(InvalidInputEmail); + await expect(decryptPwdProtectedEmailAndSubject({} as PwdProtectedEmailAndSubject, sharedSecret)).rejects.toThrow( + InvalidInputEmail, + ); + }); + it('should throw an error if password-protected email is modified', async () => { const encryptedEmail = await createPwdProtectedEmail(email, sharedSecret); - const modifiedCiphertext = encryptedEmail; modifiedCiphertext.encEmail.encText += 'modified ciphertext'; await expect(decryptPwdProtectedEmail(modifiedCiphertext, sharedSecret)).rejects.toThrow( diff --git a/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts b/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts index 792eb7e..b00e558 100644 --- a/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts +++ b/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts @@ -49,10 +49,27 @@ describe('Test email crypto functions', () => { ); }); - it('throws FailedToDecryptEmail when decryption fails', async () => { - const encryptedEmail = {} as PwdProtectedEmail; - const encryptedEmailAndSubject = {} as PwdProtectedEmailAndSubject; + const encryptedEmail = { + encEmail: { + encText: 'Np2hSuNJinD6Z3KIiWhnH1qWBpaCed6dU2Du0JiDRxPLCJhMQ4FmQmqr5PRz3iHMWa7xWRRmsyeMyd1k8cwX4YRmVg==', + }, + encryptedKey: { + encryptedKey: 'PTSrN0aCrMoQIeSxQmmCHBhPFnJw8hx+Xu4nnbW81I+dvbyGAZmEvQ==', + salt: 'JUh7Xi0arXw/bhF4d+IEAw==', + }, + } as PwdProtectedEmail; + const encryptedEmailAndSubject = { + encEmail: { + encSubject: '6LD/Cnv8/mCDSns53eZzPRyNa3d9gk+gdkiFhzIGouxlQMBeE6YkOA==', + encText: 'nH1NJoZnO1Trv7RQ4+3Z+/epqt422zjJIBPO6nzSFq2pDlLvgOUcrqMlCicn+fK74XP7gYeKfd6z1Qa517XyPsCvdQ==', + }, + encryptedKey: { + encryptedKey: 'ctMRiLHe5a3AxVIC8QdFOS7VXdVMrZOZsYuC5MUQ7jbzxQio75NN2g==', + salt: 'qWsXcWQlc/uo46b0c+if0A==', + }, + } as PwdProtectedEmailAndSubject; + it('throws FailedToDecryptEmail when decryption fails', async () => { const spy = vi.spyOn(core, 'removePasswordProtection'); spy.mockRejectedValue(new Error('removePasswordProtection: unexpected failure')); @@ -63,4 +80,28 @@ describe('Test email crypto functions', () => { FailedToDecryptEmail, ); }); + + it('throws FailedToDecryptEmail when decryption fails with string error', async () => { + const spy = vi.spyOn(core, 'removePasswordProtection'); + + spy.mockRejectedValue('removePasswordProtection: unexpected failure'); + + await expect(decryptPwdProtectedEmail(encryptedEmail, sharedSecret)).rejects.toBeInstanceOf(FailedToDecryptEmail); + + await expect(decryptPwdProtectedEmailAndSubject(encryptedEmailAndSubject, sharedSecret)).rejects.toBeInstanceOf( + FailedToDecryptEmail, + ); + }); + + it('throws FailedToEncryptEmail when encryption fails with string error', async () => { + const spy = vi.spyOn(core, 'passwordProtectKey'); + + spy.mockRejectedValue('passwordProtectKey: unexpected failure'); + + await expect(createPwdProtectedEmail(email, sharedSecret)).rejects.toBeInstanceOf(FailedToEncryptEmail); + + await expect(createPwdProtectedEmailAndSubject(emailAndSubject, sharedSecret)).rejects.toBeInstanceOf( + FailedToEncryptEmail, + ); + }); });