aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/args.rs7
-rw-r--r--src/config.rs4
-rw-r--r--src/listing.rs8
-rw-r--r--src/renderer.rs2
-rw-r--r--tests/archive.rs28
-rw-r--r--tests/serve_request.rs35
6 files changed, 83 insertions, 1 deletions
diff --git a/src/args.rs b/src/args.rs
index 7e3668b..3fb0ede 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -293,6 +293,13 @@ pub struct CliArgs {
/// Enable README.md rendering in directories
#[arg(long, env = "MINISERVE_README")]
pub readme: bool,
+
+ /// Disable indexing
+ ///
+ /// This will prevent directory listings from being generated
+ /// and return an error instead.
+ #[arg(short = 'I', long, env = "MINISERVE_DISABLE_INDEXING")]
+ pub disable_indexing: bool,
}
/// Checks whether an interface is valid, i.e. it can be parsed into an IP address
diff --git a/src/config.rs b/src/config.rs
index 43414b2..3643502 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -146,6 +146,9 @@ pub struct MiniserveConfig {
/// If enabled, render the readme from the current directory
pub readme: bool,
+ /// If enabled, indexing is disabled.
+ pub disable_indexing: bool,
+
/// If set, use provided rustls config for TLS
#[cfg(feature = "tls")]
pub tls_rustls_config: Option<rustls::ServerConfig>,
@@ -311,6 +314,7 @@ impl MiniserveConfig {
hide_theme_selector: args.hide_theme_selector,
show_wget_footer: args.show_wget_footer,
readme: args.readme,
+ disable_indexing: args.disable_indexing,
tls_rustls_config: tls_rustls_server_config,
compress_response: args.compress_response,
})
diff --git a/src/listing.rs b/src/listing.rs
index 855abef..64cdaca 100644
--- a/src/listing.rs
+++ b/src/listing.rs
@@ -161,6 +161,14 @@ pub fn directory_listing(
let current_user: Option<&CurrentUser> = extensions.get::<CurrentUser>();
let conf = req.app_data::<crate::MiniserveConfig>().unwrap();
+ if conf.disable_indexing {
+ return Ok(ServiceResponse::new(
+ req.clone(),
+ HttpResponse::NotFound()
+ .content_type(mime::TEXT_PLAIN_UTF_8)
+ .body("File not found."),
+ ));
+ }
let serve_path = req.path();
let base = Path::new(serve_path);
diff --git a/src/renderer.rs b/src/renderer.rs
index 3b62704..b691bca 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -687,7 +687,7 @@ pub fn render_error(
p { (error) }
}
// WARN don't expose random route!
- @if conf.route_prefix.is_empty() {
+ @if conf.route_prefix.is_empty() && !conf.disable_indexing {
div.error-nav {
a.error-back href=(return_address) {
"Go back to file listing"
diff --git a/tests/archive.rs b/tests/archive.rs
index b8def22..e6d0263 100644
--- a/tests/archive.rs
+++ b/tests/archive.rs
@@ -56,3 +56,31 @@ fn test_tar_archives(#[with(&["-g"])] server: TestServer) -> Result<(), Error> {
Ok(())
}
+
+#[rstest]
+#[case(server(&["--disable-indexing", "--enable-tar-gz", "--enable-tar", "--enable-zip"]))]
+fn archives_are_disabled_when_indexing_disabled(#[case] server: TestServer) -> Result<(), Error> {
+ // Ensure the links to the archives are not present
+ let body = reqwest::blocking::get(server.url())?;
+ let parsed = Document::from_read(body)?;
+ assert!(parsed
+ .find(Text)
+ .all(|x| x.text() != "Download .tar.gz" && x.text() != "Download .tar"));
+
+ // Try to download anyway, ensure it's forbidden
+ // We assert for not found to make sure we aren't leaking information about directories that do exist.
+ assert_eq!(
+ reqwest::blocking::get(server.url().join("?download=tar_gz")?)?.status(),
+ StatusCode::NOT_FOUND
+ );
+ assert_eq!(
+ reqwest::blocking::get(server.url().join("?download=tar")?)?.status(),
+ StatusCode::NOT_FOUND
+ );
+ assert_eq!(
+ reqwest::blocking::get(server.url().join("?download=zip")?)?.status(),
+ StatusCode::NOT_FOUND
+ );
+
+ Ok(())
+}
diff --git a/tests/serve_request.rs b/tests/serve_request.rs
index ac4360e..b7359c3 100644
--- a/tests/serve_request.rs
+++ b/tests/serve_request.rs
@@ -321,3 +321,38 @@ fn serves_requests_static_file_check(
Ok(())
}
+
+#[rstest]
+#[case(server(&["--disable-indexing"]))]
+fn serves_no_directory_if_indexing_disabled(#[case] server: TestServer) -> Result<(), Error> {
+ let body = reqwest::blocking::get(server.url())?;
+ assert_eq!(body.status(), StatusCode::NOT_FOUND);
+ let parsed = Document::from_read(body)?;
+
+ assert!(parsed
+ .find(|x: &Node| x.text() == FILES[0])
+ .next()
+ .is_none());
+ assert!(parsed
+ .find(|x: &Node| x.text() == DIRECTORIES[0])
+ .next()
+ .is_none());
+ assert!(parsed
+ .find(|x: &Node| x.text() == "404 Not Found")
+ .next()
+ .is_some());
+ assert!(parsed
+ .find(|x: &Node| x.text() == "File not found.")
+ .next()
+ .is_some());
+
+ Ok(())
+}
+
+#[rstest]
+#[case(server(&["--disable-indexing"]))]
+fn serves_file_requests_when_indexing_disabled(#[case] server: TestServer) -> Result<(), Error> {
+ reqwest::blocking::get(format!("{}{}", server.url(), FILES[0]))?.error_for_status()?;
+
+ Ok(())
+}