diff options
author | Sven-Hendrik Haase <svenstaro@gmail.com> | 2025-03-09 06:55:03 +0000 |
---|---|---|
committer | Sven-Hendrik Haase <svenstaro@gmail.com> | 2025-03-09 06:55:03 +0000 |
commit | 3351805fffadcb9747f772ca9ea05e0d6d13619c (patch) | |
tree | d7c666d625d709354fcf03c31deaaf69d7e90fdb | |
parent | Fix lints on Windows (diff) | |
download | miniserve-3351805fffadcb9747f772ca9ea05e0d6d13619c.tar.gz miniserve-3351805fffadcb9747f772ca9ea05e0d6d13619c.zip |
Fix dir size calculation for percent-encoded paths with spaces
-rw-r--r-- | src/listing.rs | 2 | ||||
-rw-r--r-- | src/main.rs | 13 | ||||
-rw-r--r-- | src/renderer.rs | 2 | ||||
-rw-r--r-- | tests/api.rs | 12 | ||||
-rw-r--r-- | tests/fixtures/mod.rs | 4 |
5 files changed, 23 insertions, 10 deletions
diff --git a/src/listing.rs b/src/listing.rs index 6e50ba1..c98670b 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -23,7 +23,7 @@ use self::percent_encode_sets::COMPONENT; /// "percent-encode sets" as defined by WHATWG specs: /// https://url.spec.whatwg.org/#percent-encoded-bytes -mod percent_encode_sets { +pub mod percent_encode_sets { use percent_encoding::{AsciiSet, CONTROLS}; pub const QUERY: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'#').add(b'<').add(b'>'); pub const PATH: &AsciiSet = &QUERY.add(b'?').add(b'`').add(b'{').add(b'}'); diff --git a/src/main.rs b/src/main.rs index adda3f7..a7166ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ use dav_server::{ }; use fast_qr::QRBuilder; use log::{error, info, warn}; +use percent_encoding::percent_decode_str; use serde::Deserialize; mod archive; @@ -455,10 +456,14 @@ async fn api( config: web::Data<MiniserveConfig>, ) -> Result<impl Responder, RuntimeError> { match command.into_inner() { - ApiCommand::DirSize(dir) => { - // Convert the relative dir to an absolute path on the system - let sanitized_path = - file_utils::sanitize_path(&dir, true).expect("Expected a path to directory"); + ApiCommand::DirSize(path) => { + // The dir argument might be percent-encoded so let's decode it just in case. + let decoded_path = percent_decode_str(&path) + .decode_utf8() + .map_err(|e| RuntimeError::ParseError(path.clone(), e.to_string()))?; + // Convert the relative dir to an absolute path on the system. + let sanitized_path = file_utils::sanitize_path(&*decoded_path, true) + .expect("Expected a path to directory"); let full_path = config .path .canonicalize() diff --git a/src/renderer.rs b/src/renderer.rs index d6b01c8..6a8b808 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -684,7 +684,7 @@ fn page_header( body: JSON.stringify({ DirSize: dir }) - }).then(resp => resp.text()) + }).then(resp => resp.ok ? resp.text() : "~") } // Initialize shimmer effects for .size-cell elements in .entry-type-directory rows diff --git a/tests/api.rs b/tests/api.rs index 32d6cef..fe313b0 100644 --- a/tests/api.rs +++ b/tests/api.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode}; use reqwest::{StatusCode, blocking::Client}; use rstest::rstest; @@ -7,10 +8,17 @@ mod fixtures; use crate::fixtures::{DIRECTORIES, Error, TestServer, server}; +/// Test that we can get dir size for plain paths as well as percent-encoded paths #[rstest] -fn api_dir_size(server: TestServer) -> Result<(), Error> { +#[case(DIRECTORIES[0].to_string())] +#[case(DIRECTORIES[1].to_string())] +#[case(DIRECTORIES[2].to_string())] +#[case(utf8_percent_encode(DIRECTORIES[0], NON_ALPHANUMERIC).to_string())] +#[case(utf8_percent_encode(DIRECTORIES[1], NON_ALPHANUMERIC).to_string())] +#[case(utf8_percent_encode(DIRECTORIES[2], NON_ALPHANUMERIC).to_string())] +fn api_dir_size(#[case] dir: String, server: TestServer) -> Result<(), Error> { let mut command = HashMap::new(); - command.insert("DirSize", DIRECTORIES[0]); + command.insert("DirSize", dir); let resp = Client::new() .post(server.url().join(&format!("__miniserve_internal/api"))?) diff --git a/tests/fixtures/mod.rs b/tests/fixtures/mod.rs index f2869b7..5b331e9 100644 --- a/tests/fixtures/mod.rs +++ b/tests/fixtures/mod.rs @@ -34,10 +34,10 @@ pub static FILES: &[&str] = &[ pub static HIDDEN_FILES: &[&str] = &[".hidden_file1", ".hidden_file2"]; /// Directory names for testing purpose -pub static DIRECTORIES: &[&str] = &["dira/", "dirb/", "dirc/"]; +pub static DIRECTORIES: &[&str] = &["dira/", "dirb/", "dir space/"]; /// Hidden directories for testing purpose -pub static HIDDEN_DIRECTORIES: &[&str] = &[".hidden_dir1/", ".hidden_dir2/"]; +pub static HIDDEN_DIRECTORIES: &[&str] = &[".hidden_dir1/", ".hidden space dir/"]; /// Name of a deeply nested file pub static DEEPLY_NESTED_FILE: &str = "very/deeply/nested/test.rs"; |