diff --git a/conduwuit-example.toml b/conduwuit-example.toml index 118bc57d..46459547 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -594,7 +594,7 @@ # Currently, conduwuit doesn't support inbound batched key requests, so # this list should only contain other Synapse servers. # -# example: ["matrix.org", "tchncs.de"] +# example: ["matrix.org", "envs.net", "tchncs.de"] # #trusted_servers = ["matrix.org"] @@ -1186,16 +1186,13 @@ # #prune_missing_media = false -# Vector list of regex patterns of server names that conduwuit will refuse -# to download remote media from. -# -# example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"] +# Vector list of servers that conduwuit will refuse to download remote +# media from. # #prevent_media_downloads_from = [] -# List of forbidden server names via regex patterns that we will block -# incoming AND outgoing federation with, and block client room joins / -# remote user invites. +# List of forbidden server names that we will block incoming AND outgoing +# federation with, and block client room joins / remote user invites. # # This check is applied on the room ID, room alias, sender server name, # sender user's server name, inbound federation X-Matrix origin, and @@ -1203,15 +1200,11 @@ # # Basically "global" ACLs. # -# example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"] -# #forbidden_remote_server_names = [] -# List of forbidden server names via regex patterns that we will block all -# outgoing federated room directory requests for. Useful for preventing -# our users from wandering into bad servers or spaces. -# -# example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"] +# List of forbidden server names that we will block all outgoing federated +# room directory requests for. Useful for preventing our users from +# wandering into bad servers or spaces. # #forbidden_remote_room_directory_server_names = [] @@ -1322,7 +1315,7 @@ # used, and startup as warnings if any room aliases in your database have # a forbidden room alias/ID. # -# example: ["19dollarfortnitecards", "b[4a]droom", "badphrase"] +# example: ["19dollarfortnitecards", "b[4a]droom"] # #forbidden_alias_names = [] @@ -1335,7 +1328,7 @@ # startup as warnings if any local users in your database have a forbidden # username. # -# example: ["administrator", "b[a4]dusernam[3e]", "badphrase"] +# example: ["administrator", "b[a4]dusernam[3e]"] # #forbidden_usernames = [] diff --git a/src/api/client/directory.rs b/src/api/client/directory.rs index b44b9f64..9ca35537 100644 --- a/src/api/client/directory.rs +++ b/src/api/client/directory.rs @@ -52,13 +52,10 @@ pub(crate) async fn get_public_rooms_filtered_route( ) -> Result { if let Some(server) = &body.server { if services + .server .config .forbidden_remote_room_directory_server_names - .is_match(server.host()) - || services - .config - .forbidden_remote_server_names - .is_match(server.host()) + .contains(server) { return Err!(Request(Forbidden("Server is banned on this homeserver."))); } @@ -93,13 +90,10 @@ pub(crate) async fn get_public_rooms_route( ) -> Result { if let Some(server) = &body.server { if services + .server .config .forbidden_remote_room_directory_server_names - .is_match(server.host()) - || services - .config - .forbidden_remote_server_names - .is_match(server.host()) + .contains(server) { return Err!(Request(Forbidden("Server is banned on this homeserver."))); } diff --git a/src/api/client/keys.rs b/src/api/client/keys.rs index 650c573f..adbdd715 100644 --- a/src/api/client/keys.rs +++ b/src/api/client/keys.rs @@ -1,7 +1,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use axum::extract::State; -use conduwuit::{Err, Error, Result, debug, debug_warn, err, result::NotFound, utils}; +use conduwuit::{Err, Error, Result, debug, debug_warn, err, info, result::NotFound, utils}; use conduwuit_service::{Services, users::parse_master_key}; use futures::{StreamExt, stream::FuturesUnordered}; use ruma::{ @@ -177,7 +177,7 @@ pub(crate) async fn upload_signing_keys_route( body.master_key.as_ref(), ) .await - .inspect_err(|e| debug!(?e)) + .inspect_err(|e| info!(?e)) { | Ok(exists) => { if let Some(result) = exists { diff --git a/src/api/client/membership.rs b/src/api/client/membership.rs index 1eeacf83..d0345c8e 100644 --- a/src/api/client/membership.rs +++ b/src/api/client/membership.rs @@ -79,9 +79,10 @@ async fn banned_room_check( if let Some(room_id) = room_id { if services.rooms.metadata.is_banned(room_id).await || services + .server .config .forbidden_remote_server_names - .is_match(room_id.server_name().unwrap().host()) + .contains(&room_id.server_name().unwrap().to_owned()) { warn!( "User {user_id} who is not an admin attempted to send an invite for or \ @@ -119,9 +120,10 @@ async fn banned_room_check( } } else if let Some(server_name) = server_name { if services + .server .config .forbidden_remote_server_names - .is_match(server_name.host()) + .contains(&server_name.to_owned()) { warn!( "User {user_id} who is not an admin tried joining a room which has the server \ diff --git a/src/api/client/message.rs b/src/api/client/message.rs index db11ef4a..3e784a4a 100644 --- a/src/api/client/message.rs +++ b/src/api/client/message.rs @@ -261,9 +261,10 @@ pub(crate) async fn is_ignored_pdu( let ignored_type = IGNORED_MESSAGE_TYPES.binary_search(&pdu.kind).is_ok(); let ignored_server = services + .server .config .forbidden_remote_server_names - .is_match(pdu.sender().server_name().host()); + .contains(pdu.sender().server_name()); if ignored_type && (ignored_server || services.users.user_is_ignored(&pdu.sender, user_id).await) diff --git a/src/api/client/room/summary.rs b/src/api/client/room/summary.rs index 67d2e2ad..2fa81bd2 100644 --- a/src/api/client/room/summary.rs +++ b/src/api/client/room/summary.rs @@ -1,7 +1,7 @@ use axum::extract::State; use axum_client_ip::InsecureClientIp; use conduwuit::{ - Err, Result, debug_warn, trace, + Err, Result, debug_warn, utils::{IterStream, future::TryExtExt}, }; use futures::{ @@ -74,12 +74,7 @@ async fn room_summary_response( servers: &[OwnedServerName], sender_user: Option<&UserId>, ) -> Result { - if services - .rooms - .state_cache - .server_in_room(services.globals.server_name(), room_id) - .await - { + if services.rooms.metadata.exists(room_id).await { return local_room_summary_response(services, room_id, sender_user) .boxed() .await; @@ -111,14 +106,14 @@ async fn local_room_summary_response( room_id: &RoomId, sender_user: Option<&UserId>, ) -> Result { - trace!(?sender_user, "Sending local room summary response for {room_id:?}"); let join_rule = services.rooms.state_accessor.get_join_rules(room_id); + let world_readable = services.rooms.state_accessor.is_world_readable(room_id); + let guest_can_join = services.rooms.state_accessor.guest_can_join(room_id); let (join_rule, world_readable, guest_can_join) = join3(join_rule, world_readable, guest_can_join).await; - trace!("{join_rule:?}, {world_readable:?}, {guest_can_join:?}"); user_can_see_summary( services, @@ -220,7 +215,6 @@ async fn remote_room_summary_hierarchy_response( servers: &[OwnedServerName], sender_user: Option<&UserId>, ) -> Result { - trace!(?sender_user, ?servers, "Sending remote room summary response for {room_id:?}"); if !services.config.allow_federation { return Err!(Request(Forbidden("Federation is disabled."))); } @@ -243,7 +237,6 @@ async fn remote_room_summary_hierarchy_response( .collect(); while let Some(Ok(response)) = requests.next().await { - trace!("{response:?}"); let room = response.room.clone(); if room.room_id != room_id { debug_warn!( @@ -285,7 +278,6 @@ async fn user_can_see_summary<'a, I>( where I: Iterator + Send, { - let is_public_room = matches!(join_rule, Public | Knock | KnockRestricted); match sender_user { | Some(sender_user) => { let user_can_see_state_events = services @@ -304,7 +296,7 @@ where if user_can_see_state_events || (is_guest && guest_can_join) - || is_public_room + || matches!(&join_rule, &Public | &Knock | &KnockRestricted) || user_in_allowed_restricted_room { return Ok(()); @@ -317,7 +309,7 @@ where ))) }, | None => { - if is_public_room || world_readable { + if matches!(join_rule, Public | Knock | KnockRestricted) || world_readable { return Ok(()); } diff --git a/src/api/client/user_directory.rs b/src/api/client/user_directory.rs index 99b3bb67..8f564eed 100644 --- a/src/api/client/user_directory.rs +++ b/src/api/client/user_directory.rs @@ -1,20 +1,16 @@ use axum::extract::State; -use conduwuit::{ - Result, - utils::{future::BoolExt, stream::BroadbandExt}, -}; -use futures::{FutureExt, StreamExt, pin_mut}; +use conduwuit::{Result, utils::TryFutureExtExt}; +use futures::{StreamExt, pin_mut}; use ruma::{ - api::client::user_directory::search_users::{self}, - events::room::join_rules::JoinRule, + api::client::user_directory::search_users, + events::{ + StateEventType, + room::join_rules::{JoinRule, RoomJoinRulesEventContent}, + }, }; use crate::Ruma; -// conduwuit can handle a lot more results than synapse -const LIMIT_MAX: usize = 500; -const LIMIT_DEFAULT: usize = 10; - /// # `POST /_matrix/client/r0/user_directory/search` /// /// Searches all known users for a match. @@ -25,63 +21,78 @@ pub(crate) async fn search_users_route( State(services): State, body: Ruma, ) -> Result { - let sender_user = body.sender_user(); - let limit = usize::try_from(body.limit) - .map_or(LIMIT_DEFAULT, usize::from) - .min(LIMIT_MAX); + let sender_user = body.sender_user.as_ref().expect("user is authenticated"); + let limit = usize::try_from(body.limit).map_or(10, usize::from).min(100); // default limit is 10 - let mut users = services - .users - .stream() - .map(ToOwned::to_owned) - .broad_filter_map(async |user_id| { - let user = search_users::v3::User { - user_id: user_id.clone(), - display_name: services.users.displayname(&user_id).await.ok(), - avatar_url: services.users.avatar_url(&user_id).await.ok(), - }; + let users = services.users.stream().filter_map(|user_id| async { + // Filter out buggy users (they should not exist, but you never know...) + let user = search_users::v3::User { + user_id: user_id.to_owned(), + display_name: services.users.displayname(user_id).await.ok(), + avatar_url: services.users.avatar_url(user_id).await.ok(), + }; - let user_id_matches = user - .user_id - .as_str() - .to_lowercase() - .contains(&body.search_term.to_lowercase()); + let user_id_matches = user + .user_id + .to_string() + .to_lowercase() + .contains(&body.search_term.to_lowercase()); - let user_displayname_matches = user.display_name.as_ref().is_some_and(|name| { + let user_displayname_matches = user + .display_name + .as_ref() + .filter(|name| { name.to_lowercase() .contains(&body.search_term.to_lowercase()) - }); + }) + .is_some(); - if !user_id_matches && !user_displayname_matches { - return None; + if !user_id_matches && !user_displayname_matches { + return None; + } + + // It's a matching user, but is the sender allowed to see them? + let mut user_visible = false; + + let user_is_in_public_rooms = services + .rooms + .state_cache + .rooms_joined(&user.user_id) + .any(|room| { + services + .rooms + .state_accessor + .room_state_get_content::( + room, + &StateEventType::RoomJoinRules, + "", + ) + .map_ok_or(false, |content| content.join_rule == JoinRule::Public) + }) + .await; + + if user_is_in_public_rooms { + user_visible = true; + } else { + let user_is_in_shared_rooms = services + .rooms + .state_cache + .user_sees_user(sender_user, &user.user_id) + .await; + + if user_is_in_shared_rooms { + user_visible = true; } + } - let user_in_public_room = services - .rooms - .state_cache - .rooms_joined(&user_id) - .map(ToOwned::to_owned) - .any(|room| async move { - services - .rooms - .state_accessor - .get_join_rules(&room) - .map(|rule| matches!(rule, JoinRule::Public)) - .await - }); + user_visible.then_some(user) + }); - let user_sees_user = services - .rooms - .state_cache - .user_sees_user(sender_user, &user_id); + pin_mut!(users); - pin_mut!(user_in_public_room, user_sees_user); + let limited = users.by_ref().next().await.is_some(); - user_in_public_room.or(user_sees_user).await.then_some(user) - }); - - let results = users.by_ref().take(limit).collect().await; - let limited = users.next().await.is_some(); + let results = users.take(limit).collect().await; Ok(search_users::v3::Response { results, limited }) } diff --git a/src/api/router/auth.rs b/src/api/router/auth.rs index 0eb61ca6..5cd7b831 100644 --- a/src/api/router/auth.rs +++ b/src/api/router/auth.rs @@ -317,9 +317,10 @@ fn auth_server_checks(services: &Services, x_matrix: &XMatrix) -> Result<()> { let origin = &x_matrix.origin; if services + .server .config .forbidden_remote_server_names - .is_match(origin.host()) + .contains(origin) { return Err!(Request(Forbidden(debug_warn!( "Federation requests from {origin} denied." diff --git a/src/api/server/invite.rs b/src/api/server/invite.rs index edd6ac16..cda34fb5 100644 --- a/src/api/server/invite.rs +++ b/src/api/server/invite.rs @@ -38,18 +38,20 @@ pub(crate) async fn create_invite_route( if let Some(server) = body.room_id.server_name() { if services + .server .config .forbidden_remote_server_names - .is_match(server.host()) + .contains(&server.to_owned()) { return Err!(Request(Forbidden("Server is banned on this homeserver."))); } } if services + .server .config .forbidden_remote_server_names - .is_match(body.origin().host()) + .contains(body.origin()) { warn!( "Received federated/remote invite from banned server {} for room ID {}. Rejecting.", diff --git a/src/api/server/make_join.rs b/src/api/server/make_join.rs index ac2c5485..4664b904 100644 --- a/src/api/server/make_join.rs +++ b/src/api/server/make_join.rs @@ -42,9 +42,10 @@ pub(crate) async fn create_join_event_template_route( .await?; if services + .server .config .forbidden_remote_server_names - .is_match(body.origin().host()) + .contains(body.origin()) { warn!( "Server {} for remote user {} tried joining room ID {} which has a server name that \ @@ -58,9 +59,10 @@ pub(crate) async fn create_join_event_template_route( if let Some(server) = body.room_id.server_name() { if services + .server .config .forbidden_remote_server_names - .is_match(server.host()) + .contains(&server.to_owned()) { return Err!(Request(Forbidden(warn!( "Room ID server name {server} is banned on this homeserver." diff --git a/src/api/server/make_knock.rs b/src/api/server/make_knock.rs index 511c13b2..6d71ab2a 100644 --- a/src/api/server/make_knock.rs +++ b/src/api/server/make_knock.rs @@ -33,9 +33,10 @@ pub(crate) async fn create_knock_event_template_route( .await?; if services + .server .config .forbidden_remote_server_names - .is_match(body.origin().host()) + .contains(body.origin()) { warn!( "Server {} for remote user {} tried knocking room ID {} which has a server name \ @@ -49,9 +50,10 @@ pub(crate) async fn create_knock_event_template_route( if let Some(server) = body.room_id.server_name() { if services + .server .config .forbidden_remote_server_names - .is_match(server.host()) + .contains(&server.to_owned()) { return Err!(Request(Forbidden("Server is banned on this homeserver."))); } diff --git a/src/api/server/send_join.rs b/src/api/server/send_join.rs index a66d8890..2e2e89ee 100644 --- a/src/api/server/send_join.rs +++ b/src/api/server/send_join.rs @@ -268,9 +268,10 @@ pub(crate) async fn create_join_event_v1_route( body: Ruma, ) -> Result { if services + .server .config .forbidden_remote_server_names - .is_match(body.origin().host()) + .contains(body.origin()) { warn!( "Server {} tried joining room ID {} through us who has a server name that is \ @@ -283,9 +284,10 @@ pub(crate) async fn create_join_event_v1_route( if let Some(server) = body.room_id.server_name() { if services + .server .config .forbidden_remote_server_names - .is_match(server.host()) + .contains(&server.to_owned()) { warn!( "Server {} tried joining room ID {} through us which has a server name that is \ @@ -314,18 +316,20 @@ pub(crate) async fn create_join_event_v2_route( body: Ruma, ) -> Result { if services + .server .config .forbidden_remote_server_names - .is_match(body.origin().host()) + .contains(body.origin()) { return Err!(Request(Forbidden("Server is banned on this homeserver."))); } if let Some(server) = body.room_id.server_name() { if services + .server .config .forbidden_remote_server_names - .is_match(server.host()) + .contains(&server.to_owned()) { warn!( "Server {} tried joining room ID {} through us which has a server name that is \ diff --git a/src/api/server/send_knock.rs b/src/api/server/send_knock.rs index ee7b6cba..c5ab0306 100644 --- a/src/api/server/send_knock.rs +++ b/src/api/server/send_knock.rs @@ -26,9 +26,10 @@ pub(crate) async fn create_knock_event_v1_route( body: Ruma, ) -> Result { if services + .server .config .forbidden_remote_server_names - .is_match(body.origin().host()) + .contains(body.origin()) { warn!( "Server {} tried knocking room ID {} who has a server name that is globally \ @@ -41,9 +42,10 @@ pub(crate) async fn create_knock_event_v1_route( if let Some(server) = body.room_id.server_name() { if services + .server .config .forbidden_remote_server_names - .is_match(server.host()) + .contains(&server.to_owned()) { warn!( "Server {} tried knocking room ID {} which has a server name that is globally \ diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index 0ca6bbaf..bb509a0d 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -3,7 +3,7 @@ pub mod manager; pub mod proxy; use std::{ - collections::{BTreeMap, BTreeSet}, + collections::{BTreeMap, BTreeSet, HashSet}, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, path::{Path, PathBuf}, }; @@ -715,7 +715,7 @@ pub struct Config { /// Currently, conduwuit doesn't support inbound batched key requests, so /// this list should only contain other Synapse servers. /// - /// example: ["matrix.org", "tchncs.de"] + /// example: ["matrix.org", "envs.net", "tchncs.de"] /// /// default: ["matrix.org"] #[serde(default = "default_trusted_servers")] @@ -1361,18 +1361,15 @@ pub struct Config { #[serde(default)] pub prune_missing_media: bool, - /// Vector list of regex patterns of server names that conduwuit will refuse - /// to download remote media from. - /// - /// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"] + /// Vector list of servers that conduwuit will refuse to download remote + /// media from. /// /// default: [] - #[serde(default, with = "serde_regex")] - pub prevent_media_downloads_from: RegexSet, + #[serde(default)] + pub prevent_media_downloads_from: HashSet, - /// List of forbidden server names via regex patterns that we will block - /// incoming AND outgoing federation with, and block client room joins / - /// remote user invites. + /// List of forbidden server names that we will block incoming AND outgoing + /// federation with, and block client room joins / remote user invites. /// /// This check is applied on the room ID, room alias, sender server name, /// sender user's server name, inbound federation X-Matrix origin, and @@ -1380,21 +1377,17 @@ pub struct Config { /// /// Basically "global" ACLs. /// - /// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"] - /// /// default: [] - #[serde(default, with = "serde_regex")] - pub forbidden_remote_server_names: RegexSet, + #[serde(default)] + pub forbidden_remote_server_names: HashSet, - /// List of forbidden server names via regex patterns that we will block all - /// outgoing federated room directory requests for. Useful for preventing - /// our users from wandering into bad servers or spaces. - /// - /// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"] + /// List of forbidden server names that we will block all outgoing federated + /// room directory requests for. Useful for preventing our users from + /// wandering into bad servers or spaces. /// /// default: [] - #[serde(default, with = "serde_regex")] - pub forbidden_remote_room_directory_server_names: RegexSet, + #[serde(default = "HashSet::new")] + pub forbidden_remote_room_directory_server_names: HashSet, /// Vector list of IPv4 and IPv6 CIDR ranges / subnets *in quotes* that you /// do not want conduwuit to send outbound requests to. Defaults to @@ -1515,10 +1508,11 @@ pub struct Config { /// used, and startup as warnings if any room aliases in your database have /// a forbidden room alias/ID. /// - /// example: ["19dollarfortnitecards", "b[4a]droom", "badphrase"] + /// example: ["19dollarfortnitecards", "b[4a]droom"] /// /// default: [] - #[serde(default, with = "serde_regex")] + #[serde(default)] + #[serde(with = "serde_regex")] pub forbidden_alias_names: RegexSet, /// List of forbidden username patterns/strings. @@ -1530,10 +1524,11 @@ pub struct Config { /// startup as warnings if any local users in your database have a forbidden /// username. /// - /// example: ["administrator", "b[a4]dusernam[3e]", "badphrase"] + /// example: ["administrator", "b[a4]dusernam[3e]"] /// /// default: [] - #[serde(default, with = "serde_regex")] + #[serde(default)] + #[serde(with = "serde_regex")] pub forbidden_usernames: RegexSet, /// Retry failed and incomplete messages to remote servers immediately upon diff --git a/src/service/federation/execute.rs b/src/service/federation/execute.rs index 97314ffb..63f2ccfb 100644 --- a/src/service/federation/execute.rs +++ b/src/service/federation/execute.rs @@ -69,7 +69,7 @@ where .server .config .forbidden_remote_server_names - .is_match(dest.host()) + .contains(dest) { return Err!(Request(Forbidden(debug_warn!("Federation with {dest} is not allowed.")))); } diff --git a/src/service/media/remote.rs b/src/service/media/remote.rs index cdcb429e..b6c853d2 100644 --- a/src/service/media/remote.rs +++ b/src/service/media/remote.rs @@ -426,13 +426,7 @@ fn check_fetch_authorized(&self, mxc: &Mxc<'_>) -> Result<()> { .server .config .prevent_media_downloads_from - .is_match(mxc.server_name.host()) - || self - .services - .server - .config - .forbidden_remote_server_names - .is_match(mxc.server_name.host()) + .contains(mxc.server_name) { // we'll lie to the client and say the blocked server's media was not found and // log. the client has no way of telling anyways so this is a security bonus. diff --git a/tests/test_results/complement/test_results.jsonl b/tests/test_results/complement/test_results.jsonl index 97c2e1b1..c0e28750 100644 --- a/tests/test_results/complement/test_results.jsonl +++ b/tests/test_results/complement/test_results.jsonl @@ -491,7 +491,7 @@ {"Action":"fail","Test":"TestRoomCreationReportsEventsToMyself"} {"Action":"fail","Test":"TestRoomCreationReportsEventsToMyself/parallel"} {"Action":"pass","Test":"TestRoomCreationReportsEventsToMyself/parallel/Joining_room_twice_is_idempotent"} -{"Action":"fail","Test":"TestRoomCreationReportsEventsToMyself/parallel/Room_creation_reports_m.room.create_to_myself"} +{"Action":"pass","Test":"TestRoomCreationReportsEventsToMyself/parallel/Room_creation_reports_m.room.create_to_myself"} {"Action":"pass","Test":"TestRoomCreationReportsEventsToMyself/parallel/Room_creation_reports_m.room.member_to_myself"} {"Action":"pass","Test":"TestRoomCreationReportsEventsToMyself/parallel/Setting_room_topic_reports_m.room.topic_to_myself"} {"Action":"fail","Test":"TestRoomCreationReportsEventsToMyself/parallel/Setting_state_twice_is_idempotent"} @@ -527,17 +527,17 @@ {"Action":"pass","Test":"TestRoomMessagesLazyLoadingLocalUser"} {"Action":"pass","Test":"TestRoomReadMarkers"} {"Action":"pass","Test":"TestRoomReceipts"} -{"Action":"pass","Test":"TestRoomSpecificUsernameAtJoin"} -{"Action":"pass","Test":"TestRoomSpecificUsernameAtJoin/Bob_can_find_Alice_by_mxid"} -{"Action":"pass","Test":"TestRoomSpecificUsernameAtJoin/Bob_can_find_Alice_by_profile_display_name"} -{"Action":"pass","Test":"TestRoomSpecificUsernameAtJoin/Eve_can_find_Alice_by_mxid"} -{"Action":"pass","Test":"TestRoomSpecificUsernameAtJoin/Eve_can_find_Alice_by_profile_display_name"} +{"Action":"fail","Test":"TestRoomSpecificUsernameAtJoin"} +{"Action":"fail","Test":"TestRoomSpecificUsernameAtJoin/Bob_can_find_Alice_by_mxid"} +{"Action":"fail","Test":"TestRoomSpecificUsernameAtJoin/Bob_can_find_Alice_by_profile_display_name"} +{"Action":"fail","Test":"TestRoomSpecificUsernameAtJoin/Eve_can_find_Alice_by_mxid"} +{"Action":"fail","Test":"TestRoomSpecificUsernameAtJoin/Eve_can_find_Alice_by_profile_display_name"} {"Action":"pass","Test":"TestRoomSpecificUsernameAtJoin/Eve_cannot_find_Alice_by_room-specific_name_that_Eve_is_not_privy_to"} -{"Action":"pass","Test":"TestRoomSpecificUsernameChange"} -{"Action":"pass","Test":"TestRoomSpecificUsernameChange/Bob_can_find_Alice_by_mxid"} -{"Action":"pass","Test":"TestRoomSpecificUsernameChange/Bob_can_find_Alice_by_profile_display_name"} -{"Action":"pass","Test":"TestRoomSpecificUsernameChange/Eve_can_find_Alice_by_mxid"} -{"Action":"pass","Test":"TestRoomSpecificUsernameChange/Eve_can_find_Alice_by_profile_display_name"} +{"Action":"fail","Test":"TestRoomSpecificUsernameChange"} +{"Action":"fail","Test":"TestRoomSpecificUsernameChange/Bob_can_find_Alice_by_mxid"} +{"Action":"fail","Test":"TestRoomSpecificUsernameChange/Bob_can_find_Alice_by_profile_display_name"} +{"Action":"fail","Test":"TestRoomSpecificUsernameChange/Eve_can_find_Alice_by_mxid"} +{"Action":"fail","Test":"TestRoomSpecificUsernameChange/Eve_can_find_Alice_by_profile_display_name"} {"Action":"pass","Test":"TestRoomSpecificUsernameChange/Eve_cannot_find_Alice_by_room-specific_name_that_Eve_is_not_privy_to"} {"Action":"fail","Test":"TestRoomState"} {"Action":"fail","Test":"TestRoomState/Parallel"} @@ -589,7 +589,7 @@ {"Action":"fail","Test":"TestSync/parallel/Newly_joined_room_has_correct_timeline_in_incremental_sync"} {"Action":"fail","Test":"TestSync/parallel/Newly_joined_room_includes_presence_in_incremental_sync"} {"Action":"pass","Test":"TestSync/parallel/Newly_joined_room_is_included_in_an_incremental_sync"} -{"Action":"pass","Test":"TestSync/parallel/sync_should_succeed_even_if_the_sync_token_points_to_a_redaction_of_an_unknown_event"} +{"Action":"fail","Test":"TestSync/parallel/sync_should_succeed_even_if_the_sync_token_points_to_a_redaction_of_an_unknown_event"} {"Action":"pass","Test":"TestSyncFilter"} {"Action":"pass","Test":"TestSyncFilter/Can_create_filter"} {"Action":"pass","Test":"TestSyncFilter/Can_download_filter"}