From c6a64e8d932986f4a6b32774e796a32915b33d75 Mon Sep 17 00:00:00 2001 From: Hocuri Date: Thu, 13 Apr 2023 22:45:47 +0200 Subject: [PATCH] Don't let blocking be bypassed using groups (#4316) * Don't let blocking be bypassed using groups Fix #4313 * Fix another bug: A blocked group was sometimes not unblocked when an unblocked contact sent a message into it. --- CHANGELOG.md | 1 + src/receive_imf.rs | 32 ++++++++++++++------------- src/receive_imf/tests.rs | 48 ++++++++++++++++++++++++++++++++++++++++ src/test_utils.rs | 24 +++++++++++++++++++- 4 files changed, 89 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a72189f2..422f47b8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Fix python bindings README documentation on installing the bindings from source. - Show a warning if quota list is empty #4261 - Update "accounts.toml" atomically +- Don't let blocking be bypassed using groups #4316 ## [1.112.6] - 2023-04-04 diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 12e16800b..9418a228f 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -553,18 +553,18 @@ async fn add_parts( // signals whether the current user is a bot let is_bot = context.get_config_bool(Config::Bot).await?; + let create_blocked = match test_normal_chat { + Some(ChatIdBlocked { + id: _, + blocked: Blocked::Request, + }) if is_bot => Blocked::Not, + Some(ChatIdBlocked { id: _, blocked }) => blocked, + None => Blocked::Request, + }; + if chat_id.is_none() { // try to create a group - let create_blocked = match test_normal_chat { - Some(ChatIdBlocked { - id: _, - blocked: Blocked::Not, - }) => Blocked::Not, - _ if is_bot => Blocked::Not, - _ => Blocked::Request, - }; - if let Some((new_chat_id, new_chat_id_blocked)) = create_or_lookup_group( context, mime_parser, @@ -581,13 +581,15 @@ async fn add_parts( { chat_id = Some(new_chat_id); chat_id_blocked = new_chat_id_blocked; + } + } - // if the chat is somehow blocked but we want to create a non-blocked chat, - // unblock the chat - if chat_id_blocked != Blocked::Not && create_blocked == Blocked::Not { - new_chat_id.unblock(context).await?; - chat_id_blocked = Blocked::Not; - } + // if the chat is somehow blocked but we want to create a non-blocked chat, + // unblock the chat + if chat_id_blocked != Blocked::Not && create_blocked != Blocked::Yes { + if let Some(chat_id) = chat_id { + chat_id.set_blocked(context, create_blocked).await?; + chat_id_blocked = create_blocked; } } diff --git a/src/receive_imf/tests.rs b/src/receive_imf/tests.rs index aee277e5d..cf64ee3b5 100644 --- a/src/receive_imf/tests.rs +++ b/src/receive_imf/tests.rs @@ -3046,6 +3046,54 @@ async fn test_no_private_reply_to_blocked_account() -> Result<()> { Ok(()) } +/// Regression test for two bugs: +/// +/// 1. If you blocked some spammer using DC, the 1:1 messages with that contact +/// are not received, but they could easily bypass this restriction creating +/// a new group with only you two as member. +/// 2. A blocked group was sometimes not unblocked when when an unblocked +/// contact sent a message into it. +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_blocked_contact_creates_group() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = tcm.alice().await; + let bob = tcm.bob().await; + let fiona = tcm.fiona().await; + + let chat = alice.create_chat(&bob).await; + chat.id.block(&alice).await?; + + let group_id = bob + .create_group_with_members( + ProtectionStatus::Unprotected, + "group name", + &[&alice, &fiona], + ) + .await; + + let sent = bob.send_text(group_id, "Heyho, I'm a spammer!").await; + let rcvd = alice.recv_msg(&sent).await; + // Alice blocked Bob, so she shouldn't get the message + assert_eq!(rcvd.chat_blocked, Blocked::Yes); + + // Fiona didn't block Bob, though, so she gets the message + let rcvd = fiona.recv_msg(&sent).await; + assert_eq!(rcvd.chat_blocked, Blocked::Request); + + // Fiona writes to the group + rcvd.chat_id.accept(&fiona).await?; + let sent = fiona.send_text(rcvd.chat_id, "Hello from Fiona").await; + + // The group is unblocked now that Fiona sent a message to it + let rcvd = alice.recv_msg(&sent).await; + assert_eq!(rcvd.chat_blocked, Blocked::Request); + // In order not to lose context, Bob's message should also be shown in the group + let msgs = chat::get_chat_msgs(&alice, rcvd.chat_id).await?; + assert_eq!(msgs.len(), 2); + + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_thunderbird_autocrypt() -> Result<()> { let t = TestContext::new_bob().await; diff --git a/src/test_utils.rs b/src/test_utils.rs index 0332df566..003b3578c 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -18,7 +18,10 @@ use tokio::runtime::Handle; use tokio::sync::RwLock; use tokio::task; -use crate::chat::{self, Chat, ChatId, MessageListOptions}; +use crate::chat::{ + self, add_to_chat_contacts_table, create_group_chat, Chat, ChatId, MessageListOptions, + ProtectionStatus, +}; use crate::chatlist::Chatlist; use crate::config::Config; use crate::constants::Chattype; @@ -702,6 +705,25 @@ impl TestContext { ); } } + + pub async fn create_group_with_members( + &self, + protect: ProtectionStatus, + chat_name: &str, + members: &[&TestContext], + ) -> ChatId { + let chat_id = create_group_chat(self, protect, chat_name).await.unwrap(); + let mut to_add = vec![]; + for member in members { + let contact = self.add_or_lookup_contact(member).await; + to_add.push(contact.id); + } + add_to_chat_contacts_table(self, chat_id, &to_add) + .await + .unwrap(); + + chat_id + } } impl Deref for TestContext {