diff --git a/vnt-web/src/service_http.rs b/vnt-web/src/service_http.rs index 21a546e..2c23a08 100644 --- a/vnt-web/src/service_http.rs +++ b/vnt-web/src/service_http.rs @@ -444,12 +444,28 @@ fn build_headers_for_path(path: &str) -> HeaderMap { ); headers } +/// 将请求路径安全地映射到 static 目录内。 +/// 逐组件校验,拒绝 `..`、根路径、盘符等任何可能逃逸出 static 的路径。 +fn resolve_static_path(path: &str) -> Option { + let mut local_path = PathBuf::from("static"); + for component in Path::new(path).components() { + match component { + std::path::Component::Normal(part) => local_path.push(part), + std::path::Component::CurDir => {} + _ => return None, + } + } + Some(local_path) +} + async fn static_handler(uri: Uri) -> impl IntoResponse { let path = uri.path().trim_start_matches('/'); let path = if path.is_empty() { "index.html" } else { path }; // 先尝试从本地文件读取 - let local_path = Path::new("static").join(path); + let Some(local_path) = resolve_static_path(path) else { + return (StatusCode::NOT_FOUND, "404 Not Found").into_response(); + }; if local_path.is_file() && let Ok(content) = tokio::fs::read(&local_path).await { @@ -1124,3 +1140,38 @@ async fn get_routes(State(state): State) -> Json