From ef1a8f9dea8140628beadb9e77f8de6cb2c2dee5 Mon Sep 17 00:00:00 2001 From: Bart Oostveen Date: Mon, 28 Sep 2026 09:53:41 +0200 Subject: [PATCH] matrix-continuwuity_latest: drop backports We are propagating breaking changes of 26.x (removal of LDAP support, along with a few minor changed) to stable, because of multiple security fixes that are not feasible to backport to 0.5.10. This is because that would require rebasing ruwuma (the Continuwuity fork that is not maintained since 26.x) on top of ruma, as well as backporting the thousands of lines of code that came with the ruwuma -> ruma migration. Not-cherry-picked-because: neccesary for backporting all changes on master to stable, see commit message. --- .../0001-fix-backport-SEC10.patch | 250 ------------------ .../0002-fix-backport-SEC28.patch | 37 --- .../0003-fix-backport-SEC26.patch | 132 --------- .../ma/matrix-continuwuity_latest/package.nix | 6 - 4 files changed, 425 deletions(-) delete mode 100644 pkgs/by-name/ma/matrix-continuwuity_latest/0001-fix-backport-SEC10.patch delete mode 100644 pkgs/by-name/ma/matrix-continuwuity_latest/0002-fix-backport-SEC28.patch delete mode 100644 pkgs/by-name/ma/matrix-continuwuity_latest/0003-fix-backport-SEC26.patch diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/0001-fix-backport-SEC10.patch b/pkgs/by-name/ma/matrix-continuwuity_latest/0001-fix-backport-SEC10.patch deleted file mode 100644 index 042b4b12aaf2..000000000000 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/0001-fix-backport-SEC10.patch +++ /dev/null @@ -1,250 +0,0 @@ -From d25c12c343b53ea90025c11808700ad6267f4e59 Mon Sep 17 00:00:00 2001 -From: timedout -Date: Wed, 29 Jul 2026 16:38:05 +0100 -Subject: [PATCH] fix(backport): SEC10 - -Reviewed-By: Ginger -Co-Authored-By: Erwan Leboucher - -(cherry picked from commit 71016a0d7f79289f3bf8d7816c1fec3c85c43153) ---- - src/api/client/sync/v5.rs | 133 +++++++++++++++++++++++++++++++------- - 1 file changed, 111 insertions(+), 22 deletions(-) - -diff --git a/src/api/client/sync/v5.rs b/src/api/client/sync/v5.rs -index 183f3aaac..03a4c1972 100644 ---- a/src/api/client/sync/v5.rs -+++ b/src/api/client/sync/v5.rs -@@ -1,6 +1,6 @@ - use std::{ - cmp::{self, Ordering}, -- collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque}, -+ collections::{BTreeMap, BTreeSet, HashMap, HashSet}, - ops::Deref, - time::Duration, - }; -@@ -28,6 +28,7 @@ - use ruma::{ - DeviceId, OwnedEventId, OwnedRoomId, RoomId, UInt, UserId, - api::client::sync::sync_events::{self, DeviceLists, UnreadNotificationsCount}, -+ assign, - directory::RoomTypeFilter, - events::{ - AnyRawAccountDataEvent, AnySyncEphemeralRoomEvent, AnySyncStateEvent, StateEventType, -@@ -139,6 +140,13 @@ pub(crate) async fn sync_events_v5_route( - let (all_joined_rooms, all_invited_rooms, all_knocked_rooms) = - join3(all_joined_rooms, all_invited_rooms, all_knocked_rooms).await; - -+ let allowed_rooms: BTreeSet = all_joined_rooms -+ .iter() -+ .chain(all_invited_rooms.iter()) -+ .chain(all_knocked_rooms.iter()) -+ .cloned() -+ .collect(); -+ - let all_joined_rooms = all_joined_rooms.iter().map(AsRef::as_ref); - let all_invited_rooms = all_invited_rooms.iter().map(AsRef::as_ref); - let all_knocked_rooms = all_knocked_rooms.iter().map(AsRef::as_ref); -@@ -192,13 +200,14 @@ pub(crate) async fn sync_events_v5_route( - ) - .await; - -- fetch_subscriptions(services, sync_info, &known_rooms, &mut todo_rooms).await; -+ fetch_subscriptions(services, sync_info, &known_rooms, &allowed_rooms, &mut todo_rooms).await; - - response.rooms = process_rooms( - services, - sender_user, - next_batch, - all_invited_rooms.clone(), -+ all_knocked_rooms.clone(), - &todo_rooms, - &mut response, - &body, -@@ -208,6 +217,7 @@ pub(crate) async fn sync_events_v5_route( - if response.rooms.iter().all(|(id, r)| { - r.timeline.is_empty() - && r.required_state.is_empty() -+ && r.invite_state.is_none() - && !response.extensions.receipts.rooms.contains_key(id) - }) && response - .extensions -@@ -238,10 +248,17 @@ async fn fetch_subscriptions( - services: &Services, - (sender_user, sender_device, globalsince, body): SyncInfo<'_>, - known_rooms: &KnownRooms, -+ allowed_rooms: &BTreeSet, - todo_rooms: &mut TodoRooms, - ) { - let mut known_subscription_rooms = BTreeSet::new(); - for (room_id, room) in &body.room_subscriptions { -+ // Silently ignore subscriptions to rooms the user is not a member of -+ // (joined or invited). -+ if !allowed_rooms.contains(room_id) { -+ continue; -+ } -+ - let not_exists = services.rooms.metadata.exists(room_id).eq(&false); - - let is_disabled = services.rooms.metadata.is_disabled(room_id); -@@ -399,11 +416,13 @@ async fn handle_lists<'a, Rooms, AllRooms>( - BTreeMap::default() - } - -+#[allow(clippy::too_many_arguments)] - async fn process_rooms<'a, Rooms>( - services: &Services, - sender_user: &UserId, - next_batch: u64, - all_invited_rooms: Rooms, -+ all_knocked_rooms: Rooms, - todo_rooms: &TodoRooms, - response: &mut sync_events::v5::Response, - body: &sync_events::v5::Request, -@@ -416,38 +435,99 @@ async fn process_rooms<'a, Rooms>( - let roomsincecount = PduCount::Normal(*roomsince); - - let mut timestamp: Option<_> = None; -- let mut invite_state = None; - let (timeline_pdus, limited); - let new_room_id: &RoomId = (*room_id).as_ref(); - if all_invited_rooms.clone().any(is_equal_to!(new_room_id)) { -+ let Ok(invite_count) = services -+ .rooms -+ .state_cache -+ .get_invite_count(room_id, sender_user) -+ .await -+ else { -+ continue; -+ }; -+ -+ if *roomsince >= invite_count { -+ continue; -+ } -+ - // TODO: figure out a timestamp we can use for remote invites -- invite_state = services -+ let invite_state = services - .rooms - .state_cache - .invite_state(sender_user, room_id) - .await - .ok(); - -- (timeline_pdus, limited) = (VecDeque::new(), true); -- } else { -- TimelinePdus { pdus: timeline_pdus, limited } = match load_timeline( -- services, -- sender_user, -- room_id, -- Some(roomsincecount), -- Some(PduCount::from(next_batch)), -- *timeline_limit, -- ) -- .await -- { -- | Ok(value) => value, -- | Err(err) => { -- warn!("Encountered missing timeline in {}, error {}", room_id, err); -- continue; -- }, -+ rooms.insert(room_id.clone(), sync_events::v5::response::Room { -+ initial: Some(roomsince == &0), -+ invite_state, -+ limited: true, -+ ..Default::default() -+ }); -+ continue; -+ } -+ -+ if all_knocked_rooms.clone().any(is_equal_to!(new_room_id)) { -+ let Ok(knock_count) = services -+ .rooms -+ .state_cache -+ .get_knock_count(room_id, sender_user) -+ .await -+ else { -+ continue; - }; -+ -+ if *roomsince >= knock_count { -+ continue; -+ } -+ -+ let Ok(knock_state) = services -+ .rooms -+ .state_cache -+ .knock_state(sender_user, room_id) -+ .await -+ else { -+ continue; -+ }; -+ -+ rooms.insert( -+ room_id.clone(), -+ assign!(sync_events::v5::response::Room::new(), { -+ initial: Some(roomsince == &0), -+ invite_state: Some(knock_state), -+ limited: true, -+ }), -+ ); -+ continue; -+ } -+ -+ if !services -+ .rooms -+ .state_cache -+ .is_joined(sender_user, room_id) -+ .await -+ { -+ continue; - } - -+ TimelinePdus { pdus: timeline_pdus, limited } = match load_timeline( -+ services, -+ sender_user, -+ room_id, -+ Some(roomsincecount), -+ Some(PduCount::from(next_batch)), -+ *timeline_limit, -+ ) -+ .await -+ { -+ | Ok(value) => value, -+ | Err(err) => { -+ warn!("Encountered missing timeline in {}, error {}", room_id, err); -+ continue; -+ }, -+ }; -+ - if body.extensions.account_data.enabled == Some(true) { - response.extensions.account_data.rooms.insert( - room_id.to_owned(), -@@ -627,7 +707,7 @@ async fn process_rooms<'a, Rooms>( - }, - initial: Some(roomsince == &0), - is_dm: None, -- invite_state, -+ invite_state: None, - unread_notifications: UnreadNotificationsCount { - highlight_count: Some( - services -@@ -753,6 +833,15 @@ async fn collect_typing_events( - - let mut typing_response = sync_events::v5::response::Typing::default(); - for (room_id, (_, _, roomsince)) in todo_rooms { -+ if !services -+ .rooms -+ .state_cache -+ .is_joined(sender_user, room_id) -+ .await -+ { -+ continue; -+ } -+ - if services.rooms.typing.last_typing_update(room_id).await? <= *roomsince { - continue; - } --- -2.55.0 - diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/0002-fix-backport-SEC28.patch b/pkgs/by-name/ma/matrix-continuwuity_latest/0002-fix-backport-SEC28.patch deleted file mode 100644 index d07419844698..000000000000 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/0002-fix-backport-SEC28.patch +++ /dev/null @@ -1,37 +0,0 @@ -From dcb079931a929880518015794d709f93651154ca Mon Sep 17 00:00:00 2001 -From: Ginger -Date: Tue, 11 Aug 2026 15:59:26 -0400 -Subject: [PATCH 1/2] fix(backport): SEC28 - -(cherry picked from commit 49a8f6f53b6f86ed6abb8952843cb3a38c530ada) ---- - src/service/threepid/mod.rs | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/src/service/threepid/mod.rs b/src/service/threepid/mod.rs -index dcc0b58ab..bda80f82e 100644 ---- a/src/service/threepid/mod.rs -+++ b/src/service/threepid/mod.rs -@@ -112,6 +112,10 @@ pub async fn send_validation_email( - // If a validation session already exists for this client secret, we can either - // reuse it with a new token or return early because it's already valid. - | Some(session) => { -+ if session.email != recipient.email { -+ return Err!(Request(InvalidParam("Wrong email for session."))); -+ } -+ - match session.validation_state { - | ValidationState::Validated => { - // If the existing session is already valid, don't send an email. -@@ -119,7 +123,7 @@ pub async fn send_validation_email( - }, - | ValidationState::Pending(ref mut token) => { - // Check ratelimiting for the target address. -- if self.ratelimiter.check_key(&recipient.email).is_err() { -+ if self.ratelimiter.check_key(&session.email).is_err() { - return Err(Error::BadRequest( - ErrorKind::LimitExceeded { retry_after: None }, - "You're sending emails too fast, try again in a few minutes.", --- -2.55.0 - diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/0003-fix-backport-SEC26.patch b/pkgs/by-name/ma/matrix-continuwuity_latest/0003-fix-backport-SEC26.patch deleted file mode 100644 index 8736fdbeea72..000000000000 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/0003-fix-backport-SEC26.patch +++ /dev/null @@ -1,132 +0,0 @@ -From a00a615799bc55bc093a6298735732565aa2b566 Mon Sep 17 00:00:00 2001 -From: Ginger -Date: Tue, 11 Aug 2026 16:35:24 -0400 -Subject: [PATCH 2/2] fix(backport): SEC26 - -Reviewed-By: timedout -Reviewed-By: Ginger -(cherry picked from commit 700fbe472d4a8385b96f870256bfc00f9a15f809) ---- - src/api/server/event_auth.rs | 27 ++++++++++----------------- - src/api/server/state.rs | 13 ++++++++++++- - src/api/server/state_ids.rs | 13 ++++++++++++- - 3 files changed, 34 insertions(+), 19 deletions(-) - -diff --git a/src/api/server/event_auth.rs b/src/api/server/event_auth.rs -index a9019e8e7..433e18f46 100644 ---- a/src/api/server/event_auth.rs -+++ b/src/api/server/event_auth.rs -@@ -1,12 +1,9 @@ - use std::{borrow::Borrow, iter::once}; - - use axum::extract::State; --use conduwuit::{Err, Error, Result, info, utils::stream::ReadyExt}; -+use conduwuit::{Err, Result, Event, info, utils::stream::ReadyExt}; - use futures::StreamExt; --use ruma::{ -- RoomId, -- api::{client::error::ErrorKind, federation::authorization::get_event_authorization}, --}; -+use ruma::api::federation::authorization::get_event_authorization; - - use super::AccessCheck; - use crate::Ruma; -@@ -42,25 +39,21 @@ pub(crate) async fn get_event_authorization_route( - return Err!(Request(NotFound("This server is not participating in that room."))); - } - -- let event = services -+ // The event must be in the room we just authorised access to -+ if !services - .rooms - .timeline -- .get_pdu_json(&body.event_id) -+ .get_pdu(&body.event_id) - .await -- .map_err(|_| Error::BadRequest(ErrorKind::NotFound, "Event not found."))?; -- -- let room_id_str = event -- .get("room_id") -- .and_then(|val| val.as_str()) -- .ok_or_else(|| Error::bad_database("Invalid event in database."))?; -- -- let room_id = <&RoomId>::try_from(room_id_str) -- .map_err(|_| Error::bad_database("Invalid room_id in event in database."))?; -+ .is_ok_and(|pdu| pdu.room_id_or_hash() == body.room_id) -+ { -+ return Err!(Request(NotFound("Event not found."))); -+ } - - let auth_chain = services - .rooms - .auth_chain -- .event_ids_iter(room_id, once(body.event_id.borrow())) -+ .event_ids_iter(&body.room_id, once(body.event_id.borrow())) - .ready_filter_map(Result::ok) - .filter_map(|id| async move { services.rooms.timeline.get_pdu_json(&id).await.ok() }) - .then(|pdu| services.sending.convert_to_outgoing_federation_event(pdu)) -diff --git a/src/api/server/state.rs b/src/api/server/state.rs -index 5e1ad8ca2..05a008b74 100644 ---- a/src/api/server/state.rs -+++ b/src/api/server/state.rs -@@ -1,7 +1,7 @@ - use std::{borrow::Borrow, iter::once}; - - use axum::extract::State; --use conduwuit::{Err, Result, at, err, info, utils::IterStream}; -+use conduwuit::{Err, Event, Result, at, err, info, utils::IterStream}; - use futures::{FutureExt, StreamExt, TryStreamExt}; - use ruma::{OwnedEventId, api::federation::event::get_room_state}; - -@@ -24,6 +24,17 @@ pub(crate) async fn get_room_state_route( - .check() - .await?; - -+ // The event must be in the room we just authorised access to -+ if !services -+ .rooms -+ .timeline -+ .get_pdu(&body.event_id) -+ .await -+ .is_ok_and(|pdu| pdu.room_id_or_hash() == body.room_id) -+ { -+ return Err!(Request(NotFound("Event not found."))); -+ } -+ - if !services - .rooms - .state_cache -diff --git a/src/api/server/state_ids.rs b/src/api/server/state_ids.rs -index c9dea3116..206f3efc5 100644 ---- a/src/api/server/state_ids.rs -+++ b/src/api/server/state_ids.rs -@@ -1,7 +1,7 @@ - use std::{borrow::Borrow, iter::once}; - - use axum::extract::State; --use conduwuit::{Err, Result, at, err, info}; -+use conduwuit::{Err, Event, Result, at, err, info}; - use futures::{StreamExt, TryStreamExt}; - use ruma::{OwnedEventId, api::federation::event::get_room_state_ids}; - -@@ -25,6 +25,17 @@ pub(crate) async fn get_room_state_ids_route( - .check() - .await?; - -+ // The event must be in the room we just authorised access to -+ if !services -+ .rooms -+ .timeline -+ .get_pdu(&body.event_id) -+ .await -+ .is_ok_and(|pdu| pdu.room_id_or_hash() == body.room_id) -+ { -+ return Err!(Request(NotFound("Event not found."))); -+ } -+ - if !services - .rooms - .state_cache --- -2.55.0 - diff --git a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix index c94e635f7543..197b310f7f71 100644 --- a/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix +++ b/pkgs/by-name/ma/matrix-continuwuity_latest/package.nix @@ -50,12 +50,6 @@ rustPlatform.buildRustPackage (finalAttrs: { cargoHash = "sha256-uvMiFURXxkLbbbwq4pG5hevsLZHQ1wVfTNvzQRTQWxE="; - patches = [ - ./0001-fix-backport-SEC10.patch - ./0002-fix-backport-SEC28.patch - ./0003-fix-backport-SEC26.patch - ]; - nativeBuildInputs = [ pkg-config rustPlatform.bindgenHook