[26.05] matrix-continuwuity: backport SEC10 (#548357)

This commit is contained in:
Adam C. Stephens
2026-08-03 20:42:09 +00:00
committed by GitHub
2 changed files with 254 additions and 0 deletions

View File

@@ -0,0 +1,250 @@
From d25c12c343b53ea90025c11808700ad6267f4e59 Mon Sep 17 00:00:00 2001
From: timedout <git@nexy7574.co.uk>
Date: Wed, 29 Jul 2026 16:38:05 +0100
Subject: [PATCH] fix(backport): SEC10
Reviewed-By: Ginger <ginger@gingershaped.computer>
Co-Authored-By: Erwan Leboucher <erwanleboucher@gmail.com>
(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<OwnedRoomId> = 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<OwnedRoomId>,
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

View File

@@ -50,6 +50,10 @@ rustPlatform.buildRustPackage (finalAttrs: {
cargoHash = "sha256-uvMiFURXxkLbbbwq4pG5hevsLZHQ1wVfTNvzQRTQWxE=";
patches = [
./0001-fix-backport-SEC10.patch
];
nativeBuildInputs = [
pkg-config
rustPlatform.bindgenHook