* Adding check to description and body length fields.
- Also making the checks return `LemmyError`
- Fixes #1747
* Address PR comments.
* PR comments 2
traits::{Bannable, Crud, Followable},
};
use lemmy_db_views_actor::structs::PersonView;
-use lemmy_utils::{error::LemmyError, utils::time::naive_from_unix, ConnectionId};
+use lemmy_utils::{
+ error::LemmyError,
+ utils::{time::naive_from_unix, validation::is_valid_body_field},
+ ConnectionId,
+};
#[async_trait::async_trait(?Send)]
impl Perform for BanFromCommunity {
// Verify that only mods or admins can ban
is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?;
+ is_valid_body_field(&data.reason)?;
let community_user_ban_form = CommunityPersonBanForm {
community_id: data.community_id,
traits::Crud,
};
use lemmy_db_views_actor::structs::PersonView;
-use lemmy_utils::{error::LemmyError, utils::time::naive_from_unix, ConnectionId};
+use lemmy_utils::{
+ error::LemmyError,
+ utils::{time::naive_from_unix, validation::is_valid_body_field},
+ ConnectionId,
+};
#[async_trait::async_trait(?Send)]
impl Perform for BanPerson {
// Make sure user is an admin
is_admin(&local_user_view)?;
+ is_valid_body_field(&data.reason)?;
+
let ban = data.ban;
let banned_person_id = data.person_id;
let expires = data.expires.map(naive_from_unix);
utils::validation::{
build_totp_2fa,
generate_totp_2fa_secret,
+ is_valid_bio_field,
is_valid_display_name,
is_valid_matrix_id,
},
}
if let Some(Some(bio)) = &bio {
- if bio.chars().count() > 300 {
- return Err(LemmyError::from_message("bio_length_overflow"));
- }
+ is_valid_bio_field(bio)?;
}
if let Some(Some(display_name)) = &display_name {
- if !is_valid_display_name(
+ is_valid_display_name(
display_name.trim(),
site_view.local_site.actor_name_max_length as usize,
- ) {
- return Err(LemmyError::from_message("invalid_username"));
- }
+ )?;
}
if let Some(Some(matrix_user_id)) = &matrix_user_id {
- if !is_valid_matrix_id(matrix_user_id) {
- return Err(LemmyError::from_message("invalid_matrix_id"));
- }
+ is_valid_matrix_id(matrix_user_id)?;
}
let local_user_id = local_user_view.local_user.id;
};
use lemmy_utils::{
error::LemmyError,
- utils::{mention::scrape_text_for_mentions, slurs::remove_slurs},
+ utils::{
+ mention::scrape_text_for_mentions,
+ slurs::remove_slurs,
+ validation::is_valid_body_field,
+ },
ConnectionId,
};
&data.content.clone(),
&local_site_to_slur_regex(&local_site),
);
+ is_valid_body_field(&Some(content_slurs_removed.clone()))?;
// Check for a community ban
let post_id = data.post_id;
use lemmy_db_views::structs::CommentView;
use lemmy_utils::{
error::LemmyError,
- utils::{mention::scrape_text_for_mentions, slurs::remove_slurs},
+ utils::{
+ mention::scrape_text_for_mentions,
+ slurs::remove_slurs,
+ validation::is_valid_body_field,
+ },
ConnectionId,
};
.content
.as_ref()
.map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site)));
+
+ is_valid_body_field(&content_slurs_removed)?;
+
let comment_id = data.comment_id;
let form = CommentUpdateForm::builder()
.content(content_slurs_removed)
error::LemmyError,
utils::{
slurs::{check_slurs, check_slurs_opt},
- validation::is_valid_actor_name,
+ validation::{is_valid_actor_name, is_valid_body_field},
},
ConnectionId,
};
check_slurs(&data.title, &slur_regex)?;
check_slurs_opt(&data.description, &slur_regex)?;
- if !is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize) {
- return Err(LemmyError::from_message("invalid_community_name"));
- }
+ is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?;
+ is_valid_body_field(&data.description)?;
// Double check for duplicate community actor_ids
let community_actor_id = generate_local_apub_endpoint(
utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
};
use lemmy_db_views_actor::structs::CommunityModeratorView;
-use lemmy_utils::{error::LemmyError, utils::slurs::check_slurs_opt, ConnectionId};
+use lemmy_utils::{
+ error::LemmyError,
+ utils::{slurs::check_slurs_opt, validation::is_valid_body_field},
+ ConnectionId,
+};
#[async_trait::async_trait(?Send)]
impl PerformCrud for EditCommunity {
let slur_regex = local_site_to_slur_regex(&local_site);
check_slurs_opt(&data.title, &slur_regex)?;
check_slurs_opt(&data.description, &slur_regex)?;
+ is_valid_body_field(&data.description)?;
// Verify its a mod (only mods can edit it)
let community_id = data.community_id;
error::LemmyError,
utils::{
slurs::{check_slurs, check_slurs_opt},
- validation::{clean_url_params, is_valid_post_title},
+ validation::{clean_url_params, is_valid_body_field, is_valid_post_title},
},
ConnectionId,
};
let data_url = data.url.as_ref();
let url = data_url.map(clean_url_params).map(Into::into); // TODO no good way to handle a "clear"
- if !is_valid_post_title(&data.name) {
- return Err(LemmyError::from_message("invalid_post_title"));
- }
+ is_valid_post_title(&data.name)?;
+ is_valid_body_field(&data.body)?;
check_community_ban(local_user_view.person.id, data.community_id, context.pool()).await?;
check_community_deleted_or_removed(data.community_id, context.pool()).await?;
error::LemmyError,
utils::{
slurs::check_slurs_opt,
- validation::{clean_url_params, is_valid_post_title},
+ validation::{clean_url_params, is_valid_body_field, is_valid_post_title},
},
ConnectionId,
};
check_slurs_opt(&data.body, &slur_regex)?;
if let Some(name) = &data.name {
- if !is_valid_post_title(name) {
- return Err(LemmyError::from_message("invalid_post_title"));
- }
+ is_valid_post_title(name)?;
}
+ is_valid_body_field(&data.body)?;
+
let post_id = data.post_id;
let orig_post = Post::read(context.pool(), post_id).await?;
traits::Crud,
};
use lemmy_db_views::structs::LocalUserView;
-use lemmy_utils::{error::LemmyError, utils::slurs::remove_slurs, ConnectionId};
+use lemmy_utils::{
+ error::LemmyError,
+ utils::{slurs::remove_slurs, validation::is_valid_body_field},
+ ConnectionId,
+};
#[async_trait::async_trait(?Send)]
impl PerformCrud for CreatePrivateMessage {
&data.content.clone(),
&local_site_to_slur_regex(&local_site),
);
+ is_valid_body_field(&Some(content_slurs_removed.clone()))?;
check_person_block(local_user_view.person.id, data.recipient_id, context.pool()).await?;
traits::Crud,
utils::naive_now,
};
-use lemmy_utils::{error::LemmyError, utils::slurs::remove_slurs, ConnectionId};
+use lemmy_utils::{
+ error::LemmyError,
+ utils::{slurs::remove_slurs, validation::is_valid_body_field},
+ ConnectionId,
+};
#[async_trait::async_trait(?Send)]
impl PerformCrud for EditPrivateMessage {
// Doing the update
let content_slurs_removed = remove_slurs(&data.content, &local_site_to_slur_regex(&local_site));
+ is_valid_body_field(&Some(content_slurs_removed.clone()))?;
+
let private_message_id = data.private_message_id;
PrivateMessage::update(
context.pool(),
use lemmy_db_views::structs::SiteView;
use lemmy_utils::{
error::LemmyError,
- utils::slurs::{check_slurs, check_slurs_opt},
+ utils::{
+ slurs::{check_slurs, check_slurs_opt},
+ validation::is_valid_body_field,
+ },
ConnectionId,
};
use url::Url;
site_description_length_check(desc)?;
}
+ is_valid_body_field(&data.sidebar)?;
+
let application_question = diesel_option_overwrite(&data.application_question);
check_application_question(
&application_question,
ListingType,
};
use lemmy_db_views::structs::SiteView;
-use lemmy_utils::{error::LemmyError, utils::slurs::check_slurs_opt, ConnectionId};
+use lemmy_utils::{
+ error::LemmyError,
+ utils::{slurs::check_slurs_opt, validation::is_valid_body_field},
+ ConnectionId,
+};
use std::str::FromStr;
#[async_trait::async_trait(?Send)]
site_description_length_check(desc)?;
}
+ is_valid_body_field(&data.sidebar)?;
+
let application_question = diesel_option_overwrite(&data.application_question);
check_application_question(
&application_question,
check_slurs_opt(&data.answer, &slur_regex)?;
let actor_keypair = generate_actor_keypair()?;
- if !is_valid_actor_name(&data.username, local_site.actor_name_max_length as usize) {
- return Err(LemmyError::from_message("invalid_username"));
- }
+ is_valid_actor_name(&data.username, local_site.actor_name_max_length as usize)?;
let actor_id = generate_local_apub_endpoint(
EndpointType::Person,
&data.username,
-use crate::error::LemmyError;
+use crate::error::{LemmyError, LemmyResult};
use itertools::Itertools;
use once_cell::sync::Lazy;
use regex::Regex;
Regex::new(r"^utm_source|utm_medium|utm_campaign|utm_term|utm_content|gclid|gclsrc|dclid|fbclid$")
.expect("compile regex")
});
+const BODY_MAX_LENGTH: usize = 10000;
+const BIO_MAX_LENGTH: usize = 300;
fn has_newline(name: &str) -> bool {
name.contains('\n')
}
-pub fn is_valid_actor_name(name: &str, actor_name_max_length: usize) -> bool {
- name.chars().count() <= actor_name_max_length
+pub fn is_valid_actor_name(name: &str, actor_name_max_length: usize) -> LemmyResult<()> {
+ let check = name.chars().count() <= actor_name_max_length
&& VALID_ACTOR_NAME_REGEX.is_match(name)
- && !has_newline(name)
+ && !has_newline(name);
+ if !check {
+ Err(LemmyError::from_message("invalid_name"))
+ } else {
+ Ok(())
+ }
}
// Can't do a regex here, reverse lookarounds not supported
-pub fn is_valid_display_name(name: &str, actor_name_max_length: usize) -> bool {
- !name.starts_with('@')
+pub fn is_valid_display_name(name: &str, actor_name_max_length: usize) -> LemmyResult<()> {
+ let check = !name.starts_with('@')
&& !name.starts_with('\u{200b}')
&& name.chars().count() >= 3
&& name.chars().count() <= actor_name_max_length
- && !has_newline(name)
+ && !has_newline(name);
+ if !check {
+ Err(LemmyError::from_message("invalid_username"))
+ } else {
+ Ok(())
+ }
+}
+
+pub fn is_valid_matrix_id(matrix_id: &str) -> LemmyResult<()> {
+ let check = VALID_MATRIX_ID_REGEX.is_match(matrix_id) && !has_newline(matrix_id);
+ if !check {
+ Err(LemmyError::from_message("invalid_matrix_id"))
+ } else {
+ Ok(())
+ }
}
-pub fn is_valid_matrix_id(matrix_id: &str) -> bool {
- VALID_MATRIX_ID_REGEX.is_match(matrix_id) && !has_newline(matrix_id)
+pub fn is_valid_post_title(title: &str) -> LemmyResult<()> {
+ let check = VALID_POST_TITLE_REGEX.is_match(title) && !has_newline(title);
+ if !check {
+ Err(LemmyError::from_message("invalid_post_title"))
+ } else {
+ Ok(())
+ }
}
-pub fn is_valid_post_title(title: &str) -> bool {
- VALID_POST_TITLE_REGEX.is_match(title) && !has_newline(title)
+/// This could be post bodies, comments, or any description field
+pub fn is_valid_body_field(body: &Option<String>) -> LemmyResult<()> {
+ if let Some(body) = body {
+ let check = body.chars().count() <= BODY_MAX_LENGTH;
+ if !check {
+ Err(LemmyError::from_message("invalid_body_field"))
+ } else {
+ Ok(())
+ }
+ } else {
+ Ok(())
+ }
+}
+
+pub fn is_valid_bio_field(bio: &str) -> LemmyResult<()> {
+ let check = bio.chars().count() <= BIO_MAX_LENGTH;
+ if !check {
+ Err(LemmyError::from_message("bio_length_overflow"))
+ } else {
+ Ok(())
+ }
}
pub fn clean_url_params(url: &Url) -> Url {
totp_token: &Option<String>,
site_name: &str,
username: &str,
-) -> Result<(), LemmyError> {
+) -> LemmyResult<()> {
// Check only if they have a totp_secret in the DB
if let Some(totp_secret) = totp_secret {
// Throw an error if their token is missing
#[test]
fn regex_checks() {
- assert!(!is_valid_post_title("hi"));
- assert!(is_valid_post_title("him"));
- assert!(!is_valid_post_title("n\n\n\n\nanother"));
- assert!(!is_valid_post_title("hello there!\n this is a test."));
- assert!(is_valid_post_title("hello there! this is a test."));
+ assert!(is_valid_post_title("hi").is_err());
+ assert!(is_valid_post_title("him").is_ok());
+ assert!(is_valid_post_title("n\n\n\n\nanother").is_err());
+ assert!(is_valid_post_title("hello there!\n this is a test.").is_err());
+ assert!(is_valid_post_title("hello there! this is a test.").is_ok());
}
#[test]
fn test_valid_actor_name() {
let actor_name_max_length = 20;
- assert!(is_valid_actor_name("Hello_98", actor_name_max_length));
- assert!(is_valid_actor_name("ten", actor_name_max_length));
- assert!(!is_valid_actor_name("Hello-98", actor_name_max_length));
- assert!(!is_valid_actor_name("a", actor_name_max_length));
- assert!(!is_valid_actor_name("", actor_name_max_length));
+ assert!(is_valid_actor_name("Hello_98", actor_name_max_length).is_ok());
+ assert!(is_valid_actor_name("ten", actor_name_max_length).is_ok());
+ assert!(is_valid_actor_name("Hello-98", actor_name_max_length).is_err());
+ assert!(is_valid_actor_name("a", actor_name_max_length).is_err());
+ assert!(is_valid_actor_name("", actor_name_max_length).is_err());
}
#[test]
fn test_valid_display_name() {
let actor_name_max_length = 20;
- assert!(is_valid_display_name("hello @there", actor_name_max_length));
- assert!(!is_valid_display_name(
- "@hello there",
- actor_name_max_length
- ));
+ assert!(is_valid_display_name("hello @there", actor_name_max_length).is_ok());
+ assert!(is_valid_display_name("@hello there", actor_name_max_length).is_err());
// Make sure zero-space with an @ doesn't work
- assert!(!is_valid_display_name(
- &format!("{}@my name is", '\u{200b}'),
- actor_name_max_length
- ));
+ assert!(
+ is_valid_display_name(&format!("{}@my name is", '\u{200b}'), actor_name_max_length).is_err()
+ );
}
#[test]
fn test_valid_post_title() {
- assert!(is_valid_post_title("Post Title"));
- assert!(is_valid_post_title(" POST TITLE 😃😃😃😃😃"));
- assert!(!is_valid_post_title("\n \n \n \n ")); // tabs/spaces/newlines
+ assert!(is_valid_post_title("Post Title").is_ok());
+ assert!(is_valid_post_title(" POST TITLE 😃😃😃😃😃").is_ok());
+ assert!(is_valid_post_title("\n \n \n \n ").is_err()); // tabs/spaces/newlines
}
#[test]
fn test_valid_matrix_id() {
- assert!(is_valid_matrix_id("@dess:matrix.org"));
- assert!(!is_valid_matrix_id("dess:matrix.org"));
- assert!(!is_valid_matrix_id(" @dess:matrix.org"));
- assert!(!is_valid_matrix_id("@dess:matrix.org t"));
+ assert!(is_valid_matrix_id("@dess:matrix.org").is_ok());
+ assert!(is_valid_matrix_id("dess:matrix.org").is_err());
+ assert!(is_valid_matrix_id(" @dess:matrix.org").is_err());
+ assert!(is_valid_matrix_id("@dess:matrix.org t").is_err());
}
#[test]