-
Notifications
You must be signed in to change notification settings - Fork 1.4k
fix: chat.update not updating e2ee messages correctly #6723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughThis change replaces the previous encrypted message content type with a new union type across message and attachment definitions, updates the chat.update REST params to accept encrypted content and e2e mentions, adjusts decryption to populate message text, and updates editMessage to handle encryption and branch requests accordingly. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant UI as Client UI
participant Service as editMessage()
participant Crypto as E2E Encryptor
participant API as REST chat.update
UI->>Service: editMessage({ id, rid, msg?, content?, e2eMentions? })
Note over Service: Prepare payload for encryption
Service->>Crypto: encrypt({ msg/content, rid, mentions })
alt Encryption fails
Crypto-->>Service: error/null
Service-->>UI: throw Error("encryption failed")
else Encryption succeeds
Crypto-->>Service: { content? , text? , e2eMentions? }
alt Has encrypted content
Service->>API: POST /chat.update { roomId, msgId, content, e2eMentions }
else Fallback to plaintext
Service->>API: POST /chat.update { roomId, msgId, text }
end
API-->>Service: 200 OK
Service-->>UI: resolve()
end
sequenceDiagram
autonumber
participant Server as Server
participant Decrypt as room.decrypt()
participant Msg as IMessage
Server-->>Decrypt: content (rc.v1.aes-sha2)
Decrypt-->>Decrypt: decrypt content
alt content.text present
Decrypt->>Msg: msg = content.text
else No text
Note over Decrypt,Msg: msg unchanged
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45–70 minutes Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
app/lib/encryption/room.ts (1)
648-651: Normalize from bothcontent.textandcontent.msg.Be explicit and resilient to either field being present.
- if (content.text) { - message.msg = content.text; - } + const normalizedText = (content as any)?.text ?? (content as any)?.msg; + if (normalizedText) { + message.msg = normalizedText; + }app/definitions/IMessage.ts (1)
83-107: Consider a single discriminated union without the base interface.Defining
TEncryptedContentdirectly tightens thealgorithmliteral type and reduces indirection. Current approach works; this is just simplification.export type TEncryptedContent = | { algorithm: 'rc.v1.aes-sha2'; ciphertext: string } | { algorithm: 'rc.v2.aes-sha2'; ciphertext: string; iv: string; kid: string };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
app/definitions/IAttachment.ts(2 hunks)app/definitions/IMessage.ts(3 hunks)app/definitions/rest/v1/chat.ts(2 hunks)app/lib/encryption/room.ts(1 hunks)app/lib/services/restApi.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
app/lib/services/restApi.ts (2)
app/definitions/IMessage.ts (1)
IMessage(146-177)android/app/src/main/java/chat/rocket/reactnative/notification/Encryption.java (1)
Encryption(67-267)
app/definitions/rest/v1/chat.ts (2)
app/definitions/IRoom.ts (1)
IServerRoom(152-236)app/definitions/IMessage.ts (2)
TEncryptedContent(107-107)IMessage(146-177)
app/definitions/IAttachment.ts (1)
app/definitions/IMessage.ts (1)
TEncryptedContent(107-107)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ESLint and Test / run-eslint-and-test
🔇 Additional comments (4)
app/definitions/IMessage.ts (1)
171-171: Publice2eMentionsshape looks good.No concerns. Aligns with usage downstream.
app/definitions/rest/v1/chat.ts (1)
78-84: API shape update LGTM; confirm backward compatibility.Extending
chat.updateto acceptcontent/e2eMentionsmakes sense. Please verify behavior against older servers wheretextmight still be required.app/definitions/IAttachment.ts (2)
2-2: Type import update LGTM.Consistent with
TEncryptedContentadoption.
75-76: Attachmentcontentnow usesTEncryptedContent— good.Matches decryption logic that checks
algorithmandciphertext.
| export const editMessage = async (message: Pick<IMessage, 'id' | 'msg' | 'rid' | 'content' | 'e2eMentions'>) => { | ||
| const result = await Encryption.encryptMessage(message as IMessage); | ||
| if (!result) { | ||
| throw new Error('Failed to encrypt message'); | ||
| } | ||
|
|
||
| if (result.content) { | ||
| // RC 0.49.0 | ||
| return sdk.post('chat.update', { | ||
| roomId: message.rid, | ||
| msgId: message.id, | ||
| content: result.content, | ||
| e2eMentions: result.e2eMentions | ||
| }); | ||
| } | ||
|
|
||
| // RC 0.49.0 | ||
| return sdk.post('chat.update', { roomId: rid, msgId: message.id, text: msg }); | ||
| return sdk.post('chat.update', { | ||
| roomId: message.rid, | ||
| msgId: message.id, | ||
| text: message.msg || '' | ||
| }); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add fallback for servers that don’t support content in chat.update.
Currently, edits in E2EE rooms may fail on older servers (where text is required). Try content first, then fallback to text with the ciphertext.
export const editMessage = async (message: Pick<IMessage, 'id' | 'msg' | 'rid' | 'content' | 'e2eMentions'>) => {
const result = await Encryption.encryptMessage(message as IMessage);
if (!result) {
throw new Error('Failed to encrypt message');
}
if (result.content) {
- // RC 0.49.0
- return sdk.post('chat.update', {
- roomId: message.rid,
- msgId: message.id,
- content: result.content,
- e2eMentions: result.e2eMentions
- });
+ try {
+ // Prefer new API with encrypted content
+ return await sdk.post('chat.update', {
+ roomId: message.rid,
+ msgId: message.id,
+ content: result.content,
+ e2eMentions: result.e2eMentions
+ });
+ } catch (e) {
+ // Fallback for older servers: send ciphertext as text
+ const ciphertext = (result as any).msg ?? result.content.ciphertext ?? message.msg ?? '';
+ return sdk.post('chat.update', {
+ roomId: message.rid,
+ msgId: message.id,
+ text: ciphertext
+ });
+ }
}
// RC 0.49.0
return sdk.post('chat.update', {
roomId: message.rid,
msgId: message.id,
text: message.msg || ''
});
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const editMessage = async (message: Pick<IMessage, 'id' | 'msg' | 'rid' | 'content' | 'e2eMentions'>) => { | |
| const result = await Encryption.encryptMessage(message as IMessage); | |
| if (!result) { | |
| throw new Error('Failed to encrypt message'); | |
| } | |
| if (result.content) { | |
| // RC 0.49.0 | |
| return sdk.post('chat.update', { | |
| roomId: message.rid, | |
| msgId: message.id, | |
| content: result.content, | |
| e2eMentions: result.e2eMentions | |
| }); | |
| } | |
| // RC 0.49.0 | |
| return sdk.post('chat.update', { roomId: rid, msgId: message.id, text: msg }); | |
| return sdk.post('chat.update', { | |
| roomId: message.rid, | |
| msgId: message.id, | |
| text: message.msg || '' | |
| }); | |
| }; | |
| export const editMessage = async (message: Pick<IMessage, 'id' | 'msg' | 'rid' | 'content' | 'e2eMentions'>) => { | |
| const result = await Encryption.encryptMessage(message as IMessage); | |
| if (!result) { | |
| throw new Error('Failed to encrypt message'); | |
| } | |
| if (result.content) { | |
| try { | |
| // Prefer new API with encrypted content | |
| return await sdk.post('chat.update', { | |
| roomId: message.rid, | |
| msgId: message.id, | |
| content: result.content, | |
| e2eMentions: result.e2eMentions | |
| }); | |
| } catch (e) { | |
| // Fallback for older servers: send ciphertext as text | |
| const ciphertext = (result as any).msg ?? result.content.ciphertext ?? message.msg ?? ''; | |
| return sdk.post('chat.update', { | |
| roomId: message.rid, | |
| msgId: message.id, | |
| text: ciphertext | |
| }); | |
| } | |
| } | |
| // RC 0.49.0 | |
| return sdk.post('chat.update', { | |
| roomId: message.rid, | |
| msgId: message.id, | |
| text: message.msg || '' | |
| }); | |
| }; |
| export const editMessage = async (message: Pick<IMessage, 'id' | 'msg' | 'rid'>) => { | ||
| const { rid, msg } = await Encryption.encryptMessage(message as IMessage); | ||
| export const editMessage = async (message: Pick<IMessage, 'id' | 'msg' | 'rid' | 'content' | 'e2eMentions'>) => { | ||
| const result = await Encryption.encryptMessage(message as IMessage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
Closing, since it'll be released on #6668 |

Proposed changes
Sends the extra params needed for
chat.updateto work.Issue(s)
Counterpart of RocketChat/Rocket.Chat#37138
https://rocketchat.atlassian.net/browse/ESH-47
How to test or reproduce
Tested on workspaces with and without RocketChat/Rocket.Chat#37138.
The app doesn't crash.
Screenshots
Types of changes
Checklist
Further comments
Summary by CodeRabbit
New Features
Bug Fixes