From 38aff602b034329a58872503cf40d1bfd7b48130 Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Thu, 19 Mar 2026 10:40:48 +0100 Subject: [PATCH] Skip non-unicode static path regression test on Windows --- src/webserver/http.rs | 11 ++++----- tests/core/mod.rs | 56 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 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..53e89ae9 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,58 @@ 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(); + 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 decoded = percent_encoding::percent_decode_str("%FF.txt"); + let expected_db_path = + std::path::PathBuf::from(OsString::from_vec(decoded.collect::>())) + .display() + .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();