diff --git a/ts/services/backups/export.preload.ts b/ts/services/backups/export.preload.ts index 73587d7441..caf220709c 100644 --- a/ts/services/backups/export.preload.ts +++ b/ts/services/backups/export.preload.ts @@ -1537,6 +1537,20 @@ export class BackupExportStream extends Readable { } } + // Drop messages in the wrong 1:1 chat + if (conversation && isDirectConversation(conversation.attributes)) { + const convoAuthor = this.#getOrPushPrivateRecipient({ + id: conversation.attributes.id, + }); + + if (authorId !== me && authorId !== convoAuthor) { + log.warn( + `${message.sent_at}: Dropping direct message with mismatched author` + ); + return undefined; + } + } + if (isOutgoing || isIncoming) { strictAssert(authorId, 'Incoming/outgoing messages require an author'); } @@ -1793,10 +1807,15 @@ export class BackupExportStream extends Readable { }; } } else if (message.storyReplyContext || message.storyReaction) { + const directStoryReplyMessage = await this.#toDirectStoryReplyMessage({ + message, + }); + if (!directStoryReplyMessage) { + return undefined; + } + item = { - directStoryReplyMessage: await this.#toDirectStoryReplyMessage({ - message, - }), + directStoryReplyMessage, }; revisions = await this.#toChatItemRevisions(base, item, message); @@ -3427,14 +3446,17 @@ export class BackupExportStream extends Readable { | 'received_at' | 'reactions' >; - }): Promise { + }): Promise { const reactions = this.#getMessageReactions(message); let reply: Backups.DirectStoryReplyMessage.Params['reply']; if (message.storyReaction) { reply = { emoji: message.storyReaction.emoji }; - } else { + } else if (message.body) { reply = { textReply: await this.#toTextAndLongTextFields(message) }; + } else { + log.warn('Dropping direct story reply message without reaction or body'); + return undefined; } return { reply, reactions }; @@ -3549,10 +3571,15 @@ export class BackupExportStream extends Readable { let item: Backups.ChatItem.Params['item']; if (parentItem.directStoryReplyMessage) { - item = { - directStoryReplyMessage: await this.#toDirectStoryReplyMessage({ + const directStoryReplyMessage = + await this.#toDirectStoryReplyMessage({ message: history, - }), + }); + if (!directStoryReplyMessage) { + return undefined; + } + item = { + directStoryReplyMessage, }; } else { const standardMessage = await this.#toStandardMessage({ diff --git a/ts/test-electron/backup/bubble_test.preload.ts b/ts/test-electron/backup/bubble_test.preload.ts index 642f552598..ba16d46b6a 100644 --- a/ts/test-electron/backup/bubble_test.preload.ts +++ b/ts/test-electron/backup/bubble_test.preload.ts @@ -354,6 +354,28 @@ describe('backup/bubble messages', () => { ); }); + it('drops misattributed incoming 1:1 messages', async () => { + await asymmetricRoundtripHarness( + [ + { + conversationId: contactA.id, + id: generateGuid(), + type: 'incoming', + received_at: 3, + received_at_ms: 3, + sent_at: 3, + sourceServiceId: CONTACT_B, + readStatus: ReadStatus.Unread, + seenStatus: SeenStatus.Unseen, + unidentifiedDeliveryReceived: true, + timestamp: 3, + body: 'hello', + }, + ], + [] + ); + }); + it('updates incoming messages authored by self to outgoing', async () => { const ourConversation = window.ConversationController.get(OUR_ACI); strictAssert(ourConversation, 'our conversation exists'); @@ -1263,6 +1285,34 @@ describe('backup/bubble messages', () => { await symmetricRoundtripHarness([incomingReply, outgoingReply]); }); + it('drops direct story text replies with no body', async () => { + strictAssert(ourConversation, 'conversation exists'); + + await asymmetricRoundtripHarness( + [ + { + conversationId: contactA.id, + id: generateGuid(), + type: 'incoming', + body: '', + unidentifiedDeliveryReceived: true, + sourceServiceId: CONTACT_A, + received_at: 3, + received_at_ms: 3, + sent_at: 3, + timestamp: 3, + readStatus: ReadStatus.Read, + seenStatus: SeenStatus.Seen, + storyReplyContext: { + authorAci: OUR_ACI, + messageId: '', + }, + }, + ], + [] + ); + }); + it('does not export group story replies', async () => { strictAssert(ourConversation, 'conversations exist'); strictAssert(group, 'conversations exist');