From 56ee97f722292cfc7c9dabd6c9039b1d9cee218f Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 18 Nov 2021 18:14:34 +0000 Subject: [PATCH] add files path traversal tests --- actix-files/src/path_buf.rs | 24 +++++++++++++++++++++++- actix-files/tests/traversal.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 actix-files/tests/traversal.rs diff --git a/actix-files/src/path_buf.rs b/actix-files/src/path_buf.rs index 76f589307..0e0d4f51d 100644 --- a/actix-files/src/path_buf.rs +++ b/actix-files/src/path_buf.rs @@ -8,7 +8,7 @@ use actix_web::{dev::Payload, FromRequest, HttpRequest}; use crate::error::UriSegmentError; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub(crate) struct PathBufWrap(PathBuf); impl FromStr for PathBufWrap { @@ -21,6 +21,8 @@ impl FromStr for PathBufWrap { impl PathBufWrap { /// Parse a path, giving the choice of allowing hidden files to be considered valid segments. + /// + /// Path traversal is guarded by this method. pub fn parse_path(path: &str, hidden_files: bool) -> Result { let mut buf = PathBuf::new(); @@ -115,4 +117,24 @@ mod tests { PathBuf::from_iter(vec!["test", ".tt"]) ); } + + #[test] + fn path_traversal() { + assert_eq!( + PathBufWrap::parse_path("/../README.md", false).unwrap().0, + PathBuf::from_iter(vec!["README.md"]) + ); + + assert_eq!( + PathBufWrap::parse_path("/../README.md", true).unwrap().0, + PathBuf::from_iter(vec!["README.md"]) + ); + + assert_eq!( + PathBufWrap::parse_path("/../../../../../../../../../../etc/passwd", false) + .unwrap() + .0, + PathBuf::from_iter(vec!["etc/passwd"]) + ); + } } diff --git a/actix-files/tests/traversal.rs b/actix-files/tests/traversal.rs new file mode 100644 index 000000000..c890b3fe4 --- /dev/null +++ b/actix-files/tests/traversal.rs @@ -0,0 +1,27 @@ +use actix_files::Files; +use actix_web::{ + http::StatusCode, + test::{self, TestRequest}, + App, +}; + +#[actix_rt::test] +async fn test_directory_traversal_prevention() { + let srv = test::init_service(App::new().service(Files::new("/", "./tests"))).await; + + let req = + TestRequest::with_uri("/../../../../../../../../../../../etc/passwd").to_request(); + let res = test::call_service(&srv, req).await; + assert_eq!(res.status(), StatusCode::NOT_FOUND); + + let req = TestRequest::with_uri( + "/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd", + ) + .to_request(); + let res = test::call_service(&srv, req).await; + assert_eq!(res.status(), StatusCode::NOT_FOUND); + + let req = TestRequest::with_uri("/%00/etc/passwd%00").to_request(); + let res = test::call_service(&srv, req).await; + assert_eq!(res.status(), StatusCode::NOT_FOUND); +}