From 68f7dc951278f76a1d8b42b3fc9d6681ed0aa6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emma=20Cypress=20=E2=9A=98?= Date: Sun, 10 Jul 2022 01:51:55 +0000 Subject: [PATCH] Add auto-reporting inappropriate text content (#387) * Add initial support for blocking inappropriate text content To make something clear, **nothing** is automatically censored or deleted as a result of this pull request. This pull request is meant to add two things: - Regenerate new IDs (project, version, user, etc.) with profanity - Send reports to the moderators for new inappropriate content * Make it build * Fix logic issue Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com> --- Cargo.lock | 10 +++++++++ Cargo.toml | 4 +++- src/database/models/ids.rs | 5 ++++- src/routes/auth.rs | 41 ++++++++++++++++++++++++++++++---- src/routes/projects.rs | 40 +++++++++++++++++++++++++++++++++ src/routes/users.rs | 40 +++++++++++++++++++++++++++++++++ src/routes/version_creation.rs | 22 ++++++++++++++++++ src/routes/versions.rs | 21 +++++++++++++++++ src/util/mod.rs | 1 + src/util/report.rs | 38 +++++++++++++++++++++++++++++++ 10 files changed, 216 insertions(+), 6 deletions(-) create mode 100644 src/util/report.rs diff --git a/Cargo.lock b/Cargo.lock index 656bf80b7..3fd3ac306 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -584,6 +584,15 @@ dependencies = [ "jobserver", ] +[[package]] +name = "censor" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5563d2728feef9a6186acdd148bccbe850dad63c5ba55a3b3355abc9137cb3eb" +dependencies = [ + "once_cell", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -1456,6 +1465,7 @@ dependencies = [ "bitflags", "bytes", "cached", + "censor", "dashmap", "dotenv", "env_logger", diff --git a/Cargo.toml b/Cargo.toml index 3e9878be4..28f345cae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,4 +63,6 @@ bytes = "1.1.0" dashmap = "5.2.0" -cached = "0.34.0" \ No newline at end of file +cached = "0.34.0" + +censor = "0.2.0" \ No newline at end of file diff --git a/src/database/models/ids.rs b/src/database/models/ids.rs index b0ad29dff..bca034d59 100644 --- a/src/database/models/ids.rs +++ b/src/database/models/ids.rs @@ -1,5 +1,7 @@ use super::DatabaseError; +use crate::models::ids::base62_impl::to_base62; use crate::models::ids::random_base62_rng; +use censor::Censor; use sqlx::sqlx_macros::Type; const ID_RETRY_COUNT: usize = 20; @@ -13,6 +15,7 @@ macro_rules! generate_ids { let length = $id_length; let mut id = random_base62_rng(&mut rng, length); let mut retry_count = 0; + let censor = Censor::Standard + Censor::Sex; // Check if ID is unique loop { @@ -20,7 +23,7 @@ macro_rules! generate_ids { .fetch_one(&mut *con) .await?; - if results.exists.unwrap_or(true) { + if results.exists.unwrap_or(true) || censor.check(&*to_base62(id)) { id = random_base62_rng(&mut rng, length); } else { break; diff --git a/src/routes/auth.rs b/src/routes/auth.rs index 9f90c90e2..ba254875c 100644 --- a/src/routes/auth.rs +++ b/src/routes/auth.rs @@ -247,19 +247,52 @@ pub async fn auth_callback( } if let Some(username) = username { - User { + let new_user = User { id: user_id, github_id: Some(user.id as i64), - username, + username: username.clone(), name: user.name, email: user.email, avatar_url: Some(user.avatar_url), bio: user.bio, created: OffsetDateTime::now_utc(), role: Role::Developer.to_string(), - } - .insert(&mut transaction) + }; + + crate::util::report::censor_check( + &*username, + None, + None, + Some(user_id), + "New user's username is inappropriate".to_string(), + &mut transaction, + ) .await?; + + if let Some(name) = &new_user.name { + crate::util::report::censor_check( + &*name, + None, + None, + Some(user_id), + "New user's name is inappropriate".to_string(), + &mut transaction, + ) + .await?; + } + if let Some(bio) = &new_user.bio { + crate::util::report::censor_check( + &*bio, + None, + None, + Some(user_id), + "New user's bio is inappropriate".to_string(), + &mut transaction, + ) + .await?; + } + + new_user.insert(&mut transaction).await?; } } } diff --git a/src/routes/projects.rs b/src/routes/projects.rs index 04a7accb6..a8d5c6cc1 100644 --- a/src/routes/projects.rs +++ b/src/routes/projects.rs @@ -387,6 +387,16 @@ pub async fn project_edit( ) .execute(&mut *transaction) .await?; + + crate::util::report::censor_check( + &*title, + Some(project_item.inner.id), + None, + None, + "Project edited with inappropriate title".to_string(), + &mut transaction, + ) + .await?; } if let Some(description) = &new_project.description { @@ -408,6 +418,16 @@ pub async fn project_edit( ) .execute(&mut *transaction) .await?; + + crate::util::report::censor_check( + &*description, + Some(project_item.inner.id), + None, + None, + "Project edited with inappropriate description".to_string(), + &mut transaction, + ) + .await?; } if let Some(status) = &new_project.status { @@ -679,6 +699,16 @@ pub async fn project_edit( )); } } + + crate::util::report::censor_check( + &*slug, + Some(project_item.inner.id), + None, + None, + "Project edited with inappropriate slug".to_string(), + &mut transaction, + ) + .await?; } sqlx::query!( @@ -891,6 +921,16 @@ pub async fn project_edit( ) .execute(&mut *transaction) .await?; + + crate::util::report::censor_check( + &*body, + Some(project_item.inner.id), + None, + None, + "Project edited with inappropriate body".to_string(), + &mut transaction, + ) + .await?; } transaction.commit().await?; diff --git a/src/routes/users.rs b/src/routes/users.rs index f05722d2c..ec8ca9962 100644 --- a/src/routes/users.rs +++ b/src/routes/users.rs @@ -204,6 +204,18 @@ pub async fn user_edit( ) .execute(&mut *transaction) .await?; + + crate::util::report::censor_check( + &*username, + None, + None, + Some(crate::database::models::ids::UserId::from( + user_id, + )), + "User edited with inappropriate username".to_string(), + &mut transaction, + ) + .await?; } else { return Err(ApiError::InvalidInput(format!( "Username {} is taken!", @@ -224,6 +236,20 @@ pub async fn user_edit( ) .execute(&mut *transaction) .await?; + + if let Some(name) = name { + crate::util::report::censor_check( + &*name, + None, + None, + Some(crate::database::models::ids::UserId::from( + user_id, + )), + "User edited with inappropriate name".to_string(), + &mut transaction, + ) + .await?; + } } if let Some(bio) = &new_user.bio { @@ -238,6 +264,20 @@ pub async fn user_edit( ) .execute(&mut *transaction) .await?; + + if let Some(bio) = bio { + crate::util::report::censor_check( + &*bio, + None, + None, + Some(crate::database::models::ids::UserId::from( + user_id, + )), + "User edited with inappropriate bio".to_string(), + &mut transaction, + ) + .await?; + } } if let Some(email) = &new_user.email { diff --git a/src/routes/version_creation.rs b/src/routes/version_creation.rs index 24cf53094..473f5a0ee 100644 --- a/src/routes/version_creation.rs +++ b/src/routes/version_creation.rs @@ -393,6 +393,28 @@ async fn version_create_inner( .insert_many(users, &mut *transaction) .await?; + if let Some(version_body) = version_data.version_body { + crate::util::report::censor_check( + &*version_body, + None, + Some(models::ids::VersionId::from(version_id)), + None, + "Version created with inappropriate changelog".to_string(), + &mut *transaction, + ) + .await?; + } + + crate::util::report::censor_check( + &*version_data.version_title, + None, + Some(models::ids::VersionId::from(version_id)), + None, + "Version created with inappropriate name".to_string(), + &mut *transaction, + ) + .await?; + let response = Version { id: builder.version_id.into(), project_id: builder.project_id.into(), diff --git a/src/routes/versions.rs b/src/routes/versions.rs index 5515947f2..9e344513f 100644 --- a/src/routes/versions.rs +++ b/src/routes/versions.rs @@ -1,5 +1,6 @@ use super::ApiError; use crate::database; +use crate::database::models::VersionId; use crate::models; use crate::models::projects::{Dependency, Version}; use crate::models::teams::Permissions; @@ -247,6 +248,16 @@ pub async fn version_edit( ) .execute(&mut *transaction) .await?; + + crate::util::report::censor_check( + &*name, + None, + Some(VersionId::from(version_id)), + None, + "Version edited with inappropriate name".to_string(), + &mut transaction, + ) + .await?; } if let Some(number) = &new_version.version_number { @@ -463,6 +474,16 @@ pub async fn version_edit( ) .execute(&mut *transaction) .await?; + + crate::util::report::censor_check( + &*body, + None, + Some(VersionId::from(version_id)), + None, + "Version edited with inappropriate changelog".to_string(), + &mut transaction, + ) + .await?; } if let Some(downloads) = &new_version.downloads { diff --git a/src/util/mod.rs b/src/util/mod.rs index d00f562b3..13de35940 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -2,6 +2,7 @@ pub mod auth; pub mod env; pub mod ext; pub mod guards; +pub mod report; pub mod routes; pub mod time_ser; pub mod validate; diff --git a/src/util/report.rs b/src/util/report.rs new file mode 100644 index 000000000..f3b648b08 --- /dev/null +++ b/src/util/report.rs @@ -0,0 +1,38 @@ +use crate::database::models::categories::ReportType; +use crate::database::models::report_item::Report; +use crate::database::models::{ + generate_report_id, DatabaseError, ProjectId, UserId, VersionId, +}; +use crate::models::users::DELETED_USER; +use censor::Censor; +use time::OffsetDateTime; + +pub async fn censor_check( + text: &str, + project: Option, + version: Option, + user: Option, + report_text: String, + mut transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, +) -> Result<(), DatabaseError> { + let censor = Censor::Standard + Censor::Sex; + if censor.check(text) { + let report_type = + ReportType::get_id("inappropriate", &mut *transaction) + .await? + .expect("No database entry for 'inappropriate' report type"); + Report { + id: generate_report_id(&mut transaction).await?, + report_type_id: report_type, + project_id: project, + version_id: version, + user_id: user, + body: report_text, + reporter: UserId::from(DELETED_USER), + created: OffsetDateTime::now_utc(), + } + .insert(&mut transaction) + .await?; + } + Ok(()) +}