From b66ef02fd37c7c8d14f59332a3977bcff7f2e6ba Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Tue, 17 Mar 2026 07:45:08 +0100 Subject: [PATCH 1/2] Fix non-unicode DB filesystem regression test --- src/webserver/http.rs | 11 ++++---- tests/core/mod.rs | 63 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/webserver/http.rs b/src/webserver/http.rs index 1d0e31e7..ecc06c13 100644 --- a/src/webserver/http.rs +++ b/src/webserver/http.rs @@ -496,12 +496,11 @@ pub async fn main_handler( Serve(path) => { let if_modified_since = IfModifiedSince::parse(&service_request).ok(); let app_state: &web::Data = service_request.app_data().expect("app_state"); - serve_file( - path.as_os_str().to_str().unwrap(), - app_state, - if_modified_since, - ) - .await + let path = path + .as_os_str() + .to_str() + .ok_or_else(|| ErrorBadRequest("requested file path must be valid unicode"))?; + serve_file(path, app_state, if_modified_since).await } } .map(|response| service_request.into_response(response)) diff --git a/tests/core/mod.rs b/tests/core/mod.rs index 10f116ce..c409d62c 100644 --- a/tests/core/mod.rs +++ b/tests/core/mod.rs @@ -4,6 +4,10 @@ use sqlpage::{ AppState, }; use sqlx::Executor as _; +#[cfg(unix)] +use std::ffi::OsString; +#[cfg(unix)] +use std::os::unix::ffi::OsStringExt; use crate::common::{make_app_data_from_config, req_path, req_path_with_app_data, test_config}; @@ -87,6 +91,65 @@ async fn test_routing_with_db_fs() { ); } +#[actix_web::test] +async fn test_non_unicode_static_path_returns_bad_request_with_db_fs() { + let mut config = test_config(); + if !config.database_url.starts_with("sqlite") { + return; + } + config.database_url = + "sqlite://file:test_non_unicode_static_path?mode=memory&cache=shared".to_string(); + + let expected_db_path = { + let decoded = percent_encoding::percent_decode_str("%FF.txt"); + #[cfg(unix)] + { + std::path::PathBuf::from(OsString::from_vec(decoded.collect::>())) + .display() + .to_string() + } + #[cfg(not(unix))] + { + decoded.decode_utf8_lossy().to_string() + } + }; + + use sqlx::Connection as _; + let mut conn = sqlx::AnyConnection::connect(&config.database_url) + .await + .unwrap(); + conn.execute("DROP TABLE IF EXISTS sqlpage_files") + .await + .unwrap(); + conn.execute(sqlpage::filesystem::DbFsQueries::get_create_table_sql( + sqlpage::webserver::database::SupportedDatabase::Sqlite, + )) + .await + .unwrap(); + sqlx::query("INSERT INTO sqlpage_files(path, contents) VALUES (?, ?)") + .bind(expected_db_path) + .bind("file from db fs".as_bytes()) + .execute(&mut conn) + .await + .unwrap(); + + let state = AppState::init(&config).await.unwrap(); + + let app_data = actix_web::web::Data::new(state); + let req = test::TestRequest::get() + .uri("/%FF.txt") + .app_data(app_data) + .to_srv_request(); + + let err = sqlpage::webserver::http::main_handler(req) + .await + .expect_err("non-unicode path should not panic and must return bad request"); + assert_eq!( + err.as_response_error().status_code(), + StatusCode::BAD_REQUEST + ); +} + #[actix_web::test] async fn test_routing_with_prefix() { let mut config = test_config(); From 90848d008553acbc1f5a8764d96d33220ba65ab8 Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Tue, 17 Mar 2026 11:59:47 +0100 Subject: [PATCH 2/2] Fix non-unicode DB fs routing test --- tests/core/mod.rs | 46 ++++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/tests/core/mod.rs b/tests/core/mod.rs index c409d62c..d765d87e 100644 --- a/tests/core/mod.rs +++ b/tests/core/mod.rs @@ -4,10 +4,6 @@ use sqlpage::{ AppState, }; use sqlx::Executor as _; -#[cfg(unix)] -use std::ffi::OsString; -#[cfg(unix)] -use std::os::unix::ffi::OsStringExt; use crate::common::{make_app_data_from_config, req_path, req_path_with_app_data, test_config}; @@ -91,6 +87,7 @@ async fn test_routing_with_db_fs() { ); } +#[cfg(unix)] #[actix_web::test] async fn test_non_unicode_static_path_returns_bad_request_with_db_fs() { let mut config = test_config(); @@ -100,41 +97,30 @@ async fn test_non_unicode_static_path_returns_bad_request_with_db_fs() { config.database_url = "sqlite://file:test_non_unicode_static_path?mode=memory&cache=shared".to_string(); - let expected_db_path = { - let decoded = percent_encoding::percent_decode_str("%FF.txt"); - #[cfg(unix)] - { - std::path::PathBuf::from(OsString::from_vec(decoded.collect::>())) - .display() - .to_string() - } - #[cfg(not(unix))] - { - decoded.decode_utf8_lossy().to_string() - } - }; + let state = AppState::init(&config).await.unwrap(); + let expected_db_path = "\u{FFFD}.txt"; + let mut conn = state.db.connection.acquire().await.unwrap(); - use sqlx::Connection as _; - let mut conn = sqlx::AnyConnection::connect(&config.database_url) - .await - .unwrap(); - conn.execute("DROP TABLE IF EXISTS sqlpage_files") + (&mut *conn) + .execute(sqlpage::filesystem::DbFsQueries::get_create_table_sql( + sqlpage::webserver::database::SupportedDatabase::Sqlite, + )) .await .unwrap(); - conn.execute(sqlpage::filesystem::DbFsQueries::get_create_table_sql( - sqlpage::webserver::database::SupportedDatabase::Sqlite, - )) - .await - .unwrap(); - sqlx::query("INSERT INTO sqlpage_files(path, contents) VALUES (?, ?)") + let insert_sql = format!( + "INSERT INTO sqlpage_files(path, contents) VALUES ({}, {})", + make_placeholder(state.db.info.kind, 1), + make_placeholder(state.db.info.kind, 2) + ); + sqlx::query(&insert_sql) .bind(expected_db_path) .bind("file from db fs".as_bytes()) - .execute(&mut conn) + .execute(&mut *conn) .await .unwrap(); + drop(conn); let state = AppState::init(&config).await.unwrap(); - let app_data = actix_web::web::Data::new(state); let req = test::TestRequest::get() .uri("/%FF.txt")