From 844d8148257f0aac165177a8f55ec0d15feef89e Mon Sep 17 00:00:00 2001 From: Denis Cornehl Date: Mon, 13 Apr 2026 17:06:22 +0200 Subject: [PATCH 1/2] block more encoded path traversals --- crates/bin/docs_rs_web/src/middleware/security.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/bin/docs_rs_web/src/middleware/security.rs b/crates/bin/docs_rs_web/src/middleware/security.rs index 13d1284aa..12b395c99 100644 --- a/crates/bin/docs_rs_web/src/middleware/security.rs +++ b/crates/bin/docs_rs_web/src/middleware/security.rs @@ -41,7 +41,11 @@ fn validate_path(initial_path: &str) -> Result<()> { } fn validate_decoded_path(path: &str) -> Result<()> { - if path.contains("/../") || path.ends_with("/..") { + if path.contains("/../") + || path.ends_with("/..") + || path.contains("//\\../") + || path.contains("\\..\\") + { bail!("path traversal attempt"); } @@ -61,6 +65,7 @@ mod tests { testing::{AxumResponseTestExt as _, AxumRouterTestExt as _}, }; use axum::{Router, middleware, routing::get}; + use test_case::test_case; use tower::ServiceBuilder; @@ -78,6 +83,14 @@ mod tests { #[test_case( "/crate/mika-cli/latest/source/..%25c1%259c..%25c1%259c..%25c1%259c..%25c1%259c..%25c1%259c..%25c1%259c..%25c1%259c..%25c1%259c/etc/passwd" )] + #[test_case( + "/crate/aether/latest/source/compiler/node_modules/@richardanaya//%5c../%5c../%5c../%5c../%5c../%5c../%5c../etc/passwd"; + "with backslash" + )] + #[test_case( + "/casual_logger/0.6.4/%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5cwindows/win.ini"; + "double backslash" + )] async fn test_invalid_path(path: &str) -> Result<()> { let app = Router::new() .route("/{*inner}", get(|| async { StatusCode::OK })) From a4fdf90d3080e448052965c02b8115215bf5af68 Mon Sep 17 00:00:00 2001 From: Denis Cornehl Date: Tue, 14 Apr 2026 17:25:17 +0200 Subject: [PATCH 2/2] also block `\\..` suffix in urls --- crates/bin/docs_rs_web/src/middleware/security.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/bin/docs_rs_web/src/middleware/security.rs b/crates/bin/docs_rs_web/src/middleware/security.rs index 12b395c99..d2e76484c 100644 --- a/crates/bin/docs_rs_web/src/middleware/security.rs +++ b/crates/bin/docs_rs_web/src/middleware/security.rs @@ -45,6 +45,7 @@ fn validate_decoded_path(path: &str) -> Result<()> { || path.ends_with("/..") || path.contains("//\\../") || path.contains("\\..\\") + || path.ends_with("\\..") { bail!("path traversal attempt"); } @@ -91,6 +92,10 @@ mod tests { "/casual_logger/0.6.4/%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5cwindows/win.ini"; "double backslash" )] + #[test_case( + "/casual_logger/0.6.4/%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e"; + "ends with backslash dot dot" + )] async fn test_invalid_path(path: &str) -> Result<()> { let app = Router::new() .route("/{*inner}", get(|| async { StatusCode::OK }))