diff options
-rw-r--r-- | CHANGELOG.md | 7 | ||||
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/archive.rs | 27 | ||||
-rw-r--r-- | src/errors.rs | 52 | ||||
-rw-r--r-- | src/file_upload.rs | 4 | ||||
-rw-r--r-- | src/main.rs | 16 |
7 files changed, 69 insertions, 39 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 09479d2..f58c1c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate - Change default log level to `Warn` -- Change start messages a bit to be more clear +- Change some messages a bit to be more clear - Add `--print-completions` to print shell completions for various supported shells [#482](https://github.com/svenstaro/miniserve/pull/482) (thanks @rouge8) +- Don't print some messages if not attached to an interactive terminal +- Refuse to start if not attached to interactive terminal and no explicit path is provided + + This is a security consideration as you wouldn't want to run miniserve without an explicit path + as a service. You could end up serving `/` or `/root` in case those working directories are set. ## [0.12.1] - 2021-03-27 - Fix QR code not showing when using both `--random-route` and `--qrcode` [#480](https://github.com/svenstaro/miniserve/pull/480) (thanks @rouge8) @@ -1522,6 +1522,7 @@ dependencies = [ "alphanumeric-sort", "assert_cmd", "assert_fs", + "atty", "bytes 1.0.1", "bytesize", "chrono", @@ -48,6 +48,7 @@ mime = "0.3" httparse = "1" http = "0.2.3" bytes = "1" +atty = "0.2" [dev-dependencies] assert_cmd = "1" diff --git a/src/archive.rs b/src/archive.rs index 894ee3f..4df3a31 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -220,15 +220,18 @@ where let mut buffer = Vec::new(); while !paths_queue.is_empty() { let next = paths_queue.pop().ok_or_else(|| { - ContextualError::CustomError("Could not get path from queue".to_string()) + ContextualError::ArchiveCreationDetailError("Could not get path from queue".to_string()) })?; let current_dir = next.as_path(); let directory_entry_iterator = std::fs::read_dir(current_dir) .map_err(|e| ContextualError::IoError("Could not read directory".to_string(), e))?; - let zip_directory = - Path::new(zip_root_folder_name).join(current_dir.strip_prefix(directory).map_err( - |_| ContextualError::CustomError("Could not append base directory".to_string()), - )?); + let zip_directory = Path::new(zip_root_folder_name).join( + current_dir.strip_prefix(directory).map_err(|_| { + ContextualError::ArchiveCreationDetailError( + "Could not append base directory".to_string(), + ) + })?, + ); for entry in directory_entry_iterator { let entry_path = entry @@ -259,10 +262,14 @@ where zip_writer .start_file(relative_path.to_string_lossy(), options) .map_err(|_| { - ContextualError::CustomError("Could not add file path to ZIP".to_string()) + ContextualError::ArchiveCreationDetailError( + "Could not add file path to ZIP".to_string(), + ) })?; zip_writer.write(buffer.as_ref()).map_err(|_| { - ContextualError::CustomError("Could not write file to ZIP".to_string()) + ContextualError::ArchiveCreationDetailError( + "Could not write file to ZIP".to_string(), + ) })?; buffer.clear(); } else if entry_metadata.is_dir() { @@ -270,7 +277,7 @@ where zip_writer .add_directory(relative_path.to_string_lossy(), options) .map_err(|_| { - ContextualError::CustomError( + ContextualError::ArchiveCreationDetailError( "Could not add directory path to ZIP".to_string(), ) })?; @@ -280,7 +287,9 @@ where } zip_writer.finish().map_err(|_| { - ContextualError::CustomError("Could not finish writing ZIP archive".to_string()) + ContextualError::ArchiveCreationDetailError( + "Could not finish writing ZIP archive".to_string(), + ) })?; Ok(()) } diff --git a/src/errors.rs b/src/errors.rs index 3287fc3..f079657 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -2,65 +2,78 @@ use thiserror::Error; #[derive(Debug, Error)] pub enum ContextualError { - /// Fully customized errors, not inheriting from any error - #[error("{0}")] - CustomError(String), - /// Any kind of IO errors #[error("{0}\ncaused by: {1}")] IoError(String, std::io::Error), - /// MultipartError, which might occur during file upload, when processing the multipart request fails + /// Might occur during file upload, when processing the multipart request fails #[error("Failed to process multipart request\ncaused by: {0}")] MultipartError(actix_multipart::MultipartError), + /// Might occur during file upload + #[error("File already exists, and the overwrite_files option has not been set")] + DuplicateFileError, + /// Any error related to an invalid path (failed to retrieve entry name, unexpected entry type, etc) #[error("Invalid path\ncaused by: {0}")] InvalidPathError(String), - /// This error might occur if the HTTP credential string does not respect the expected format + /// Might occur if the HTTP credential string does not respect the expected format #[error("Invalid format for credentials string. Expected username:password, username:sha256:hash or username:sha512:hash")] InvalidAuthFormat, - /// This error might occure if the hash method is neither sha256 nor sha512 + /// Might occure if the hash method is neither sha256 nor sha512 #[error("{0} is not a valid hashing method. Expected sha256 or sha512")] InvalidHashMethod(String), - /// This error might occur if the HTTP auth hash password is not a valid hex code + /// Might occur if the HTTP auth hash password is not a valid hex code #[error("Invalid format for password hash. Expected hex code")] InvalidPasswordHash, - /// This error might occur if the HTTP auth password exceeds 255 characters + /// Might occur if the HTTP auth password exceeds 255 characters #[error("HTTP password length exceeds 255 characters")] PasswordTooLongError, - /// This error might occur if the user has unsufficient permissions to create an entry in a given directory + /// Might occur if the user has unsufficient permissions to create an entry in a given directory #[error("Insufficient permissions to create file in {0}")] InsufficientPermissionsError(String), - /// Any error related to parsing. + /// Any error related to parsing #[error("Failed to parse {0}\ncaused by: {1}")] ParseError(String, String), - /// This error might occur when the creation of an archive fails + /// Might occur when the creation of an archive fails #[error("An error occured while creating the {0}\ncaused by: {1}")] ArchiveCreationError(String, Box<ContextualError>), - /// This error might occur when the HTTP authentication fails + /// More specific archive creation failure reason + #[error("{0}")] + ArchiveCreationDetailError(String), + + /// Might occur when the HTTP authentication fails #[error("An error occured during HTTP authentication\ncaused by: {0}")] HttpAuthenticationError(Box<ContextualError>), - /// This error might occur when the HTTP credentials are not correct + /// Might occur when the HTTP credentials are not correct #[error("Invalid credentials for HTTP authentication")] InvalidHttpCredentials, - /// This error might occur when an HTTP request is invalid + /// Might occur when an HTTP request is invalid #[error("Invalid HTTP request\ncaused by: {0}")] InvalidHttpRequestError(String), - /// This error might occur when trying to access a page that does not exist + /// Might occur when trying to access a page that does not exist #[error("Route {0} could not be found")] RouteNotFoundError(String), + + /// In case miniserve was invoked without an interactive terminal and without an explicit path + #[error("Refusing to start as no explicit serve path was set and no interactive terminal was attached +Please set an explicit serve path like: `miniserve /my/path`")] + NoExplicitPathAndNoTerminal, + + /// In case miniserve was invoked with --no-symlinks but the serve path is a symlink + #[error("The -P|--no-symlinks option was provided but the serve path '{0}' is a symlink")] + NoSymlinksOptionWithSymlinkServePath(String), } pub fn log_error_chain(description: String) { @@ -68,10 +81,3 @@ pub fn log_error_chain(description: String) { log::error!("{}", cause); } } - -/// This makes creating CustomErrors easier -impl From<String> for ContextualError { - fn from(msg: String) -> ContextualError { - ContextualError::CustomError(msg) - } -} diff --git a/src/file_upload.rs b/src/file_upload.rs index 785d72f..93b7109 100644 --- a/src/file_upload.rs +++ b/src/file_upload.rs @@ -20,9 +20,7 @@ fn save_file( overwrite_files: bool, ) -> Pin<Box<dyn Future<Output = Result<i64, ContextualError>>>> { if !overwrite_files && file_path.exists() { - return Box::pin(future::err(ContextualError::CustomError( - "File already exists, and the overwrite_files option has not been set".to_string(), - ))); + return Box::pin(future::err(ContextualError::DuplicateFileError)); } let mut file = match std::fs::File::create(&file_path) { diff --git a/src/main.rs b/src/main.rs index 5fadb07..7cd4d3e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -220,8 +220,8 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { .is_symlink(); if is_symlink { - return Err(ContextualError::from( - "The no-symlinks option cannot be used with a symlink path".to_string(), + return Err(ContextualError::NoSymlinksOptionWithSymlinkServePath( + miniserve_config.path.to_string_lossy().to_string(), )); } } @@ -266,6 +266,14 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { version = crate_version!() ); if !miniserve_config.path_explicitly_chosen { + // If the path to serve has NOT been explicitly chosen and if this is NOT an interactive + // terminal, we should refuse to start for security reasons. This would be the case when + // running miniserve as a service but forgetting to set the path. This could be pretty + // dangerous if given with an undesired context path (for instance /root or /). + if !atty::is(atty::Stream::Stdout) { + return Err(ContextualError::NoExplicitPathAndNoTerminal); + } + warn!("miniserve has been invoked without an explicit path so it will serve the current directory after a short delay."); warn!( "Invoke with -h|--help to see options or invoke as `miniserve .` to hide this advice." @@ -361,7 +369,9 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { addresses = addresses, ); - println!("\nQuit by pressing CTRL-C"); + if atty::is(atty::Stream::Stdout) { + println!("\nQuit by pressing CTRL-C"); + } srv.await .map_err(|e| ContextualError::IoError("".to_owned(), e)) |