Skip to content

Commit ad1acae

Browse files
authored
test(e2ee): guard resend/correction against plaintext downgrade (#415)
The downgrade-protection fix routes `sendMessage`, `resendMessage`, and `sendCorrection` through the single `applyE2EEToOutboundChat` funnel, but only `sendMessage` had an explicit regression test that a *selected-plugin* encrypt failure blocks the send (nothing reaches the wire).
1 parent f25a822 commit ad1acae

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

packages/fluux-sdk/src/core/modules/Chat.e2ee.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,6 +1051,20 @@ describe('Chat E2EE wiring', () => {
10511051
expect(captured).toHaveLength(0)
10521052
})
10531053

1054+
it('blocks the resend (no plaintext) when a selected plugin throws mid-encrypt', async () => {
1055+
// resendMessage shares applyE2EEToOutboundChat with sendMessage: a
1056+
// selected plugin failing mid-encrypt must block the retry, never fall
1057+
// back to resending the body in cleartext.
1058+
vi.spyOn(manager, 'encryptOutbound').mockRejectedValue(
1059+
new E2EEPluginError('permanent', 'pin-mismatch', 'fingerprint changed'),
1060+
)
1061+
1062+
await expect(
1063+
chat.resendMessage('bob@example.com', 'leaky retry', 'msg-retry-fail'),
1064+
).rejects.toBeInstanceOf(E2EEPluginError)
1065+
expect(captured).toHaveLength(0)
1066+
})
1067+
10541068
it('throws when retrying a message whose attachment carries an aesgcm key', async () => {
10551069
const emptyManager = new E2EEManager({
10561070
storage: new InMemoryStorageBackend(),
@@ -1126,6 +1140,20 @@ describe('Chat E2EE wiring', () => {
11261140
expect(captured).toHaveLength(0)
11271141
})
11281142

1143+
it('blocks the correction (no plaintext) when a selected plugin throws mid-encrypt', async () => {
1144+
// sendCorrection shares applyE2EEToOutboundChat: a selected plugin
1145+
// failing mid-encrypt must block the edit, never push the corrected
1146+
// body to the server in cleartext.
1147+
vi.spyOn(manager, 'encryptOutbound').mockRejectedValue(
1148+
new E2EEPluginError('permanent', 'pin-mismatch', 'fingerprint changed'),
1149+
)
1150+
1151+
await expect(
1152+
chat.sendCorrection('bob@example.com', 'orig-id', 'leaky edit'),
1153+
).rejects.toBeInstanceOf(E2EEPluginError)
1154+
expect(captured).toHaveLength(0)
1155+
})
1156+
11291157
it('throws when correcting a message whose attachment carries an aesgcm key', async () => {
11301158
const emptyManager = new E2EEManager({
11311159
storage: new InMemoryStorageBackend(),

0 commit comments

Comments
 (0)