From 9e67f120b797bae10bd226bbb4632f8d19a24361 Mon Sep 17 00:00:00 2001 From: KunoiSayami Date: Thu, 11 Jun 2026 14:53:23 +0800 Subject: fix: Fix OsRng import and refactor Redis connection setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix password-hash version mismatch (0.6→0.5) causing OsRng to be gated behind missing getrandom feature; enable argon2 rand feature - Extract get_redis_connection() helper to deduplicate Redis client setup across authentication, repo control, and tests - Tighten username validation regex to require ≥2 chars and disallow leading/trailing dots or hyphens - Handle malformed cookies without split_once gracefully (continue instead of panic) - Fix typos: "rated commands" → "related commands" in CLI help text Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: KunoiSayami --- src/authentication.rs | 16 ++++++++-------- src/datastructures.rs | 11 ++++++++++- src/main.rs | 6 +++--- src/test.rs | 10 ++++++---- 4 files changed, 27 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/authentication.rs b/src/authentication.rs index 05707f0..dd27c25 100644 --- a/src/authentication.rs +++ b/src/authentication.rs @@ -42,8 +42,9 @@ impl IOModule { log::trace!("Method is {}", cfg.get_authorizer().method()); // Establish Redis connection early for rate limiting + session storage - let redis_client = redis::Client::open(cfg.get_config().redis_url.as_str())?; - let mut conn = redis_client.get_multiplexed_async_connection().await?; + let mut conn = + crate::datastructures::get_redis_connection(cfg.get_config().redis_url.as_str()) + .await?; // Rate limit check let max_attempts = cfg.get_config().max_login_attempts; @@ -166,8 +167,7 @@ pub(crate) async fn cmd_authenticate_cookie(matches: &ArgMatches, cfg: Config) - return Ok(false); } - let redis_conn = redis::Client::open(cfg.redis_url.as_str())?; - let mut conn = redis_conn.get_multiplexed_async_connection().await?; + let mut conn = crate::datastructures::get_redis_connection(cfg.redis_url.as_str()).await?; let redis_key = format!("cgit_repo_{repo}"); if !repo.is_empty() && !conn.exists(&redis_key).await? { @@ -289,7 +289,7 @@ pub(crate) async fn cmd_body(matches: &ArgMatches, _cfg: Config) { } pub(crate) async fn cmd_add_user(matches: &ArgMatches, cfg: Config) -> Result<()> { - let re = regex::Regex::new(r"^\w+$").unwrap(); + let re = regex::Regex::new(r"^[a-zA-Z0-9_][a-zA-Z0-9_.\-]*[a-zA-Z0-9_]$").unwrap(); let user = matches .get_one::("user") .map(|s| s.as_str()) @@ -308,7 +308,7 @@ pub(crate) async fn cmd_add_user(matches: &ArgMatches, cfg: Config) -> Result<() if !re.is_match(user) { return Err(anyhow::Error::msg( - "Username must pass regex check\"^\\w+$\"", + "Username must be at least 2 characters, contain only alphanumeric characters, underscores, dots, or hyphens, and must not start or end with a dot or hyphen", )); } @@ -504,8 +504,8 @@ pub(crate) async fn cmd_repo_user_control( return Err(anyhow::Error::msg("Invalid repository or username")); } - let redis_client = redis::Client::open(cfg.redis_url.as_str())?; - let mut redis_conn = redis_client.get_multiplexed_async_connection().await?; + let mut redis_conn = + crate::datastructures::get_redis_connection(cfg.redis_url.as_str()).await?; let mut conn = SqliteConnection::connect(cfg.get_database_location()).await?; diff --git a/src/datastructures.rs b/src/datastructures.rs index 4ca1a00..82c0325 100644 --- a/src/datastructures.rs +++ b/src/datastructures.rs @@ -71,6 +71,13 @@ pub fn rand_str(len: usize) -> String { password } +pub(crate) async fn get_redis_connection( + redis_url: &str, +) -> Result { + let client = redis::Client::open(redis_url)?; + Ok(client.get_multiplexed_async_connection().await?) +} + pub(crate) trait TestSuite { fn generate_test_config() -> Self; } @@ -532,7 +539,9 @@ impl Cookie { pub fn load_from_request(cookies: &str) -> Result> { let mut cookie_self = None; for cookie in cookies.split(';').map(|x| x.trim()) { - let (key, value) = cookie.split_once('=').unwrap(); + let Some((key, value)) = cookie.split_once('=') else { + continue; + }; if key.eq("cgit_auth") { let value = base64::engine::general_purpose::STANDARD .decode(value) diff --git a/src/main.rs b/src/main.rs index 318a94c..3b9e04b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -133,7 +133,7 @@ fn get_arg_matches(arguments: Option>) -> ArgMatches { ) .subcommand( Command::new("database") - .about("Database rated commands") + .about("Database related commands") .subcommand( Command::new("init") .about("Init sqlite database") @@ -154,7 +154,7 @@ fn get_arg_matches(arguments: Option>) -> ArgMatches { ) .subcommand( Command::new("user") - .about("Users rated commands") + .about("User related commands") .subcommand( Command::new("add") .about("Add user to database") @@ -177,7 +177,7 @@ fn get_arg_matches(arguments: Option>) -> ArgMatches { ) .subcommand( Command::new("repo") - .about("Repository ACL rated commands") + .about("Repository ACL related commands") .subcommand( Command::new("add") .about("Add user to repository") diff --git a/src/test.rs b/src/test.rs index 7628921..e71aee4 100644 --- a/src/test.rs +++ b/src/test.rs @@ -55,8 +55,9 @@ mod core { } async fn async_test_redis() -> anyhow::Result<()> { - let redis_conn = redis::Client::open(crate::datastructures::DEFAULT_REDIS_URL)?; - let mut conn = redis_conn.get_multiplexed_async_connection().await?; + let mut conn = + crate::datastructures::get_redis_connection(crate::datastructures::DEFAULT_REDIS_URL) + .await?; let s = rand_str(crate::datastructures::COOKIE_LENGTH); conn.set_ex::<_, _, String>("auth_test", &s, 60).await?; @@ -344,8 +345,9 @@ mod core { } async fn clear_redis_setting() -> anyhow::Result<()> { - let client = redis::Client::open(crate::datastructures::DEFAULT_REDIS_URL)?; - let mut conn = client.get_multiplexed_async_connection().await?; + let mut conn = + crate::datastructures::get_redis_connection(crate::datastructures::DEFAULT_REDIS_URL) + .await?; for key in &[ "cgit_repo_test", -- cgit v1.3.1