aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSven-Hendrik Haase <svenstaro@gmail.com>2021-03-28 20:41:07 +0000
committerSven-Hendrik Haase <svenstaro@gmail.com>2021-03-28 20:41:07 +0000
commit906af1587144dd4b3caecacdff5ea834012cffa4 (patch)
tree57ca5b252dcfc76fa79b7a0f41584527b4188cff /src
parentChange start message without arguments to be a bit more clear (diff)
downloadminiserve-906af1587144dd4b3caecacdff5ea834012cffa4.tar.gz
miniserve-906af1587144dd4b3caecacdff5ea834012cffa4.zip
Refuse to start without explicit path if not attached to interactive terminal
Diffstat (limited to 'src')
-rw-r--r--src/archive.rs27
-rw-r--r--src/errors.rs52
-rw-r--r--src/file_upload.rs4
-rw-r--r--src/main.rs16
4 files changed, 61 insertions, 38 deletions
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))