From 48a123353409e9204fc48e0be80a387208aa6d4c Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Thu, 15 Jun 2023 13:13:12 -0700 Subject: [PATCH] Use graphemes for input length checks instead of number of bytes. --- src/signup.rs | 34 +++++++++++++++++++++++++++++++--- src/util.rs | 5 ++++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/signup.rs b/src/signup.rs index 171a643..a2e73f0 100644 --- a/src/signup.rs +++ b/src/signup.rs @@ -81,10 +81,9 @@ pub async fn post_create_user( if password != verify { return Err(CreateUserErrorKind::PasswordMismatch.into()); } - - let password = password.trim(); + let pwlen = password.graphemes(true).size_hint().1.unwrap_or(0); let password = password.as_bytes(); - if !(4..=50).contains(&password.len()) { + if !(4..=50).contains(&pwlen) { return Err(CreateUserErrorKind::BadPassword.into()); } @@ -352,6 +351,35 @@ mod test { 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] async fn username_short() { let pool = get_db_pool().await; diff --git a/src/util.rs b/src/util.rs index 466660c..022c0f8 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,5 +1,7 @@ use std::{error::Error, ops::Range}; +use unicode_segmentation::UnicodeSegmentation; + pub fn validate_optional_length( opt: &Option, len_range: Range, @@ -7,7 +9,8 @@ pub fn validate_optional_length( ) -> Result, E> { if let Some(opt) = opt { 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) } else { Ok(Some(opt.to_string()))