diff --git a/resources/static-folder/Cargo.toml b/resources/static-folder/Cargo.toml index d42d2a032..6cd93225a 100644 --- a/resources/static-folder/Cargo.toml +++ b/resources/static-folder/Cargo.toml @@ -9,8 +9,10 @@ keywords = ["shuttle-service", "static-folder"] [dependencies] async-trait = "0.1.56" +fs_extra = "1.3.0" shuttle-service = { path = "../../service", version = "0.11.0", default-features = false } tokio = { version = "1.19.2", features = ["rt"] } +tracing = "0.1.37" [dev-dependencies] tempfile = "3.3.0" diff --git a/resources/static-folder/src/lib.rs b/resources/static-folder/src/lib.rs index 8b08e7e72..77982eb6c 100644 --- a/resources/static-folder/src/lib.rs +++ b/resources/static-folder/src/lib.rs @@ -1,13 +1,12 @@ use async_trait::async_trait; +use fs_extra::dir::{copy, CopyOptions}; use shuttle_service::{ error::{CustomError, Error as ShuttleError}, Factory, ResourceBuilder, }; -use std::{ - fs::rename, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use tokio::runtime::Runtime; +use tracing::{error, trace}; pub struct StaticFolder<'a> { /// The folder to reach at runtime. Defaults to `static` @@ -17,6 +16,7 @@ pub struct StaticFolder<'a> { pub enum Error { AbsolutePath, TransversedUp, + Copy(fs_extra::error::Error), } impl<'a> StaticFolder<'a> { @@ -40,34 +40,59 @@ impl<'a> ResourceBuilder for StaticFolder<'a> { ) -> Result { let folder = Path::new(self.folder); + trace!(?folder, "building static folder"); + // Prevent users from users from reading anything outside of their crate's build folder if folder.is_absolute() { + error!("the static folder cannot be an absolute path"); return Err(Error::AbsolutePath)?; } let input_dir = factory.get_build_path()?.join(self.folder); + trace!(input_directory = ?input_dir, "got input directory"); + match input_dir.canonicalize() { Ok(canonical_path) if canonical_path != input_dir => return Err(Error::TransversedUp)?, Ok(_) => { // The path did not change to outside the crate's build folder } - Err(err) => return Err(err)?, + Err(err) => { + error!( + error = &err as &dyn std::error::Error, + "failed to get static folder" + ); + return Err(err)?; + } } - let output_dir = factory.get_storage_path()?.join(self.folder); + let output_dir = factory.get_storage_path()?; - rename(input_dir, output_dir.clone())?; + trace!(output_directory = ?output_dir, "got output directory"); - Ok(output_dir) + let copy_options = CopyOptions::new().overwrite(true); + match copy(&input_dir, &output_dir, ©_options) { + Ok(_) => Ok(output_dir.join(self.folder)), + Err(error) => { + error!( + error = &error as &dyn std::error::Error, + "failed to copy static folder" + ); + + Err(Error::Copy(error))? + } + } } } impl From for shuttle_service::Error { fn from(error: Error) -> Self { let msg = match error { - Error::AbsolutePath => "Cannot use an absolute path for a static folder", - Error::TransversedUp => "Cannot transverse out of crate for a static folder", + Error::AbsolutePath => "Cannot use an absolute path for a static folder".to_string(), + Error::TransversedUp => { + "Cannot transverse out of crate for a static folder".to_string() + } + Error::Copy(error) => format!("Cannot copy static folder: {}", error), }; ShuttleError::Custom(CustomError::msg(msg))