diff options
author | Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com> | 2021-03-23 19:42:28 +0000 |
---|---|---|
committer | Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com> | 2021-09-01 11:02:19 +0000 |
commit | 699e17c7de0b40c1e5f7e4171683710378e4af58 (patch) | |
tree | b68d53e1121091a3b0d6b2247a2d6a68d422f91e /tests/upload_files.rs | |
parent | Add CHANGELOG entry for printing QR codes on terminal (diff) | |
download | miniserve-699e17c7de0b40c1e5f7e4171683710378e4af58.tar.gz miniserve-699e17c7de0b40c1e5f7e4171683710378e4af58.zip |
file_upload.rs: sanitize path input
Signed-off-by: Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>
Diffstat (limited to '')
-rw-r--r-- | tests/upload_files.rs | 81 |
1 files changed, 80 insertions, 1 deletions
diff --git a/tests/upload_files.rs b/tests/upload_files.rs index 698eb46..26750d9 100644 --- a/tests/upload_files.rs +++ b/tests/upload_files.rs @@ -1,6 +1,7 @@ mod fixtures; -use fixtures::{server, Error, TestServer}; +use assert_fs::fixture::TempDir; +use fixtures::{server, server_no_stderr, tmpdir, Error, TestServer}; use reqwest::blocking::{multipart, Client}; use rstest::rstest; use select::document::Document; @@ -78,3 +79,81 @@ fn uploading_files_is_prevented(server: TestServer) -> Result<(), Error> { Ok(()) } + +#[rstest] +#[case("foo", "bar", "foo/bar")] +#[case("/../foo", "bar", "foo/bar")] +#[case("/foo", "/../bar", "foo/bar")] +#[case("C:/foo", "C:/bar", if cfg!(windows) { "foo/bar" } else { "C:/foo/C:/bar" })] +#[case(r"C:\foo", r"C:\bar", if cfg!(windows) { "foo/bar" } else { r"C:\foo/C:\bar" })] +#[case(r"\foo", r"\..\bar", if cfg!(windows) { "foo/bar" } else { r"\foo/\..\bar" })] +fn path_traversal( + #[with(&["-u"])] server: TestServer, + #[case] path: &str, + #[case] filename: &'static str, + #[case] expected: &str, +) -> Result<(), Error> { + // create test directories + use std::fs::create_dir_all; + create_dir_all(server.path().join("foo")).unwrap(); + if !cfg!(windows) { + for dir in &["C:/foo/C:", r"C:\foo", r"\foo"] { + create_dir_all(server.path().join(dir)).expect(&format!("failed to create: {:?}", dir)); + } + } + + let expected_path = server.path().join(expected); + assert!(!expected_path.exists()); + + // Perform the actual upload. + let part = multipart::Part::text("this should be uploaded") + .file_name(filename) + .mime_str("text/plain")?; + let form = multipart::Form::new().part("file_to_upload", part); + + Client::new() + .post(server.url().join(&format!("/upload?path={}", path))?) + .multipart(form) + .send()? + .error_for_status()?; + + // Make sure that the file was uploaded to the expected path + assert!(expected_path.exists()); + + Ok(()) +} + +#[rstest] +#[case(server(&["-u"]), true)] +#[case(server_no_stderr(&["-u", "--no-symlinks"]), false)] +fn symlink(#[case] server: TestServer, #[case] ok: bool, tmpdir: TempDir) -> Result<(), Error> { + #[cfg(unix)] + use std::os::unix::fs::symlink as symlink_dir; + #[cfg(windows)] + use std::os::windows::fs::symlink_dir; + + // create symlink directory "foo" to point outside the root + let (dir, filename) = ("foo", "bar"); + symlink_dir(tmpdir.path(), server.path().join(dir)).unwrap(); + + let full_path = server.path().join(dir).join(filename); + assert!(!full_path.exists()); + + // try to upload + let part = multipart::Part::text("this should be uploaded") + .file_name(filename) + .mime_str("text/plain")?; + let form = multipart::Form::new().part("file_to_upload", part); + + let status = Client::new() + .post(server.url().join(&format!("/upload?path={}", dir))?) + .multipart(form) + .send()? + .error_for_status(); + + // Make sure upload behave as expected + assert_eq!(status.is_ok(), ok); + assert_eq!(full_path.exists(), ok); + + Ok(()) +} |