Add test case for incoming OMEMO message corrections.

The correction was being ignored because the parsed `msgid` of an
incoming correction was set to the `msgid` of the message being
replaced.
This commit is contained in:
JC Brand 2022-03-22 22:52:22 +01:00
parent 297869c59f
commit 7355c2c5fe
4 changed files with 62 additions and 9 deletions

View File

@ -637,7 +637,8 @@ const ChatBox = ModelWithContact.extend({
} else { } else {
older_versions[message.get('time')] = message.getMessageText(); older_versions[message.get('time')] = message.getMessageText();
} }
attrs = Object.assign(attrs, {'older_versions': older_versions}); attrs = Object.assign(attrs, { older_versions });
delete attrs['msgid']; // We want to keep the msgid of the original message
delete attrs['id']; // Delete id, otherwise a new cache entry gets created delete attrs['id']; // Delete id, otherwise a new cache entry gets created
attrs['time'] = message.get('time'); attrs['time'] = message.get('time');
message.save(attrs); message.save(attrs);
@ -679,15 +680,16 @@ const ChatBox = ModelWithContact.extend({
}, },
getMessageBodyQueryAttrs (attrs) { getMessageBodyQueryAttrs (attrs) {
if (attrs.message && attrs.msgid) { if (attrs.msgid) {
const query = { const query = {
'from': attrs.from, 'from': attrs.from,
'msgid': attrs.msgid 'msgid': attrs.msgid
} }
if (!attrs.is_encrypted) { // XXX: Need to take XEP-428 <fallback> into consideration
if (!attrs.is_encrypted && attrs.body) {
// We can't match the message if it's a reflected // We can't match the message if it's a reflected
// encrypted message (e.g. via MAM or in a MUC) // encrypted message (e.g. via MAM or in a MUC)
query['message'] = attrs.message; query['body'] = attrs.body;
} }
return query; return query;
} }

View File

@ -125,7 +125,8 @@ export async function handleMessageStanza (stanza) {
attrs.stanza && log.error(attrs.stanza); attrs.stanza && log.error(attrs.stanza);
return log.error(attrs.message); return log.error(attrs.message);
} }
const has_body = !!sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length; // XXX: Need to take XEP-428 <fallback> into consideration
const has_body = !!(attrs.body || attrs.plaintext)
const chatbox = await api.chats.get(attrs.contact_jid, { 'nickname': attrs.nick }, has_body); const chatbox = await api.chats.get(attrs.contact_jid, { 'nickname': attrs.nick }, has_body);
await chatbox?.queueMessage(attrs); await chatbox?.queueMessage(attrs);
/** /**

View File

@ -108,12 +108,10 @@ export function getCorrectionAttributes (stanza, original_stanza) {
const el = sizzle(`replace[xmlns="${Strophe.NS.MESSAGE_CORRECT}"]`, stanza).pop(); const el = sizzle(`replace[xmlns="${Strophe.NS.MESSAGE_CORRECT}"]`, stanza).pop();
if (el) { if (el) {
const replace_id = el.getAttribute('id'); const replace_id = el.getAttribute('id');
const msgid = replace_id;
if (replace_id) { if (replace_id) {
const delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, original_stanza).pop(); const delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, original_stanza).pop();
const time = delay ? dayjs(delay.getAttribute('stamp')).toISOString() : new Date().toISOString(); const time = delay ? dayjs(delay.getAttribute('stamp')).toISOString() : new Date().toISOString();
return { return {
msgid,
replace_id, replace_id,
'edited': time 'edited': time
}; };

View File

@ -1,6 +1,6 @@
/*global mock, converse */ /*global mock, converse */
const { Strophe, $iq, $pres, u } = converse.env; const { Strophe, $iq, $msg, $pres, u, omemo } = converse.env;
describe("An OMEMO encrypted message", function() { describe("An OMEMO encrypted message", function() {
@ -126,7 +126,7 @@ describe("An OMEMO encrypted message", function() {
`<encryption namespace="eu.siacs.conversations.axolotl" xmlns="urn:xmpp:eme:0"/>`+ `<encryption namespace="eu.siacs.conversations.axolotl" xmlns="urn:xmpp:eme:0"/>`+
`</message>`); `</message>`);
const older_versions = first_msg.get('older_versions'); let older_versions = first_msg.get('older_versions');
let keys = Object.keys(older_versions); let keys = Object.keys(older_versions);
expect(keys.length).toBe(1); expect(keys.length).toBe(1);
expect(older_versions[keys[0]]).toBe('But soft, what light through yonder airlock breaks?'); expect(older_versions[keys[0]]).toBe('But soft, what light through yonder airlock breaks?');
@ -154,6 +154,58 @@ describe("An OMEMO encrypted message", function() {
expect(keys.length).toBe(2); expect(keys.length).toBe(2);
expect(older_versions[keys[0]]).toBe('But soft, what light through yonder airlock breaks?'); expect(older_versions[keys[0]]).toBe('But soft, what light through yonder airlock breaks?');
expect(older_versions[keys[1]]).toBe('But soft, what light through yonder door breaks?'); expect(older_versions[keys[1]]).toBe('But soft, what light through yonder door breaks?');
const first_rcvd_msg_id = u.getUniqueId();
let obj = await omemo.encryptMessage('This is an encrypted message from the contact')
_converse.connection._dataRecv(mock.createRequest($msg({
'from': contact_jid,
'to': _converse.connection.jid,
'type': 'chat',
'id': first_rcvd_msg_id
}).c('body').t(fallback_text).up()
.c('origin-id', {'id': first_rcvd_msg_id, 'xmlns': 'urn:xmpp:sid:0'}).up()
.c('encrypted', {'xmlns': Strophe.NS.OMEMO})
.c('header', {'sid': '555'})
.c('key', {'rid': _converse.omemo_store.get('device_id')}).t(u.arrayBufferToBase64(obj.key_and_tag)).up()
.c('iv').t(obj.iv)
.up().up()
.c('payload').t(obj.payload)));
await new Promise(resolve => view.model.messages.once('rendered', resolve));
expect(view.model.messages.length).toBe(2);
expect(view.querySelectorAll('.chat-msg__body')[1].textContent.trim())
.toBe('This is an encrypted message from the contact');
const msg_id = u.getUniqueId();
obj = await omemo.encryptMessage('This is an edited encrypted message from the contact')
_converse.connection._dataRecv(mock.createRequest($msg({
'from': contact_jid,
'to': _converse.connection.jid,
'type': 'chat',
'id': msg_id
}).c('body').t(fallback_text).up()
.c('replace', {'id': first_rcvd_msg_id, 'xmlns': 'urn:xmpp:message-correct:0'}).up()
.c('origin-id', {'id': msg_id, 'xmlns': 'urn:xmpp:sid:0'}).up()
.c('encrypted', {'xmlns': Strophe.NS.OMEMO})
.c('header', {'sid': '555'})
.c('key', {'rid': _converse.omemo_store.get('device_id')}).t(u.arrayBufferToBase64(obj.key_and_tag)).up()
.c('iv').t(obj.iv)
.up().up()
.c('payload').t(obj.payload)));
await new Promise(resolve => view.model.messages.once('rendered', resolve));
expect(view.model.messages.length).toBe(2);
expect(view.querySelectorAll('.chat-msg__body')[1].textContent.trim())
.toBe('This is an edited encrypted message from the contact');
const message = view.model.messages.at(1);
older_versions = message.get('older_versions');
keys = Object.keys(older_versions);
expect(keys.length).toBe(1);
expect(older_versions[keys[0]]).toBe('This is an encrypted message from the contact');
expect(message.get('plaintext')).toBe('This is an edited encrypted message from the contact');
expect(message.get('is_encrypted')).toBe(true);
expect(message.get('body')).toBe(fallback_text);
expect(message.get('message')).toBe(fallback_text);
expect(message.get('msgid')).toBe(first_rcvd_msg_id);
})); }));
}); });