fix(composer): stop three E2EE plaintext leaks (compress, schedule, GIF)
- Image compression in an encrypted room re-encoded the *plaintext* original, uploaded it unencrypted, and reused the original's encInfo, so the media sat on the server in the clear AND the attachment was undecryptable. The compressed bytes are now run through encryptFile and the synthetic upload item carries the new encInfo (buildCompressedUploadItem, unit-tested; it can never inherit the stale encInfo). - Scheduled messages (MSC4140) are PUT as raw m.room.message, bypassing the SDK encryption pipeline. The Schedule button is now hidden in encrypted rooms, handleScheduleClick no-ops there, and scheduleMessage() itself refuses with a clear error so no caller can regress this. README notes the limitation. - The GIF picker uploaded the Giphy blob unencrypted into E2EE rooms; it now mirrors the voice/attachment path (encryptFile -> upload ciphertext -> content.file). Fixes #6 Fixes #7 Fixes #11 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { IContent, MatrixClient, MsgType } from 'matrix-js-sdk';
|
||||
import to from 'await-to-js';
|
||||
import { EncryptedAttachmentInfo } from 'browser-encrypt-attachment';
|
||||
import {
|
||||
IThumbnailContent,
|
||||
MATRIX_BLUR_HASH_PROPERTY_NAME,
|
||||
@@ -43,6 +44,28 @@ const generateThumbnailContent = async (
|
||||
return thumbnailContent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the synthetic upload item for a *re-encoded* (compressed) image.
|
||||
*
|
||||
* The compressed bytes are a brand new payload, so the item must never inherit
|
||||
* the original's `encInfo` — that key/iv/sha256 describes the pre-compression
|
||||
* ciphertext and would make receivers fail to decrypt. In an encrypted room the
|
||||
* caller re-runs `encryptFile` and passes the new ciphertext + encInfo here; in
|
||||
* an unencrypted room both are omitted and the item carries no `encInfo` at all.
|
||||
*/
|
||||
export const buildCompressedUploadItem = (
|
||||
item: TUploadItem,
|
||||
compressedFile: File,
|
||||
encrypted?: { file: File; encInfo: EncryptedAttachmentInfo },
|
||||
): TUploadItem => ({
|
||||
...item,
|
||||
// `file` is what gets uploaded/described, `originalFile` is the plaintext used
|
||||
// for dimensions + blurhash.
|
||||
file: encrypted?.file ?? compressedFile,
|
||||
originalFile: compressedFile,
|
||||
encInfo: encrypted?.encInfo,
|
||||
});
|
||||
|
||||
export const getImageMsgContent = async (
|
||||
mx: MatrixClient,
|
||||
item: TUploadItem,
|
||||
|
||||
Reference in New Issue
Block a user