Fix asset serving: preserve original case in path (don't lowercase on GET)
All checks were successful
Deploy to Server / deploy (push) Successful in 1m39s

This commit is contained in:
Peter Meier
2026-03-15 14:47:30 +01:00
parent 40c83afeb3
commit e2de403702

View File

@@ -112,6 +112,22 @@ fn parse_path(path: &str) -> Result<(Option<String>, String), ApiError> {
} }
} }
/// Parse a path for serving only preserves original case, only validates for path traversal.
fn parse_serve_path(path: &str) -> Result<(Option<String>, String), ApiError> {
let path = path.trim_start_matches('/');
if path.contains("..") || path.contains('\\') {
return Err(ApiError::BadRequest("Invalid asset path".into()));
}
let mut parts = path.splitn(2, '/');
match (parts.next(), parts.next()) {
(Some(a), None) if !a.is_empty() => Ok((None, a.to_string())),
(Some(folder), Some(filename)) if !folder.is_empty() && !filename.is_empty() => {
Ok((Some(folder.to_string()), filename.to_string()))
}
_ => Err(ApiError::BadRequest("Invalid asset path".into())),
}
}
/// Read all image files from a directory, tagging them with the folder name. /// Read all image files from a directory, tagging them with the folder name.
async fn read_images( async fn read_images(
dir: &std::path::Path, dir: &std::path::Path,
@@ -521,7 +537,7 @@ pub async fn get_asset(
) -> Result<axum::response::Response, ApiError> { ) -> Result<axum::response::Response, ApiError> {
let env = state.effective_environment_from_param(env_param.environment.as_deref().map(|s| s.trim()).filter(|s| !s.is_empty())); let env = state.effective_environment_from_param(env_param.environment.as_deref().map(|s| s.trim()).filter(|s| !s.is_empty()));
let assets_dir = state.assets_dir_for(&env); let assets_dir = state.assets_dir_for(&env);
let (folder, filename) = parse_path(&path)?; let (folder, filename) = parse_serve_path(&path)?;
let file_path = match &folder { let file_path = match &folder {
Some(f) => assets_dir.join(f).join(&filename), Some(f) => assets_dir.join(f).join(&filename),
None => assets_dir.join(&filename), None => assets_dir.join(&filename),