aboutsummaryrefslogtreecommitdiffstats
path: root/tests/create_directories.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--tests/create_directories.rs163
1 files changed, 163 insertions, 0 deletions
diff --git a/tests/create_directories.rs b/tests/create_directories.rs
new file mode 100644
index 0000000..fa8f4b8
--- /dev/null
+++ b/tests/create_directories.rs
@@ -0,0 +1,163 @@
+mod fixtures;
+
+use fixtures::{server, Error, TestServer, DIRECTORIES};
+use reqwest::blocking::{multipart, Client};
+use rstest::rstest;
+use select::document::Document;
+use select::predicate::{Attr, Text};
+#[cfg(unix)]
+use std::os::unix::fs::symlink as symlink_dir;
+#[cfg(windows)]
+use std::os::windows::fs::symlink_dir;
+
+/// This should work because the flags for uploading files and creating directories
+/// are set, and the directory name and path are valid.
+#[rstest]
+fn creating_directories_works(
+ #[with(&["--upload-files", "--mkdir"])] server: TestServer,
+) -> Result<(), Error> {
+ let test_directory_name = "hello";
+
+ // Before creating, check whether the directory does not yet exist.
+ let body = reqwest::blocking::get(server.url())?.error_for_status()?;
+ let parsed = Document::from_read(body)?;
+ assert!(parsed.find(Text).all(|x| x.text() != test_directory_name));
+
+ // Perform the actual creation.
+ let create_action = parsed
+ .find(Attr("id", "mkdir"))
+ .next()
+ .expect("Couldn't find element with id=mkdir")
+ .attr("action")
+ .expect("Directory form doesn't have action attribute");
+ let form = multipart::Form::new();
+ let part = multipart::Part::text(test_directory_name);
+ let form = form.part("mkdir", part);
+
+ let client = Client::new();
+ client
+ .post(server.url().join(create_action)?)
+ .multipart(form)
+ .send()?
+ .error_for_status()?;
+
+ // After creating, check whether the directory is now getting listed.
+ let body = reqwest::blocking::get(server.url())?;
+ let parsed = Document::from_read(body)?;
+ assert!(parsed
+ .find(Text)
+ .any(|x| x.text() == test_directory_name.to_owned() + "/"));
+
+ Ok(())
+}
+
+/// This should fail because the server does not allow for creating directories
+/// as the flags for uploading files and creating directories are not set.
+/// The directory name and path are valid.
+#[rstest]
+fn creating_directories_is_prevented(server: TestServer) -> Result<(), Error> {
+ let test_directory_name = "hello";
+
+ // Before creating, check whether the directory does not yet exist.
+ let body = reqwest::blocking::get(server.url())?.error_for_status()?;
+ let parsed = Document::from_read(body)?;
+ assert!(parsed.find(Text).all(|x| x.text() != test_directory_name));
+
+ // Ensure the directory creation form is not present
+ assert!(parsed.find(Attr("id", "mkdir")).next().is_none());
+
+ // Then try to create anyway
+ let form = multipart::Form::new();
+ let part = multipart::Part::text(test_directory_name);
+ let form = form.part("mkdir", part);
+
+ let client = Client::new();
+ // This should fail
+ assert!(client
+ .post(server.url().join("/upload?path=/")?)
+ .multipart(form)
+ .send()?
+ .error_for_status()
+ .is_err());
+
+ // After creating, check whether the directory is now getting listed (shouldn't).
+ let body = reqwest::blocking::get(server.url())?;
+ let parsed = Document::from_read(body)?;
+ assert!(parsed
+ .find(Text)
+ .all(|x| x.text() != test_directory_name.to_owned() + "/"));
+
+ Ok(())
+}
+
+/// This should fail because directory creation through symlinks should not be possible
+/// when the the no symlinks flag is set.
+#[rstest]
+fn creating_directories_through_symlinks_is_prevented(
+ #[with(&["--upload-files", "--mkdir", "--no-symlinks"])] server: TestServer,
+) -> Result<(), Error> {
+ // Make symlinks
+ let symlink_directory_str = "symlink";
+ let symlink_directory = server.path().join(symlink_directory_str);
+ let symlinked_direcotry = server.path().join(DIRECTORIES[0]);
+ symlink_dir(symlinked_direcotry, symlink_directory).unwrap();
+
+ // Before attempting to create, ensure the symlink does not exist.
+ let body = reqwest::blocking::get(server.url())?.error_for_status()?;
+ let parsed = Document::from_read(body)?;
+ assert!(parsed.find(Text).all(|x| x.text() != symlink_directory_str));
+
+ // Attempt to perform directory creation.
+ let form = multipart::Form::new();
+ let part = multipart::Part::text(symlink_directory_str);
+ let form = form.part("mkdir", part);
+
+ // This should fail
+ assert!(Client::new()
+ .post(
+ server
+ .url()
+ .join(format!("/upload?path=/{}", symlink_directory_str).as_str())?
+ )
+ .multipart(form)
+ .send()?
+ .error_for_status()
+ .is_err());
+
+ Ok(())
+}
+
+/// Test for path traversal vulnerability (CWE-22) in both path parameter of query string and in
+/// mkdir name (Content-Disposition)
+///
+/// see: https://github.com/svenstaro/miniserve/issues/518
+#[rstest]
+#[case("foo", "bar", "foo/bar")] // Not CWE-22, but `foo` isn't a directory
+#[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 prevent_path_transversal_attacks(
+ #[with(&["--upload-files", "--mkdir"])] server: TestServer,
+ #[case] path: &str,
+ #[case] dir_name: &'static str,
+ #[case] expected: &str,
+) -> Result<(), Error> {
+ let expected_path = server.path().join(expected);
+ assert!(!expected_path.exists());
+
+ let form = multipart::Form::new();
+ let part = multipart::Part::text(dir_name);
+ let form = form.part("mkdir", part);
+
+ // This should fail
+ assert!(Client::new()
+ .post(server.url().join(&format!("/upload/path={}", path))?)
+ .multipart(form)
+ .send()?
+ .error_for_status()
+ .is_err());
+
+ Ok(())
+}