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>
This commit is contained in:
Emma Cypress ⚘
2022-07-10 01:51:55 +00:00
committed by GitHub
parent 18d1bc56fd
commit 68f7dc9512
10 changed files with 216 additions and 6 deletions

10
Cargo.lock generated
View File

@@ -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",

View File

@@ -63,4 +63,6 @@ bytes = "1.1.0"
dashmap = "5.2.0"
cached = "0.34.0"
cached = "0.34.0"
censor = "0.2.0"

View File

@@ -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;

View File

@@ -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?;
}
}
}

View File

@@ -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?;

View File

@@ -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 {

View File

@@ -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(),

View File

@@ -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 {

View File

@@ -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;

38
src/util/report.rs Normal file
View File

@@ -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<ProjectId>,
version: Option<VersionId>,
user: Option<UserId>,
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(())
}