Use graphemes for input length checks instead of number of bytes.

This commit is contained in:
Joe Ardent 2023-06-15 13:13:12 -07:00
parent 1540153b67
commit 48a1233534
2 changed files with 35 additions and 4 deletions

View file

@ -81,10 +81,9 @@ pub async fn post_create_user(
if password != verify { if password != verify {
return Err(CreateUserErrorKind::PasswordMismatch.into()); return Err(CreateUserErrorKind::PasswordMismatch.into());
} }
let pwlen = password.graphemes(true).size_hint().1.unwrap_or(0);
let password = password.trim();
let password = password.as_bytes(); let password = password.as_bytes();
if !(4..=50).contains(&password.len()) { if !(4..=50).contains(&pwlen) {
return Err(CreateUserErrorKind::BadPassword.into()); return Err(CreateUserErrorKind::BadPassword.into());
} }
@ -352,6 +351,35 @@ mod test {
assert_eq!(&expected, body); assert_eq!(&expected, body);
} }
#[tokio::test]
async fn multibyte_password_too_short() {
let pw = "🤡";
// min length is 4
assert_eq!(pw.len(), 4);
let pool = get_db_pool().await;
let server = server_with_pool(&pool).await;
let form =
format!("username=test_user&displayname=Test+User&password={pw}&pw_verify={pw}");
let body = massage(&form);
let resp = server
.post("/signup")
// failure to sign up is not failure to submit the request
.expect_success()
.bytes(body)
.content_type(FORM_CONTENT_TYPE)
.await;
// no user in db
let user = User::try_get("test_user", &pool).await;
assert!(user.is_err());
let body = std::str::from_utf8(resp.bytes()).unwrap();
let expected = CreateUserError(CreateUserErrorKind::BadPassword).to_string();
assert_eq!(&expected, body);
}
#[tokio::test] #[tokio::test]
async fn username_short() { async fn username_short() {
let pool = get_db_pool().await; let pool = get_db_pool().await;

View file

@ -1,5 +1,7 @@
use std::{error::Error, ops::Range}; use std::{error::Error, ops::Range};
use unicode_segmentation::UnicodeSegmentation;
pub fn validate_optional_length<E: Error>( pub fn validate_optional_length<E: Error>(
opt: &Option<String>, opt: &Option<String>,
len_range: Range<usize>, len_range: Range<usize>,
@ -7,7 +9,8 @@ pub fn validate_optional_length<E: Error>(
) -> Result<Option<String>, E> { ) -> Result<Option<String>, E> {
if let Some(opt) = opt { if let Some(opt) = opt {
let opt = opt.trim(); let opt = opt.trim();
if !len_range.contains(&opt.len()) { let len = opt.graphemes(true).size_hint().1.unwrap();
if !len_range.contains(&len) {
Err(err) Err(err)
} else { } else {
Ok(Some(opt.to_string())) Ok(Some(opt.to_string()))