Add lowercase only check for community names

This commit is contained in:
SleeplessOne1917 2024-11-03 21:45:26 -05:00
parent 02ba54c589
commit eef51e35d2
3 changed files with 75 additions and 76 deletions

View file

@ -5,26 +5,15 @@ use lemmy_api_common::{
community::{CommunityResponse, CreateCommunity},
context::LemmyContext,
utils::{
generate_followers_url,
generate_inbox_url,
generate_local_apub_endpoint,
get_url_blocklist,
is_admin,
local_site_to_slur_regex,
process_markdown_opt,
proxy_image_link_api,
EndpointType,
generate_followers_url, generate_inbox_url, generate_local_apub_endpoint, get_url_blocklist,
is_admin, local_site_to_slur_regex, process_markdown_opt, proxy_image_link_api, EndpointType,
},
};
use lemmy_db_schema::{
source::{
actor_language::{CommunityLanguage, SiteLanguage},
community::{
Community,
CommunityFollower,
CommunityFollowerForm,
CommunityInsertForm,
CommunityModerator,
Community, CommunityFollower, CommunityFollowerForm, CommunityInsertForm, CommunityModerator,
CommunityModeratorForm,
},
},
@ -37,9 +26,7 @@ use lemmy_utils::{
utils::{
slurs::check_slurs,
validation::{
is_valid_actor_name,
is_valid_body_field,
site_or_community_description_length_check,
is_valid_body_field, is_valid_community_name, site_or_community_description_length_check,
},
},
};
@ -80,7 +67,7 @@ pub async fn create_community(
let banner = diesel_url_create(data.banner.as_deref())?;
let banner = proxy_image_link_api(banner, &context).await?;
is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?;
is_valid_community_name(&data.name, local_site.actor_name_max_length as usize)?;
// Double check for duplicate community actor_ids
let community_actor_id = generate_local_apub_endpoint(

View file

@ -6,17 +6,9 @@ use lemmy_api_common::{
oauth_provider::AuthenticateWithOauth,
person::{LoginResponse, Register},
utils::{
check_email_verified,
check_registration_application,
check_user_valid,
generate_inbox_url,
generate_local_apub_endpoint,
honeypot_check,
local_site_to_slur_regex,
password_length_check,
send_new_applicant_email_to_admins,
send_verification_email,
EndpointType,
check_email_verified, check_registration_application, check_user_valid, generate_inbox_url,
generate_local_apub_endpoint, honeypot_check, local_site_to_slur_regex, password_length_check,
send_new_applicant_email_to_admins, send_verification_email, EndpointType,
},
};
use lemmy_db_schema::{
@ -41,7 +33,7 @@ use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
utils::{
slurs::{check_slurs, check_slurs_opt},
validation::is_valid_actor_name,
validation::is_valid_username,
},
};
use serde::{Deserialize, Serialize};
@ -407,7 +399,7 @@ async fn create_person(
context: &Data<LemmyContext>,
) -> Result<Person, LemmyError> {
let actor_keypair = generate_actor_keypair()?;
is_valid_actor_name(&username, local_site.actor_name_max_length as usize)?;
is_valid_username(&username, local_site.actor_name_max_length as usize)?;
let actor_id = generate_local_apub_endpoint(
EndpointType::Person,
&username,

View file

@ -84,27 +84,37 @@ fn has_newline(name: &str) -> bool {
name.contains('\n')
}
pub fn is_valid_actor_name(name: &str, actor_name_max_length: usize) -> LemmyResult<()> {
static VALID_ACTOR_NAME_REGEX_EN: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^[a-zA-Z0-9_]{3,}$").expect("compile regex"));
static VALID_ACTOR_NAME_REGEX_AR: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^[\p{Arabic}0-9_]{3,}$").expect("compile regex"));
static VALID_ACTOR_NAME_REGEX_RU: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^[\p{Cyrillic}0-9_]{3,}$").expect("compile regex"));
let check = name.chars().count() <= actor_name_max_length && !has_newline(name);
pub fn is_valid_username(name: &str, actor_name_max_length: usize) -> LemmyResult<()> {
// Only allow characters from a single alphabet per username. This avoids problems with lookalike
// characters like `o` which looks identical in Latin and Cyrillic, and can be used to imitate
// other users. Checks for additional alphabets can be added in the same way.
let lang_check = VALID_ACTOR_NAME_REGEX_EN.is_match(name)
|| VALID_ACTOR_NAME_REGEX_AR.is_match(name)
|| VALID_ACTOR_NAME_REGEX_RU.is_match(name);
static VALID_USERNAME_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^(?:[a-zA-Z0-9_]{3,}|[0-9_\p{Arabic}]{3,}|[0-9_\p{Cyrillic}]{3,})$")
.expect("compile regex")
});
if !check || !lang_check {
Err(LemmyErrorType::InvalidName.into())
} else {
is_valid_actor_name(name, actor_name_max_length, &VALID_USERNAME_REGEX)
}
pub fn is_valid_community_name(name: &str, actor_name_max_length: usize) -> LemmyResult<()> {
// Only allow characters from a single alphabet per username. This avoids problems with lookalike
// characters like `o` which looks identical in Latin and Cyrillic, and can be used to imitate
// other users. Checks for additional alphabets can be added in the same way.
static VALID_COMMUNITY_NAME_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(
r"^(?:[0-9_a-z]{3,}|[0-9_[\p{Arabic}]&&\P{Lu}&&\P{Lt}]{3,}|[0-9_[\p{Cyrillic}&&\P{Lu}&&\P{Lt}]]{3,})$",
)
.expect("compile regex")
});
is_valid_actor_name(name, actor_name_max_length, &VALID_COMMUNITY_NAME_REGEX)
}
fn is_valid_actor_name(name: &str, actor_name_max_length: usize, r: &Regex) -> LemmyResult<()> {
if name.len() <= actor_name_max_length && !has_newline(name) && r.is_match(name) {
Ok(())
} else {
Err(LemmyErrorType::InvalidName.into())
}
}
@ -356,24 +366,11 @@ mod tests {
use crate::{
error::{LemmyErrorType, LemmyResult},
utils::validation::{
build_and_check_regex,
check_site_visibility_valid,
check_urls_are_valid,
clean_url,
clean_urls_in_text,
is_url_blocked,
is_valid_actor_name,
is_valid_bio_field,
is_valid_display_name,
is_valid_matrix_id,
is_valid_post_title,
is_valid_url,
site_name_length_check,
site_or_community_description_length_check,
BIO_MAX_LENGTH,
SITE_DESCRIPTION_MAX_LENGTH,
SITE_NAME_MAX_LENGTH,
URL_MAX_LENGTH,
build_and_check_regex, check_site_visibility_valid, check_urls_are_valid, clean_url,
clean_urls_in_text, is_url_blocked, is_valid_bio_field, is_valid_community_name,
is_valid_display_name, is_valid_matrix_id, is_valid_post_title, is_valid_url,
is_valid_username, site_name_length_check, site_or_community_description_length_check,
BIO_MAX_LENGTH, SITE_DESCRIPTION_MAX_LENGTH, SITE_NAME_MAX_LENGTH, URL_MAX_LENGTH,
},
};
use pretty_assertions::assert_eq;
@ -421,23 +418,46 @@ mod tests {
}
#[test]
fn test_valid_actor_name() {
fn test_valid_username() {
let actor_name_max_length = 20;
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("تجريب", actor_name_max_length).is_ok());
assert!(is_valid_actor_name("تجريب_123", actor_name_max_length).is_ok());
assert!(is_valid_actor_name("Владимир", actor_name_max_length).is_ok());
assert!(is_valid_username("Hello_98", actor_name_max_length).is_ok());
assert!(is_valid_username("ten", actor_name_max_length).is_ok());
assert!(is_valid_username("تجريب", actor_name_max_length).is_ok());
assert!(is_valid_username("تجريب_123", actor_name_max_length).is_ok());
assert!(is_valid_username("Владимир", actor_name_max_length).is_ok());
// mixed scripts
assert!(is_valid_actor_name("تجريب_abc", actor_name_max_length).is_err());
assert!(is_valid_actor_name("Влад_abc", actor_name_max_length).is_err());
assert!(is_valid_username("تجريب_abc", actor_name_max_length).is_err());
assert!(is_valid_username("Влад_abc", actor_name_max_length).is_err());
// dash
assert!(is_valid_actor_name("Hello-98", actor_name_max_length).is_err());
assert!(is_valid_username("Hello-98", actor_name_max_length).is_err());
// too short
assert!(is_valid_actor_name("a", actor_name_max_length).is_err());
assert!(is_valid_username("a", actor_name_max_length).is_err());
// empty
assert!(is_valid_actor_name("", actor_name_max_length).is_err());
assert!(is_valid_username("", actor_name_max_length).is_err());
}
#[test]
fn test_valid_community_name() {
let actor_name_max_length = 20;
assert!(is_valid_community_name("hello_98", actor_name_max_length).is_ok());
assert!(is_valid_community_name("ten", actor_name_max_length).is_ok());
assert!(is_valid_community_name("تجريب", actor_name_max_length).is_ok());
assert!(is_valid_community_name("تجريب_123", actor_name_max_length).is_ok());
assert!(is_valid_community_name("владимир", actor_name_max_length).is_ok());
// uppercase
assert!(is_valid_community_name("Ten", actor_name_max_length).is_err());
assert!(is_valid_community_name("Владимир", actor_name_max_length).is_err());
// mixed scripts
assert!(is_valid_community_name("تجريب_abc", actor_name_max_length).is_err());
assert!(is_valid_community_name("Влад_abc", actor_name_max_length).is_err());
// dash
assert!(is_valid_community_name("hello-98", actor_name_max_length).is_err());
// too short
assert!(is_valid_community_name("a", actor_name_max_length).is_err());
// empty
assert!(is_valid_community_name("", actor_name_max_length).is_err());
}
#[test]