Compare commits
5 commits
main
...
17-01-2023
Author | SHA1 | Date | |
---|---|---|---|
![]() |
29283942a4 | ||
![]() |
432e36aa63 | ||
![]() |
7534ee3946 | ||
![]() |
4ac794a6ab | ||
![]() |
46327021f4 |
7 changed files with 115 additions and 14 deletions
|
@ -52,4 +52,5 @@
|
|||
- Only follow 6 redirects total in our default reqwest ClientBuilder
|
||||
- Generate passwords with 25 characters instead of 15
|
||||
- Add missing `reason` field to user ban events (`/ban`)
|
||||
- For all [`/report`](https://spec.matrix.org/v1.9/client-server-api/#post_matrixclientv3roomsroomidreporteventid) requests: check if the reported event ID belongs to the reported room ID, raise report reasoning character limit to 750, fix broken formatting, make a small delayed random response per spec suggestion on privacy, and check if the sender user is in the reported room.
|
||||
- For all [`/report`](https://spec.matrix.org/v1.9/client-server-api/#post_matrixclientv3roomsroomidreporteventid) requests: check if the reported event ID belongs to the reported room ID, raise report reasoning character limit to 750, fix broken formatting, make a small delayed random response per spec suggestion on privacy, and check if the sender user is in the reported room.
|
||||
- Support blocking servers from downloading remote media from
|
|
@ -8,6 +8,7 @@ use ruma::api::client::{
|
|||
get_media_config,
|
||||
},
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
/// generated MXC ID (`media-id`) length
|
||||
const MXC_LENGTH: usize = 32;
|
||||
|
@ -65,6 +66,17 @@ pub async fn get_remote_content(
|
|||
server_name: &ruma::ServerName,
|
||||
media_id: String,
|
||||
) -> Result<get_content::v3::Response, Error> {
|
||||
// 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.
|
||||
if services()
|
||||
.globals
|
||||
.prevent_media_downloads_from()
|
||||
.contains(&server_name.to_owned())
|
||||
{
|
||||
info!("Received request for remote media `{}` but server is in our media server blocklist. Returning 404.", mxc);
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."));
|
||||
}
|
||||
|
||||
let content_response = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
|
@ -92,7 +104,7 @@ pub async fn get_remote_content(
|
|||
Ok(content_response)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/r0/download/{serverName}/{mediaId}`
|
||||
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media from our server or over federation.
|
||||
///
|
||||
|
@ -123,7 +135,7 @@ pub async fn get_content_route(
|
|||
}
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/r0/download/{serverName}/{mediaId}/{fileName}`
|
||||
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}/{fileName}`
|
||||
///
|
||||
/// Load media from our server or over federation, permitting desired filename.
|
||||
///
|
||||
|
@ -158,7 +170,7 @@ pub async fn get_content_as_filename_route(
|
|||
}
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/r0/thumbnail/{serverName}/{mediaId}`
|
||||
/// # `GET /_matrix/media/v3/thumbnail/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media thumbnail from our server or over federation.
|
||||
///
|
||||
|
@ -189,6 +201,17 @@ pub async fn get_content_thumbnail_route(
|
|||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
||||
})
|
||||
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
||||
// 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.
|
||||
if services()
|
||||
.globals
|
||||
.prevent_media_downloads_from()
|
||||
.contains(&body.server_name.to_owned())
|
||||
{
|
||||
info!("Received request for remote media `{}` but server is in our media server blocklist. Returning 404.", mxc);
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."));
|
||||
}
|
||||
|
||||
let get_thumbnail_response = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
|
|
|
@ -9,12 +9,13 @@ use ruma::{
|
|||
},
|
||||
events::{StateEventType, TimelineEventType},
|
||||
};
|
||||
use serde_json::from_str;
|
||||
use std::{
|
||||
collections::{BTreeMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// # `PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}`
|
||||
/// # `PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}`
|
||||
///
|
||||
/// Send a message event into the room.
|
||||
///
|
||||
|
@ -48,6 +49,52 @@ pub async fn send_message_event_route(
|
|||
));
|
||||
}
|
||||
|
||||
// certain event types require certain fields to be valid in request bodies.
|
||||
// this helps prevent attempting to handle events that we can't deserialise later so don't waste resources on it.
|
||||
//
|
||||
// see https://spec.matrix.org/v1.9/client-server-api/#events-2 for what's required per event type.
|
||||
match body.event_type.to_string().into() {
|
||||
TimelineEventType::RoomMessage => {
|
||||
let body_field = body.body.body.get_field::<String>("body");
|
||||
let msgtype_field = body.body.body.get_field::<String>("msgtype");
|
||||
|
||||
if body_field.is_err() {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"'body' field in JSON request is invalid",
|
||||
));
|
||||
}
|
||||
|
||||
if msgtype_field.is_err() {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"'msgtype' field in JSON request is invalid",
|
||||
));
|
||||
}
|
||||
}
|
||||
TimelineEventType::RoomName => {
|
||||
let name_field = body.body.body.get_field::<String>("name");
|
||||
|
||||
if name_field.is_err() {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"'name' field in JSON request is invalid",
|
||||
));
|
||||
}
|
||||
}
|
||||
TimelineEventType::RoomTopic => {
|
||||
let topic_field = body.body.body.get_field::<String>("topic");
|
||||
|
||||
if topic_field.is_err() {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"'topic' field in JSON request is invalid",
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {} // event may be custom/experimental or can be empty don't do anything with it
|
||||
};
|
||||
|
||||
// Check if this is a new transaction id
|
||||
if let Some(response) =
|
||||
services()
|
||||
|
@ -79,7 +126,7 @@ pub async fn send_message_event_route(
|
|||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: body.event_type.to_string().into(),
|
||||
content: serde_json::from_str(body.body.body.json().get())
|
||||
content: from_str(body.body.body.json().get())
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?,
|
||||
unsigned: Some(unsigned),
|
||||
state_key: None,
|
||||
|
|
|
@ -1822,8 +1822,10 @@ pub async fn create_invite_route(
|
|||
));
|
||||
}
|
||||
|
||||
let mut signed_event = utils::to_canonical_object(&body.event)
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invite event is invalid."))?;
|
||||
let mut signed_event = utils::to_canonical_object(&body.event).map_err(|e| {
|
||||
error!("Failed to convert invite event to canonical JSON: {}", e);
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Invite event is invalid.")
|
||||
})?;
|
||||
|
||||
ruma::signatures::hash_and_sign_event(
|
||||
services().globals.server_name().as_str(),
|
||||
|
|
|
@ -128,6 +128,9 @@ pub struct Config {
|
|||
#[serde(default)]
|
||||
pub allow_guest_registration: bool,
|
||||
|
||||
#[serde(default = "Vec::new")]
|
||||
pub prevent_media_downloads_from: Vec<OwnedServerName>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub catchall: BTreeMap<String, IgnoredAny>,
|
||||
}
|
||||
|
@ -305,6 +308,13 @@ impl fmt::Display for Config {
|
|||
"RocksDB database optimize for spinning disks",
|
||||
&self.rocksdb_optimize_for_spinning_disks.to_string(),
|
||||
),
|
||||
("Prevent Media Downloads From", {
|
||||
let mut lst = vec![];
|
||||
for domain in &self.prevent_media_downloads_from {
|
||||
lst.push(domain.host());
|
||||
}
|
||||
&lst.join(", ")
|
||||
}),
|
||||
];
|
||||
|
||||
let mut msg: String = "Active config values:\n\n".to_owned();
|
||||
|
|
|
@ -423,6 +423,10 @@ impl Service<'_> {
|
|||
self.config.rocksdb_optimize_for_spinning_disks
|
||||
}
|
||||
|
||||
pub fn prevent_media_downloads_from(&self) -> &[OwnedServerName] {
|
||||
&self.config.prevent_media_downloads_from
|
||||
}
|
||||
|
||||
pub fn supported_room_versions(&self) -> Vec<RoomVersionId> {
|
||||
let mut room_versions: Vec<RoomVersionId> = vec![];
|
||||
room_versions.extend(self.stable_room_versions.clone());
|
||||
|
|
|
@ -260,8 +260,17 @@ impl Service {
|
|||
unsigned.insert(
|
||||
"prev_content".to_owned(),
|
||||
CanonicalJsonValue::Object(
|
||||
utils::to_canonical_object(prev_state.content.clone())
|
||||
.expect("event is valid, we just created it"),
|
||||
utils::to_canonical_object(prev_state.content.clone()).map_err(
|
||||
|e| {
|
||||
error!(
|
||||
"Failed to convert prev_state to canonical JSON: {}",
|
||||
e
|
||||
);
|
||||
Error::bad_database(
|
||||
"Failed to convert prev_state to canonical JSON.",
|
||||
)
|
||||
},
|
||||
)?,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -802,7 +811,7 @@ impl Service {
|
|||
|k, s| auth_events.get(&(k.clone(), s.to_owned())),
|
||||
)
|
||||
.map_err(|e| {
|
||||
error!("{:?}", e);
|
||||
error!("Auth check failed: {:?}", e);
|
||||
Error::bad_database("Auth check failed.")
|
||||
})?;
|
||||
|
||||
|
@ -814,8 +823,10 @@ impl Service {
|
|||
}
|
||||
|
||||
// Hash and sign
|
||||
let mut pdu_json =
|
||||
utils::to_canonical_object(&pdu).expect("event is valid, we just created it");
|
||||
let mut pdu_json = utils::to_canonical_object(&pdu).map_err(|e| {
|
||||
error!("Failed to convert PDU to canonical JSON: {}", e);
|
||||
Error::bad_database("Failed to convert PDU to canonical JSON.")
|
||||
})?;
|
||||
|
||||
pdu_json.remove("event_id");
|
||||
|
||||
|
@ -1103,7 +1114,10 @@ impl Service {
|
|||
pdu.redact(room_version_id, reason)?;
|
||||
self.replace_pdu(
|
||||
&pdu_id,
|
||||
&utils::to_canonical_object(&pdu).expect("PDU is an object"),
|
||||
&utils::to_canonical_object(&pdu).map_err(|e| {
|
||||
error!("Failed to convert PDU to canonical JSON: {}", e);
|
||||
Error::bad_database("Failed to convert PDU to canonical JSON.")
|
||||
})?,
|
||||
&pdu,
|
||||
)?;
|
||||
}
|
||||
|
|
Reference in a new issue