Compare commits

..

2 commits

Author SHA1 Message Date
nexy7574
f49c73c031
feat: Forbid suspended users from sending reports
Some checks failed
Checks / Prefligit / prefligit (push) Failing after 4s
Release Docker Image / define-variables (push) Failing after 2s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 4s
Checks / Rust / Clippy (push) Failing after 14s
Checks / Rust / Cargo Test (push) Failing after 16s
2025-07-01 15:44:04 +01:00
nexy7574
59912709aa
feat: Send intentional mentions in report messages 2025-07-01 15:42:38 +01:00

View file

@ -1,4 +1,7 @@
use std::time::Duration;
use std::{
ops::{Mul, Sub},
time::Duration,
};
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
@ -6,19 +9,35 @@ use conduwuit::{Err, Error, Result, debug_info, info, matrix::pdu::PduEvent, uti
use conduwuit_service::Services;
use rand::Rng;
use ruma::{
EventId, RoomId, UserId,
EventId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId,
api::client::{
error::ErrorKind,
report_user,
room::{report_content, report_room},
},
events::room::message,
events::{
Mentions,
room::{
message,
message::{RoomMessageEvent, RoomMessageEventContent},
},
},
int,
};
use tokio::time::sleep;
use crate::Ruma;
struct Report {
sender: OwnedUserId,
room_id: Option<OwnedRoomId>,
event_id: Option<OwnedEventId>,
user_id: Option<OwnedUserId>,
report_type: String,
reason: Option<String>,
score: Option<ruma::Int>,
}
/// # `POST /_matrix/client/v3/rooms/{roomId}/report`
///
/// Reports an abusive room to homeserver admins
@ -30,6 +49,9 @@ pub(crate) async fn report_room_route(
) -> Result<report_room::v3::Response> {
// user authentication
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
if body.reason.as_ref().is_some_and(|s| s.len() > 750) {
return Err(Error::BadRequest(
@ -56,18 +78,17 @@ pub(crate) async fn report_room_route(
body.reason.as_deref().unwrap_or("")
);
// send admin room message that we received the report with an @room ping for
// urgency
services
.admin
.send_message(message::RoomMessageEventContent::text_markdown(format!(
"@room Room report received from {} -\n\nRoom ID: {}\n\nReport Reason: {}",
sender_user.to_owned(),
body.room_id,
body.reason.as_deref().unwrap_or("")
)))
.await
.ok();
let report = Report {
sender: sender_user.to_owned(),
room_id: Some(body.room_id.to_owned()),
event_id: None,
user_id: None,
report_type: "room".to_string(),
reason: body.reason.clone(),
score: None,
};
services.admin.send_message(build_report(report)).await.ok();
Ok(report_room::v3::Response {})
}
@ -83,6 +104,9 @@ pub(crate) async fn report_event_route(
) -> Result<report_content::v3::Response> {
// user authentication
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
delay_response().await;
@ -108,23 +132,16 @@ pub(crate) async fn report_event_route(
body.event_id,
body.reason.as_deref().unwrap_or("")
);
// send admin room message that we received the report with an @room ping for
// urgency
services
.admin
.send_message(message::RoomMessageEventContent::text_markdown(format!(
"@room Event report received from {} -\n\nEvent ID: {}\nRoom ID: {}\nSent By: \
{}\n\nReport Score: {}\nReport Reason: {}",
sender_user.to_owned(),
pdu.event_id,
pdu.room_id,
pdu.sender,
body.score.unwrap_or_else(|| ruma::Int::from(0)),
body.reason.as_deref().unwrap_or("")
)))
.await
.ok();
let report = Report {
sender: sender_user.to_owned(),
room_id: Some(body.room_id.to_owned()),
event_id: Some(body.event_id.to_owned()),
user_id: None,
report_type: "event".to_string(),
reason: body.reason.clone(),
score: body.score,
};
services.admin.send_message(build_report(report)).await.ok();
Ok(report_content::v3::Response {})
}
@ -137,6 +154,9 @@ pub(crate) async fn report_user_route(
) -> Result<report_user::v3::Response> {
// user authentication
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
if body.reason.as_ref().is_some_and(|s| s.len() > 750) {
return Err(Error::BadRequest(
@ -152,24 +172,23 @@ pub(crate) async fn report_user_route(
return Ok(report_user::v3::Response {});
}
let report = Report {
sender: sender_user.to_owned(),
room_id: None,
event_id: None,
user_id: Some(body.user_id.to_owned()),
report_type: "user".to_string(),
reason: body.reason.clone(),
score: None,
};
info!(
"Received room report from {sender_user} for user {} with reason: \"{}\"",
body.user_id,
body.reason.as_deref().unwrap_or("")
);
// send admin room message that we received the report with an @room ping for
// urgency
services
.admin
.send_message(message::RoomMessageEventContent::text_markdown(format!(
"@room User report received from {} -\n\nUser ID: {}\n\nReport Reason: {}",
sender_user.to_owned(),
body.user_id,
body.reason.as_deref().unwrap_or("")
)))
.await
.ok();
services.admin.send_message(build_report(report)).await.ok();
Ok(report_user::v3::Response {})
}
@ -231,6 +250,33 @@ async fn is_event_report_valid(
Ok(())
}
/// Builds a report message to be sent to the admin room.
fn build_report(report: Report) -> RoomMessageEventContent {
let mut text =
format!("@room New {} report received from {}:\n\n", report.report_type, report.sender);
if report.user_id.is_some() {
text.push_str(&format!("- Reported User ID: `{}`\n", report.user_id.unwrap()));
}
if report.room_id.is_some() {
text.push_str(&format!("- Reported Room ID: `{}`\n", report.room_id.unwrap()));
}
if report.event_id.is_some() {
text.push_str(&format!("- Reported Event ID: `{}`\n", report.event_id.unwrap()));
}
if let Some(score) = report.score {
if score < int!(0) {
score.mul(int!(-1)); // invert the score to make it N/100
// unsure why the spec says -100 to 0, but 0 to 100 is more human.
}
text.push_str(&format!("- User-supplied offensiveness score: {}%\n", -score));
}
if let Some(reason) = report.reason {
text.push_str(&format!("- Report Reason: {}\n", reason));
}
RoomMessageEventContent::text_markdown(text).add_mentions(Mentions::with_room_mention());
}
/// even though this is kinda security by obscurity, let's still make a small
/// random delay sending a response per spec suggestion regarding
/// enumerating for potential events existing in our server.