From 3e7b662796b4939f0e11ab3521a59be91567a8e5 Mon Sep 17 00:00:00 2001 From: link2xt Date: Mon, 13 Jan 2025 22:28:16 +0000 Subject: [PATCH] feat: do not allow non-members to modify member list --- src/chat.rs | 36 +++++++++++++++ src/receive_imf.rs | 112 +++++++++++++++++++++++---------------------- 2 files changed, 93 insertions(+), 55 deletions(-) diff --git a/src/chat.rs b/src/chat.rs index c34c7302f..f8bfb62b1 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -7860,4 +7860,40 @@ mod tests { Ok(()) } + + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn non_member_cannot_modify_member_list() -> Result<()> { + let mut tcm = TestContextManager::new(); + + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; + + let bob_addr = bob.get_config(Config::Addr).await?.unwrap(); + let alice_bob_contact_id = Contact::create(alice, "Bob", &bob_addr).await?; + + let alice_chat_id = + create_group_chat(alice, ProtectionStatus::Unprotected, "Group chat").await?; + add_contact_to_chat(alice, alice_chat_id, alice_bob_contact_id).await?; + let alice_sent_msg = alice + .send_text(alice_chat_id, "Hi! I created a group.") + .await; + let bob_received_msg = bob.recv_msg(&alice_sent_msg).await; + let bob_chat_id = bob_received_msg.get_chat_id(); + bob_chat_id.accept(bob).await?; + + let bob_fiona_contact_id = Contact::create(bob, "Fiona", "fiona@example.net").await?; + + // Alice removes Bob and Bob adds Fiona at the same time. + remove_contact_from_chat(alice, alice_chat_id, alice_bob_contact_id).await?; + add_contact_to_chat(bob, bob_chat_id, bob_fiona_contact_id).await?; + + let bob_sent_add_msg = bob.pop_sent_msg().await; + + // Alice ignores Bob's message because Bob is not a member. + assert_eq!(get_chat_contacts(alice, alice_chat_id).await?.len(), 1); + alice.recv_msg(&bob_sent_add_msg).await; + assert_eq!(get_chat_contacts(alice, alice_chat_id).await?.len(), 1); + + Ok(()) + } } diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 90b5a53aa..d2f8e69f9 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -2329,65 +2329,67 @@ async fn apply_group_changes( let mut added_ids = HashSet::::new(); let mut removed_ids = HashSet::::new(); - if let Some(ref chat_group_member_timestamps) = mime_parser.chat_group_member_timestamps() { - send_event_chat_modified |= update_chats_contacts_timestamps( - context, - chat_id, - Some(from_id), - to_ids, - past_ids, - chat_group_member_timestamps, - ) - .await?; - let new_chat_contacts = HashSet::::from_iter( - chat::get_chat_contacts(context, chat_id) - .await? - .iter() - .copied(), - ); - added_ids = new_chat_contacts - .difference(&chat_contacts) - .copied() - .collect(); - removed_ids = chat_contacts - .difference(&new_chat_contacts) - .copied() - .collect(); - } else if is_from_in_chat { - let mut new_members = HashSet::from_iter(to_ids.iter().copied()); - new_members.insert(ContactId::SELF); - if !from_id.is_special() { - new_members.insert(from_id); - } - - if !self_added { - if mime_parser.get_header(HeaderDef::ChatVersion).is_none() { - // Allow non-Delta Chat MUAs to add members. - added_ids = new_members.difference(&chat_contacts).copied().collect(); - } - - if let Some(added_id) = added_id { - added_ids.insert(added_id); - } - new_members.clone_from(&chat_contacts); - // Don't delete any members locally, but instead add absent ones to provide group - // membership consistency for all members: - new_members.extend(added_ids.clone()); - } - if let Some(removed_id) = removed_id { - new_members.remove(&removed_id); - } - - if new_members != chat_contacts { - chat::update_chat_contacts_table( + if is_from_in_chat { + if let Some(ref chat_group_member_timestamps) = mime_parser.chat_group_member_timestamps() { + send_event_chat_modified |= update_chats_contacts_timestamps( context, - mime_parser.timestamp_sent, chat_id, - &new_members, + Some(from_id), + to_ids, + past_ids, + chat_group_member_timestamps, ) .await?; - chat_contacts = new_members; - send_event_chat_modified = true; + let new_chat_contacts = HashSet::::from_iter( + chat::get_chat_contacts(context, chat_id) + .await? + .iter() + .copied(), + ); + added_ids = new_chat_contacts + .difference(&chat_contacts) + .copied() + .collect(); + removed_ids = chat_contacts + .difference(&new_chat_contacts) + .copied() + .collect(); + } else { + let mut new_members = HashSet::from_iter(to_ids.iter().copied()); + new_members.insert(ContactId::SELF); + if !from_id.is_special() { + new_members.insert(from_id); + } + + if !self_added { + if mime_parser.get_header(HeaderDef::ChatVersion).is_none() { + // Allow non-Delta Chat MUAs to add members. + added_ids = new_members.difference(&chat_contacts).copied().collect(); + } + + if let Some(added_id) = added_id { + added_ids.insert(added_id); + } + new_members.clone_from(&chat_contacts); + // Don't delete any members locally, but instead add absent ones to provide group + // membership consistency for all members: + new_members.extend(added_ids.clone()); + } + if let Some(removed_id) = removed_id { + new_members.remove(&removed_id); + } + + if new_members != chat_contacts { + chat::update_chat_contacts_table( + context, + mime_parser.timestamp_sent, + chat_id, + &new_members, + ) + .await?; + chat_contacts = new_members; + send_event_chat_modified = true; + } } }