Repository: epi052/feroxbuster (language: rust)

Issue:
[FEATURE REQUEST] Optionally ignore directory listings
**Is your feature request related to a problem? Please describe.**
Feroxbuster always stops exploring a directory when a directory listing is detected. I think that this behavior is desired in the common case, but may fail to detect files in special cases, i.e., those ignored by the directory listing (see https://serverfault.com/questions/345542/force-apache-directory-listing-even-if-directoryindex-files-are-present). I believe that a tool like feroxbuster should cover such cases - detecting *hidden* files is it's primary purpose after all.

**Describe the solution you'd like**
A flag or config option to force feroxbuster to ignore directory listings, i.e., recurse into directories which have a (potentially empty) directory listing.

**Additional context**
This issue occured during the latest stream from 0xTib3rius, specifically when he tackled THM Box *Valley*.

I'm happy to provide a PR if the maintainers agree with this request.


Discussion / hints:
Adding my support to this. I was going to make my own request but this pretty much covers everything I talked about on stream. I think the initial reason for skipping over directory listings is sound, but as has been pointed out, you can configure directory listings to only show certain files or that match a certain pattern.

For that reason I think feroxbuster needs an option to override the default behavior at the very least.

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 3e337a4d..a2c8b5d8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,6 +17,12 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
 [[package]]
 name = "ahash"
 version = "0.8.11"
@@ -41,9 +47,9 @@ dependencies = [
 
 [[package]]
 name = "anstream"
-version = "0.6.14"
+version = "0.6.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
+checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
 dependencies = [
  "anstyle",
  "anstyle-parse",
@@ -56,33 +62,33 @@ dependencies = [
 
 [[package]]
 name = "anstyle"
-version = "1.0.7"
+version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
+checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
 
 [[package]]
 name = "anstyle-parse"
-version = "0.2.4"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
+checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
 dependencies = [
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle-query"
-version = "1.1.0"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
+checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
 dependencies = [
  "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "3.0.3"
+version = "3.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
+checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
 dependencies = [
  "anstyle",
  "windows-sys 0.52.0",
@@ -115,13 +121,14 @@ dependencies = [
 
 [[package]]
 name = "assert_cmd"
-version = "2.0.14"
+version = "2.0.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8"
+checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d"
 dependencies = [
  "anstyle",
  "bstr",
  "doc-comment",
+ "libc",
  "predicates",
  "predicates-core",
  "predicates-tree",
@@ -163,13 +170,13 @@ dependencies = [
 
 [[package]]
 name = "async-executor"
-version = "1.12.0"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0"
+checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7"
 dependencies = [
  "async-task",
  "concurrent-queue",
- "fastrand 2.1.0",
+ "fastrand 2.1.1",
  "futures-lite 2.3.0",
  "slab",
 ]
@@ -182,7 +189,7 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c"
 dependencies = [
  "async-channel 2.3.1",
  "async-executor",
- "async-io 2.3.3",
+ "async-io 2.3.4",
  "async-lock 3.4.0",
  "blocking",
  "futures-lite 2.3.0",
@@ -211,9 +218,9 @@ dependencies = [
 
 [[package]]
 name = "async-io"
-version = "2.3.3"
+version = "2.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964"
+checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8"
 dependencies = [
  "async-lock 3.4.0",
  "cfg-if",
@@ -221,11 +228,11 @@ dependencies = [
  "futures-io",
  "futures-lite 2.3.0",
  "parking",
- "polling 3.7.2",
+ "polling 3.7.3",
  "rustix 0.38.34",
  "slab",
  "tracing",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -276,11 +283,11 @@ dependencies = [
 
 [[package]]
 name = "async-signal"
-version = "0.2.8"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d"
+checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3"
 dependencies = [
- "async-io 2.3.3",
+ "async-io 2.3.4",
  "async-lock 3.4.0",
  "atomic-waker",
  "cfg-if",
@@ -289,7 +296,7 @@ dependencies = [
  "rustix 0.38.34",
  "signal-hook-registry",
  "slab",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -328,13 +335,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
 
 [[package]]
 name = "async-trait"
-version = "0.1.80"
+version = "0.1.81"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
+checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.66",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -359,7 +366,7 @@ dependencies = [
  "cc",
  "cfg-if",
  "libc",
- "miniz_oxide",
+ "miniz_oxide 0.7.4",
  "object",
  "rustc-demangle",
 ]
@@ -416,9 +423,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bitflags"
-version = "2.5.0"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 
 [[package]]
 name = "block-buffer"
@@ -444,9 +451,9 @@ dependencies = [
 
 [[package]]
 name = "bstr"
-version = "1.9.1"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
+checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
 dependencies = [
  "memchr",
  "regex-automata",
@@ -467,15 +474,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "bytes"
-version = "1.6.0"
+version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
 
 [[package]]
 name = "cc"
-version = "1.0.99"
+version = "1.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
+checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
+dependencies = [
+ "shlex",
+]
 
 [[package]]
 name = "cfg-if"
@@ -485,24 +495,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "cfg_aliases"
-version = "0.1.1"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
 
 [[package]]
 name = "clap"
-version = "4.5.7"
+version = "4.5.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
+checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
 dependencies = [
  "clap_builder",
 ]
 
 [[package]]
 name = "clap_builder"
-version = "4.5.7"
+version = "4.5.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
+checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
 dependencies = [
  "anstream",
  "anstyle",
@@ -513,24 +523,24 @@ dependencies = [
 
 [[package]]
 name = "clap_complete"
-version = "4.5.5"
+version = "4.5.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2020fa13af48afc65a9a87335bda648309ab3d154cd03c7ff95b378c7ed39c4"
+checksum = "531d7959c5bbb6e266cecdd0f20213639c3a5c3e4d615f97db87661745f781ff"
 dependencies = [
  "clap",
 ]
 
 [[package]]
 name = "clap_lex"
-version = "0.7.1"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
+checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
 
 [[package]]
 name = "colorchoice"
-version = "1.0.1"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
+checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
 
 [[package]]
 name = "concurrent-queue"
@@ -572,15 +582,15 @@ dependencies = [
 
 [[package]]
 name = "core-foundation-sys"
-version = "0.8.6"
+version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.12"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
 dependencies = [
  "libc",
 ]
@@ -625,10 +635,10 @@ version = "0.27.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
 dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
  "crossterm_winapi",
  "libc",
- "mio",
+ "mio 0.8.11",
  "parking_lot",
  "signal-hook",
  "signal-hook-mio",
@@ -680,31 +690,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
 dependencies = [
  "quote",
- "syn 2.0.66",
+ "syn 2.0.76",
 ]
 
 [[package]]
 name = "ctrlc"
-version = "3.4.4"
+version = "3.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345"
+checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3"
 dependencies = [
  "nix",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "curve25519-dalek"
-version = "4.1.2"
+version = "4.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
+checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
 dependencies = [
  "cfg-if",
  "cpufeatures",
  "curve25519-dalek-derive",
  "digest",
  "fiat-crypto",
- "platforms",
  "rustc_version",
  "subtle",
  "zeroize",
@@ -718,7 +727,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.66",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -748,7 +757,7 @@ checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.66",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -809,17 +818,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "displaydoc"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.66",
-]
-
 [[package]]
 name = "doc-comment"
 version = "0.3.3"
@@ -868,15 +866,15 @@ dependencies = [
 
 [[package]]
 name = "ego-tree"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591"
+checksum = "12a0bb14ac04a9fcf170d0bbbef949b44cc492f4452bd20c095636956f653642"
 
 [[package]]
 name = "either"
-version = "1.12.0"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
 
 [[package]]
 name = "ena"
@@ -904,9 +902,9 @@ dependencies = [
 
 [[package]]
 name = "env_filter"
-version = "0.1.0"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
+checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
 dependencies = [
  "log",
  "regex",
@@ -914,9 +912,9 @@ dependencies = [
 
 [[package]]
 name = "env_logger"
-version = "0.11.3"
+version = "0.11.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
+checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
 dependencies = [
  "anstream",
  "anstyle",
@@ -990,13 +988,13 @@ dependencies = [
 
 [[package]]
 name = "fastrand"
-version = "2.1.0"
+version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
 
 [[package]]
 name = "feroxbuster"
-version = "2.10.4"
+version = "2.11.0"
 dependencies = [
  "anyhow",
  "assert_cmd",
@@ -1040,14 +1038,14 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
 
 [[package]]
 name = "filetime"
-version = "0.2.23"
+version = "0.2.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
+checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall 0.4.1",
- "windows-sys 0.52.0",
+ "libredox",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -1058,12 +1056,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
 
 [[package]]
 name = "flate2"
-version = "1.0.30"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
+checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253"
 dependencies = [
  "crc32fast",
- "miniz_oxide",
+ "miniz_oxide 0.8.0",
 ]
 
 [[package]]
@@ -1190,7 +1188,7 @@ version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
 dependencies = [
- "fastrand 2.1.0",
+ "fastrand 2.1.1",
  "futures-core",
  "futures-io",
  "parking",
@@ -1205,7 +1203,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.66",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -1320,9 +1318,9 @@ dependencies = [
 
 [[package]]
 name = "h2"
-version = "0.4.5"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab"
+checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205"
 dependencies = [
  "atomic-waker",
  "bytes",
@@ -1357,16 +1355,16 @@ checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
 
 [[package]]
 name = "html5ever"
-version = "0.26.0"
+version = "0.27.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
+checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4"
 dependencies = [
  "log",
  "mac",
  "markup5ever",
  "proc-macro2",
  "quote",
- "syn 1.0.109",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -1404,9 +1402,9 @@ dependencies = [
 
 [[package]]
 name = "http-body"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
 dependencies = [
  "bytes",
  "http 1.1.0",
@@ -1421,15 +1419,15 @@ dependencies = [
  "bytes",
  "futures-util",
  "http 1.1.0",
- "http-body 1.0.0",
+ "http-body 1.0.1",
  "pin-project-lite",
 ]
 
 [[package]]
 name = "httparse"
-version = "1.9.3"
+version = "1.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545"
+checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
 
 [[package]]
 name = "httpdate"
@@ -1452,7 +1450,7 @@ dependencies = [
  "crossbeam-utils",
  "form_urlencoded",
  "futures-util",
- "hyper 0.14.29",
+ "hyper 0.14.30",
  "lazy_static",
  "levenshtein",
  "log",
@@ -1473,9 +1471,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
 [[package]]
 name = "hyper"
-version = "0.14.29"
+version = "0.14.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33"
+checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -1496,16 +1494,16 @@ dependencies = [
 
 [[package]]
 name = "hyper"
-version = "1.3.1"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d"
+checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
 dependencies = [
  "bytes",
  "futures-channel",
  "futures-util",
  "h2",
  "http 1.1.0",
- "http-body 1.0.0",
+ "http-body 1.0.1",
  "httparse",
  "itoa",
  "pin-project-lite",
@@ -1514,6 +1512,23 @@ dependencies = [
  "want",
 ]
 
+[[package]]
+name = "hyper-rustls"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
+dependencies = [
+ "futures-util",
+ "http 1.1.0",
+ "hyper 1.4.1",
+ "hyper-util",
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+]
+
 [[package]]
 name = "hyper-tls"
 version = "0.6.0"
@@ -1522,7 +1537,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
 dependencies = [
  "bytes",
  "http-body-util",
- "hyper 1.3.1",
+ "hyper 1.4.1",
  "hyper-util",
  "native-tls",
  "tokio",
@@ -1532,16 +1547,16 @@ dependencies = [
 
 [[package]]
 name = "hyper-util"
-version = "0.1.5"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56"
+checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
 dependencies = [
  "bytes",
  "futures-channel",
  "futures-util",
  "http 1.1.0",
- "http-body 1.0.0",
- "hyper 1.3.1",
+ "http-body 1.0.1",
+ "hyper 1.4.1",
  "pin-project-lite",
  "socket2 0.5.7",
  "tokio",
@@ -1550,141 +1565,21 @@ dependencies = [
  "tracing",
 ]
 
-[[package]]
-name = "icu_collections"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
-dependencies = [
- "displaydoc",
- "yoke",
- "zerofrom",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
-dependencies = [
- "displaydoc",
- "litemap",
- "tinystr",
- "writeable",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid_transform"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
-dependencies = [
- "displaydoc",
- "icu_locid",
- "icu_locid_transform_data",
- "icu_provider",
- "tinystr",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid_transform_data"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
-
-[[package]]
-name = "icu_normalizer"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
-dependencies = [
- "displaydoc",
- "icu_collections",
- "icu_normalizer_data",
- "icu_properties",
- "icu_provider",
- "smallvec",
- "utf16_iter",
- "utf8_iter",
- "write16",
- "zerovec",
-]
-
-[[package]]
-name = "icu_normalizer_data"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
-
-[[package]]
-name = "icu_properties"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036"
-dependencies = [
- "displaydoc",
- "icu_collections",
- "icu_locid_transform",
- "icu_properties_data",
- "icu_provider",
- "tinystr",
- "zerovec",
-]
-
-[[package]]
-name = "icu_properties_data"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
-
-[[package]]
-name = "icu_provider"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
-dependencies = [
- "displaydoc",
- "icu_locid",
- "icu_provider_macros",
- "stable_deref_trait",
- "tinystr",
- "writeable",
- "yoke",
- "zerofrom",
- "zerovec",
-]
-
-[[package]]
-name = "icu_provider_macros"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.66",
-]
-
 [[package]]
 name = "idna"
-version = "1.0.0"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
 dependencies = [
- "icu_normalizer",
- "icu_properties",
- "smallvec",
- "utf8_iter",
+ "unicode-bidi",
+ "unicode-normalization",
 ]
 
 [[package]]
 name = "indexmap"
-version = "2.2.6"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
 dependencies = [
  "equivalent",
  "hashbrown",
@@ -1731,9 +1626,9 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
 
 [[package]]
 name = "is_terminal_polyfill"
-version = "1.70.0"
+version = "1.70.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
 
 [[package]]
 name = "itertools"
@@ -1761,9 +1656,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
 
 [[package]]
 name = "js-sys"
-version = "0.3.69"
+version = "0.3.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -1810,9 +1705,9 @@ dependencies = [
 
 [[package]]
 name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "leaky-bucket"
@@ -1833,9 +1728,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
 
 [[package]]
 name = "libc"
-version = "0.2.155"
+version = "0.2.158"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
 
 [[package]]
 name = "libredox"
@@ -1843,8 +1738,9 @@ version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
 dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
  "libc",
+ "redox_syscall",
 ]
 
 [[package]]
@@ -1859,12 +1755,6 @@ version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
-[[package]]
-name = "litemap"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
-
 [[package]]
 name = "lock_api"
 version = "0.4.12"
@@ -1877,9 +1767,9 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.21"
+version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
 dependencies = [
  "value-bag",
 ]
@@ -1892,13 +1782,13 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
 
 [[package]]
 name = "markup5ever"
-version = "0.11.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
+checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45"
 dependencies = [
  "log",
- "phf 0.10.1",
- "phf_codegen",
+ "phf 0.11.2",
+ "phf_codegen 0.11.2",
  "string_cache",
  "string_cache_codegen",
  "tendril",
@@ -1918,13 +1808,22 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.7.3"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
+checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
 dependencies = [
  "adler",
 ]
 
+[[package]]
+name = "miniz_oxide"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
+dependencies = [
+ "adler2",
+]
+
 [[package]]
 name = "mio"
 version = "0.8.11"
@@ -1937,6 +1836,18 @@ dependencies = [
  "windows-sys 0.48.0",
 ]
 
+[[package]]
+name = "mio"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
+dependencies = [
+ "hermit-abi 0.3.9",
+ "libc",
+ "wasi",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "native-tls"
 version = "0.2.12"
@@ -1962,11 +1873,11 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
 
 [[package]]
 name = "nix"
-version = "0.28.0"
+version = "0.29.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
+checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
 dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
  "cfg-if",
  "cfg_aliases",
  "libc",
@@ -1993,16 +1904,6 @@ dependencies = [
  "autocfg",
 ]
 
-[[package]]
-name = "num_cpus"
-version = "1.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
-dependencies = [
- "hermit-abi 0.3.9",
- "libc",
-]
-
 [[package]]
 name = "number_prefix"
 version = "0.4.0"
@@ -2011,9 +1912,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
 
 [[package]]
 name = "object"
-version = "0.36.0"
+version = "0.36.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
+checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
 dependencies = [
  "memchr",
 ]
@@ -2026,11 +1927,11 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
 [[package]]
 name = "openssl"
-version = "0.10.64"
+version = "0.10.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
+checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
 dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
  "cfg-if",
  "foreign-types",
  "libc",
@@ -2047,7 +1948,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.66",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -2067,9 +1968,9 @@ dependencies = [
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.102"
+version = "0.9.103"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
+checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
 dependencies = [
  "cc",
  "libc",
@@ -2108,9 +2009,9 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall 0.5.2",
+ "redox_syscall",
  "smallvec",
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -2158,6 +2059,16 @@ dependencies = [
  "phf_shared 0.10.0",
 ]
 
+[[package]]
+name = "phf_codegen"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
+dependencies = [
+ "phf_generator 0.11.2",
+ "phf_shared 0.11.2",
+]
+
 [[package]]
 name = "phf_generator"
 version = "0.10.0"
@@ -2188,7 +2099,7 @@ dependencies = [
  "phf_shared 0.11.2",
  "proc-macro2",
  "quote",
- "syn 2.0.66",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -2232,7 +2143,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.66",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -2249,12 +2160,12 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
 [[package]]
 name = "piper"
-version = "0.2.3"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391"
+checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
 dependencies = [
  "atomic-waker",
- "fastrand 2.1.0",
+ "fastrand 2.1.1",
  "futures-io",
 ]
 
@@ -2274,12 +2185,6 @@ version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
 
-[[package]]
-name = "platforms"
-version = "3.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7"
-
 [[package]]
 name = "polling"
 version = "2.8.0"
@@ -2298,9 +2203,9 @@ dependencies = [
 
 [[package]]
 name = "polling"
-version = "3.7.2"
+version = "3.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b"
+checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511"
 dependencies = [
  "cfg-if",
  "concurrent-queue",
@@ -2308,14 +2213,14 @@ dependencies = [
  "pin-project-lite",
  "rustix 0.38.34",
  "tracing",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "portable-atomic"
-version = "1.6.0"
+version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
+checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
 
 [[package]]
 name = "powerfmt"
@@ -2325,9 +2230,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.17"
+version = "0.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
 
 [[package]]
 name = "precomputed-hash"
@@ -2337,9 +2245,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
 
 [[package]]
 name = "predicates"
-version = "3.1.0"
+version = "3.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8"
+checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97"
 dependencies = [
  "anstyle",
  "difflib",
@@ -2351,15 +2259,15 @@ dependencies = [
 
 [[package]]
 name = "predicates-core"
-version = "1.0.6"
+version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174"
+checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931"
 
 [[package]]
 name = "predicates-tree"
-version = "1.0.9"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf"
+checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13"
 dependencies = [
  "predicates-core",
  "termtree",
@@ -2367,9 +2275,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.85"
+version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
 dependencies = [
  "unicode-ident",
 ]
@@ -2385,9 +2293,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.36"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
 dependencies = [
  "proc-macro2",
 ]
@@ -2509,27 +2417,18 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
-dependencies = [
- "bitflags 1.3.2",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.5.2"
+version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
+checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
 dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
 ]
 
 [[package]]
 name = "redox_users"
-version = "0.4.5"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
+checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
 dependencies = [
  "getrandom",
  "libredox",
@@ -2538,9 +2437,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.10.5"
+version = "1.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -2567,9 +2466,9 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
 
 [[package]]
 name = "reqwest"
-version = "0.12.4"
+version = "0.12.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10"
+checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
 dependencies = [
  "base64 0.22.1",
  "bytes",
@@ -2579,9 +2478,10 @@ dependencies = [
  "futures-util",
  "h2",
  "http 1.1.0",
- "http-body 1.0.0",
+ "http-body 1.0.1",
  "http-body-util",
- "hyper 1.3.1",
+ "hyper 1.4.1",
+ "hyper-rustls",
  "hyper-tls",
  "hyper-util",
  "ipnet",
@@ -2606,7 +2506,22 @@ dependencies = [
  "wasm-bindgen",
  "wasm-bindgen-futures",
  "web-sys",
- "winreg",
+ "windows-registry",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom",
+ "libc",
+ "spin",
+ "untrusted",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -2653,18 +2568,31 @@ version = "0.38.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
 dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
  "errno",
  "libc",
  "linux-raw-sys 0.4.14",
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "rustls"
+version = "0.23.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
+dependencies = [
+ "once_cell",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
 [[package]]
 name = "rustls-pemfile"
-version = "2.1.2"
+version = "2.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
+checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
 dependencies = [
  "base64 0.22.1",
  "rustls-pki-types",
@@ -2672,9 +2600,20 @@ dependencies = [
 
 [[package]]
 name = "rustls-pki-types"
-version = "1.7.0"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
+checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.102.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
 
 [[package]]
 name = "rustversion"
@@ -2714,9 +2653,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
 [[package]]
 name = "scraper"
-version = "0.19.0"
+version = "0.19.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b80b33679ff7a0ea53d37f3b39de77ea0c75b12c5805ac43ec0c33b3051af1b"
+checksum = "761fb705fdf625482d2ed91d3f0559dcfeab2798fe2771c69560a774865d0802"
 dependencies = [
  "ahash",
  "cssparser",
@@ -2736,11 +2675,11 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
 
 [[package]]
 name = "security-framework"
-version = "2.11.0"
+version = "2.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
 dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
  "core-foundation",
  "core-foundation-sys",
  "libc",
@@ -2749,9 +2688,9 @@ dependencies = [
 
 [[package]]
 name = "security-framework-sys"
-version = "2.11.0"
+version = "2.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7"
+checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -2763,14 +2702,14 @@ version = "0.25.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06"
 dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
  "cssparser",
  "derive_more",
  "fxhash",
  "log",
  "new_debug_unreachable",
  "phf 0.10.1",
- "phf_codegen",
+ "phf_codegen 0.10.0",
  "precomputed-hash",
  "servo_arc",
  "smallvec",
@@ -2778,9 +2717,9 @@ dependencies = [
 
 [[package]]
 name = "self-replace"
-version = "1.3.7"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "525db198616b2bcd0f245daf7bfd8130222f7ee6af9ff9984c19a61bf1160c55"
+checksum = "f7828a58998685d8bf5a3c5e7a3379a5867289c20828c3ee436280b44b598515"
 dependencies = [
  "fastrand 1.9.0",
  "tempfile",
@@ -2795,7 +2734,7 @@ checksum = "4e4997484b55df069a4773d822715695b2cc27b23829eca2a4b41690e948bdeb"
 dependencies = [
  "either",
  "flate2",
- "hyper 1.3.1",
+ "hyper 1.4.1",
  "indicatif",
  "log",
  "quick-xml",
@@ -2819,31 +2758,32 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
 
 [[package]]
 name = "serde"
-version = "1.0.203"
+version = "1.0.209"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
+checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.203"
+version = "1.0.209"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
+checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.66",
+ "syn 2.0.76",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.117"
+version = "1.0.127"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
+checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
 dependencies = [
  "itoa",
+ "memchr",
  "ryu",
  "serde",
 ]
@@ -2860,9 +2800,9 @@ dependencies = [
 
 [[package]]
 name = "serde_spanned"
-version = "0.6.6"
+version = "0.6.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
+checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
 dependencies = [
  "serde",
 ]
@@ -2916,6 +2856,12 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "72bb884be1ddfbded5873be4672cf5aee71210ce0f8ae99787d158b9b72b5ca0"
 
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
 [[package]]
 name = "signal-hook"
 version = "0.3.17"
@@ -2928,12 +2874,12 @@ dependencies = [
 
 [[package]]
 name = "signal-hook-mio"
-version = "0.2.3"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
+checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
 dependencies = [
  "libc",
- "mio",
+ "mio 0.8.11",
  "signal-hook",
 ]
 
@@ -2958,9 +2904,9 @@ dependencies = [
 
 [[package]]
 name = "similar"
-version = "2.5.0"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
+checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
 
 [[package]]
 name = "siphasher"
@@ -3006,6 +2952,12 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
 [[package]]
 name = "spki"
 version = "0.7.3"
@@ -3056,9 +3008,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "subtle"
-version = "2.5.0"
+version = "2.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
 [[package]]
 name = "syn"
@@ -3073,9 +3025,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.66"
+version = "2.0.76"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
+checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3084,37 +3036,29 @@ dependencies = [
 
 [[package]]
 name = "sync_wrapper"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
-
-[[package]]
-name = "synstructure"
-version = "0.13.1"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.66",
+ "futures-core",
 ]
 
 [[package]]
 name = "system-configuration"
-version = "0.5.1"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
+checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.6.0",
  "core-foundation",
  "system-configuration-sys",
 ]
 
 [[package]]
 name = "system-configuration-sys"
-version = "0.5.0"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -3133,14 +3077,15 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.10.1"
+version = "3.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
 dependencies = [
  "cfg-if",
- "fastrand 2.1.0",
+ "fastrand 2.1.1",
+ "once_cell",
  "rustix 0.38.34",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -3183,22 +3128,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
 
 [[package]]
 name = "thiserror"
-version = "1.0.61"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.61"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.66",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -3230,43 +3175,47 @@ dependencies = [
 ]
 
 [[package]]
-name = "tinystr"
-version = "0.7.6"
+name = "tinyvec"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
 dependencies = [
- "displaydoc",
- "zerovec",
+ "tinyvec_macros",
 ]
 
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
 [[package]]
 name = "tokio"
-version = "1.38.0"
+version = "1.39.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
+checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5"
 dependencies = [
  "backtrace",
  "bytes",
  "libc",
- "mio",
- "num_cpus",
+ "mio 1.0.2",
  "parking_lot",
  "pin-project-lite",
  "signal-hook-registry",
  "socket2 0.5.7",
  "tokio-macros",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "tokio-macros"
-version = "2.3.0"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
+checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.66",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -3279,11 +3228,22 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "tokio-rustls"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
+dependencies = [
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+]
+
 [[package]]
 name = "tokio-socks"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0"
+checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f"
 dependencies = [
  "either",
  "futures-util",
@@ -3306,9 +3266,9 @@ dependencies = [
 
 [[package]]
 name = "toml"
-version = "0.8.14"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
+checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
 dependencies = [
  "serde",
  "serde_spanned",
@@ -3318,18 +3278,18 @@ dependencies = [
 
 [[package]]
 name = "toml_datetime"
-version = "0.6.6"
+version = "0.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "toml_edit"
-version = "0.22.14"
+version = "0.22.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
+checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
 dependencies = [
  "indexmap",
  "serde",
@@ -3355,15 +3315,15 @@ dependencies = [
 
 [[package]]
 name = "tower-layer"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
 
 [[package]]
 name = "tower-service"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
 
 [[package]]
 name = "tracing"
@@ -3386,9 +3346,9 @@ dependencies = [
 
 [[package]]
 name = "triomphe"
-version = "0.1.12"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b2cb4fbb9995eeb36ac86fadf24031ccd58f99d6b4b2d7b911db70bddb80d90"
+checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369"
 dependencies = [
  "serde",
  "stable_deref_trait",
@@ -3406,12 +3366,27 @@ version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
 
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
+[[package]]
+name = "unicode-normalization"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+dependencies = [
+ "tinyvec",
+]
+
 [[package]]
 name = "unicode-width"
 version = "0.1.13"
@@ -3420,15 +3395,21 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
 
 [[package]]
 name = "unicode-xid"
-version = "0.2.4"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
+checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
 
 [[package]]
 name = "url"
-version = "2.5.1"
+version = "2.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56"
+checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
 dependencies = [
  "form_urlencoded",
  "idna",
@@ -3448,18 +3429,6 @@ version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
 
-[[package]]
-name = "utf16_iter"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
-
-[[package]]
-name = "utf8_iter"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
-
 [[package]]
 name = "utf8parse"
 version = "0.2.2"
@@ -3468,9 +3437,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
 [[package]]
 name = "uuid"
-version = "1.8.0"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
+checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
 dependencies = [
  "getrandom",
 ]
@@ -3489,9 +3458,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
 
 [[package]]
 name = "version_check"
-version = "0.9.4"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
 
 [[package]]
 name = "wait-timeout"
@@ -3535,34 +3504,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
 dependencies = [
  "cfg-if",
+ "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
 dependencies = [
  "bumpalo",
  "log",
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.66",
+ "syn 2.0.76",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.42"
+version = "0.4.43"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
+checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -3572,9 +3542,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -3582,28 +3552,28 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.66",
+ "syn 2.0.76",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
 
 [[package]]
 name = "web-sys"
-version = "0.3.69"
+version = "0.3.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -3627,11 +3597,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
 name = "winapi-util"
-version = "0.1.8"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
 dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -3640,6 +3610,36 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
+[[package]]
+name = "windows-registry"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
+dependencies = [
+ "windows-result",
+ "windows-strings",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
+dependencies = [
+ "windows-result",
+ "windows-targets 0.52.6",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.48.0"
@@ -3655,7 +3655,16 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
 dependencies = [
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -3675,18 +3684,18 @@ dependencies = [
 
 [[package]]
 name = "windows-targets"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
- "windows_aarch64_gnullvm 0.52.5",
- "windows_aarch64_msvc 0.52.5",
- "windows_i686_gnu 0.52.5",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
  "windows_i686_gnullvm",
- "windows_i686_msvc 0.52.5",
- "windows_x86_64_gnu 0.52.5",
- "windows_x86_64_gnullvm 0.52.5",
- "windows_x86_64_msvc 0.52.5",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
 ]
 
 [[package]]
@@ -3697,9 +3706,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
 [[package]]
 name = "windows_aarch64_msvc"
@@ -3709,9 +3718,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
 [[package]]
 name = "windows_i686_gnu"
@@ -3721,15 +3730,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 
 [[package]]
 name = "windows_i686_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
 [[package]]
 name = "windows_i686_msvc"
@@ -3739,9 +3748,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
 [[package]]
 name = "windows_x86_64_gnu"
@@ -3751,9 +3760,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
@@ -3763,9 +3772,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
 [[package]]
 name = "windows_x86_64_msvc"
@@ -3775,41 +3784,19 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
 [[package]]
 name = "winnow"
-version = "0.6.13"
+version = "0.6.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1"
+checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
 dependencies = [
  "memchr",
 ]
 
-[[package]]
-name = "winreg"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
-dependencies = [
- "cfg-if",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "write16"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
-
-[[package]]
-name = "writeable"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
-
 [[package]]
 name = "xattr"
 version = "1.3.1"
@@ -3821,69 +3808,25 @@ dependencies = [
  "rustix 0.38.34",
 ]
 
-[[package]]
-name = "yoke"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
-dependencies = [
- "serde",
- "stable_deref_trait",
- "yoke-derive",
- "zerofrom",
-]
-
-[[package]]
-name = "yoke-derive"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.66",
- "synstructure",
-]
-
 [[package]]
 name = "zerocopy"
-version = "0.7.34"
+version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
 dependencies = [
+ "byteorder",
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.7.34"
+version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.66",
-]
-
-[[package]]
-name = "zerofrom"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
-dependencies = [
- "zerofrom-derive",
-]
-
-[[package]]
-name = "zerofrom-derive"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.66",
- "synstructure",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -3892,28 +3835,6 @@ version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
 
-[[package]]
-name = "zerovec"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c"
-dependencies = [
- "yoke",
- "zerofrom",
- "zerovec-derive",
-]
-
-[[package]]
-name = "zerovec-derive"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.66",
-]
-
 [[package]]
 name = "zip"
 version = "0.6.6"
@@ -3929,11 +3850,11 @@ dependencies = [
 
 [[package]]
 name = "zipsign-api"
-version = "0.1.1"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ba5aa1827d6b1a35a29b3413ec69ce5f796e4d897e3e5b38f461bef41d225ea"
+checksum = "6413a546ada9dbcd0b9a3e0b0880581279e35047bce9797e523b3408e1df607c"
 dependencies = [
- "base64 0.21.7",
+ "base64 0.22.1",
  "ed25519-dalek",
  "thiserror",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 785ee464..ad7b83b5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "feroxbuster"
-version = "2.10.4"
+version = "2.11.0"
 authors = ["Ben 'epi' Risher (@epi052)"]
 license = "MIT"
 edition = "2021"
@@ -25,13 +25,13 @@ maintenance = { status = "actively-developed" }
 clap = { version = "4.5", features = ["wrap_help", "cargo"] }
 clap_complete = "4.5"
 regex = "1.10"
-lazy_static = "1.4"
+lazy_static = "1.5"
 dirs = "5.0"
 
 [dependencies]
 scraper = "0.19"
 futures = "0.3"
-tokio = { version = "1.38", features = ["full"] }
+tokio = { version = "1.39", features = ["full"] }
 tokio-util = { version = "0.7", features = ["codec"] }
 log = "0.4"
 env_logger = "0.11"
@@ -40,13 +40,11 @@ reqwest = { version = "0.12", features = ["socks", "native-tls-alpn"] }
 url = { version = "2.5", features = ["serde"] }
 serde_regex = "1.1"
 clap = { version = "4.5", features = ["wrap_help", "cargo"] }
-lazy_static = "1.4"
+lazy_static = "1.5"
 toml = "0.8"
 serde = { version = "1.0", features = ["derive", "rc"] }
 serde_json = "1.0"
-uuid = { version = "1.8", features = ["v4"] }
-# last known working version of indicatif; 0.17.5 has a bug that causes the 
-# scan menu to fail spectacularly 
+uuid = { version = "1.10", features = ["v4"] }
 indicatif = { version = "0.17.8" }
 console = "0.15"
 openssl = { version = "0.10", features = ["vendored"] }
@@ -69,7 +67,7 @@ self_update = { version = "0.40", features = [
 ] }
 
 [dev-dependencies]
-tempfile = "3.10"
+tempfile = "3.12"
 httpmock = "0.7"
 assert_cmd = "2.0"
 predicates = "3.1"
diff --git a/Makefile.toml b/Makefile.toml
index f80e4309..49b8df0b 100644
--- a/Makefile.toml
+++ b/Makefile.toml
@@ -14,7 +14,7 @@ rm ferox-*.state
 # dependency management
 [tasks.upgrade-deps]
 command = "cargo"
-args = ["upgrade", "--exclude", "indicatif, self_update"]
+args = ["upgrade", "--exclude", "self_update"]
 
 [tasks.update]
 command = "cargo"
diff --git a/ferox-config.toml.example b/ferox-config.toml.example
index 1c87a2cd..bd8a0d9a 100644
--- a/ferox-config.toml.example
+++ b/ferox-config.toml.example
@@ -45,6 +45,7 @@
 # dont_filter = true
 # extract_links = true
 # depth = 1
+# limit_bars = 3
 # force_recursion = true
 # filter_size = [5174]
 # filter_regex = ["^ignore me$"]
@@ -57,6 +58,9 @@
 # server_certs = ["/some/cert.pem", "/some/other/cert.pem"]
 # client_cert = "/some/client/cert.pem"
 # client_key = "/some/client/key.pem"
+# request_file = "/some/raw/request/file"
+# protocol = "http"
+# scan_dir_listings = true
 
 # headers can be specified on multiple lines or as an inline table
 #
diff --git a/shell_completions/_feroxbuster b/shell_completions/_feroxbuster
index 43605a29..d78f388c 100644
--- a/shell_completions/_feroxbuster
+++ b/shell_completions/_feroxbuster
@@ -15,17 +15,18 @@ _feroxbuster() {
 
     local context curcontext="$curcontext" state line
     _arguments "${_arguments_options[@]}" : \
-'-u+[The target URL (required, unless \[--stdin || --resume-from\] used)]:URL:_urls' \
-'--url=[The target URL (required, unless \[--stdin || --resume-from\] used)]:URL:_urls' \
+'-u+[The target URL (required, unless \[--stdin || --resume-from || --request-file\] used)]:URL:_urls' \
+'--url=[The target URL (required, unless \[--stdin || --resume-from || --request-file\] used)]:URL:_urls' \
 '(-u --url)--resume-from=[State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state)]:STATE_FILE:_files' \
+'(-u --url)--request-file=[Raw HTTP request file to use as a template for all requests]:REQUEST_FILE:_files' \
 '-p+[Proxy to use for requests (ex\: http(s)\://host\:port, socks5(h)\://host\:port)]:PROXY:_urls' \
 '--proxy=[Proxy to use for requests (ex\: http(s)\://host\:port, socks5(h)\://host\:port)]:PROXY:_urls' \
 '-P+[Send only unfiltered requests through a Replay Proxy, instead of all requests]:REPLAY_PROXY:_urls' \
 '--replay-proxy=[Send only unfiltered requests through a Replay Proxy, instead of all requests]:REPLAY_PROXY:_urls' \
 '*-R+[Status Codes to send through a Replay Proxy when found (default\: --status-codes value)]:REPLAY_CODE: ' \
 '*--replay-codes=[Status Codes to send through a Replay Proxy when found (default\: --status-codes value)]:REPLAY_CODE: ' \
-'-a+[Sets the User-Agent (default\: feroxbuster/2.10.4)]:USER_AGENT: ' \
-'--user-agent=[Sets the User-Agent (default\: feroxbuster/2.10.4)]:USER_AGENT: ' \
+'-a+[Sets the User-Agent (default\: feroxbuster/2.11.0)]:USER_AGENT: ' \
+'--user-agent=[Sets the User-Agent (default\: feroxbuster/2.11.0)]:USER_AGENT: ' \
 '*-x+[File extension(s) to search for (ex\: -x php -x pdf js); reads values (newline-separated) from file if input starts with an @ (ex\: @ext.txt)]:FILE_EXTENSION: ' \
 '*--extensions=[File extension(s) to search for (ex\: -x php -x pdf js); reads values (newline-separated) from file if input starts with an @ (ex\: @ext.txt)]:FILE_EXTENSION: ' \
 '*-m+[Which HTTP request method(s) should be sent (default\: GET)]:HTTP_METHODS: ' \
@@ -37,6 +38,7 @@ _feroxbuster() {
 '*--cookies=[Specify HTTP cookies to be used in each request (ex\: -b stuff=things)]:COOKIE: ' \
 '*-Q+[Request'\''s URL query parameters (ex\: -Q token=stuff -Q secret=key)]:QUERY: ' \
 '*--query=[Request'\''s URL query parameters (ex\: -Q token=stuff -Q secret=key)]:QUERY: ' \
+'--protocol=[Specify the protocol to use when targeting via --request-file or --url with domain only (default\: https)]:PROTOCOL: ' \
 '*--dont-scan=[URL(s) or Regex Pattern(s) to exclude from recursion/scans]:URL: ' \
 '*-S+[Filter out messages of a particular size (ex\: -S 5120 -S 4927,1970)]:SIZE: ' \
 '*--filter-size=[Filter out messages of a particular size (ex\: -S 5120 -S 4927,1970)]:SIZE: ' \
@@ -74,11 +76,12 @@ _feroxbuster() {
 '-o+[Output file to write results to (use w/ --json for JSON entries)]:FILE:_files' \
 '--output=[Output file to write results to (use w/ --json for JSON entries)]:FILE:_files' \
 '--debug-log=[Output file to write log entries (use w/ --json for JSON entries)]:FILE:_files' \
+'--limit-bars=[Number of directory scan bars to show at any given time (default\: no limit)]:NUM_BARS_TO_SHOW: ' \
 '(-u --url)--stdin[Read url(s) from STDIN]' \
 '(-p --proxy -k --insecure --burp-replay)--burp[Set --proxy to http\://127.0.0.1\:8080 and set --insecure to true]' \
 '(-P --replay-proxy -k --insecure)--burp-replay[Set --replay-proxy to http\://127.0.0.1\:8080 and set --insecure to true]' \
 '(--rate-limit --auto-bail)--smart[Set --auto-tune, --collect-words, and --collect-backups to true]' \
-'(--rate-limit --auto-bail)--thorough[Use the same settings as --smart and set --collect-extensions to true]' \
+'(--rate-limit --auto-bail)--thorough[Use the same settings as --smart and set --collect-extensions and --scan-dir-listings to true]' \
 '-A[Use a random User-Agent]' \
 '--random-agent[Use a random User-Agent]' \
 '-f[Append / to each request'\''s URL]' \
@@ -101,6 +104,7 @@ _feroxbuster() {
 '--collect-extensions[Automatically discover extensions and add them to --extensions (unless they'\''re in --dont-collect)]' \
 '-g[Automatically discover important words from within responses and add them to the wordlist]' \
 '--collect-words[Automatically discover important words from within responses and add them to the wordlist]' \
+'--scan-dir-listings[Force scans to recurse into directory listings]' \
 '(--silent)*-v[Increase verbosity level (use -vv or more for greater effect. \[CAUTION\] 4 -v'\''s is probably too much)]' \
 '(--silent)*--verbosity[Increase verbosity level (use -vv or more for greater effect. \[CAUTION\] 4 -v'\''s is probably too much)]' \
 '(-q --quiet)--silent[Only print URLs (or JSON w/ --json) + turn off logging (good for piping a list of urls to other commands)]' \
diff --git a/shell_completions/_feroxbuster.ps1 b/shell_completions/_feroxbuster.ps1
index c7ad2e9f..a738be3f 100644
--- a/shell_completions/_feroxbuster.ps1
+++ b/shell_completions/_feroxbuster.ps1
@@ -21,105 +21,109 @@ Register-ArgumentCompleter -Native -CommandName 'feroxbuster' -ScriptBlock {
 
     $completions = @(switch ($command) {
         'feroxbuster' {
-            [CompletionResult]::new('-u', 'u', [CompletionResultType]::ParameterName, 'The target URL (required, unless [--stdin || --resume-from] used)')
-            [CompletionResult]::new('--url', 'url', [CompletionResultType]::ParameterName, 'The target URL (required, unless [--stdin || --resume-from] used)')
-            [CompletionResult]::new('--resume-from', 'resume-from', [CompletionResultType]::ParameterName, 'State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state)')
-            [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Proxy to use for requests (ex: http(s)://host:port, socks5(h)://host:port)')
-            [CompletionResult]::new('--proxy', 'proxy', [CompletionResultType]::ParameterName, 'Proxy to use for requests (ex: http(s)://host:port, socks5(h)://host:port)')
-            [CompletionResult]::new('-P', 'P ', [CompletionResultType]::ParameterName, 'Send only unfiltered requests through a Replay Proxy, instead of all requests')
-            [CompletionResult]::new('--replay-proxy', 'replay-proxy', [CompletionResultType]::ParameterName, 'Send only unfiltered requests through a Replay Proxy, instead of all requests')
-            [CompletionResult]::new('-R', 'R ', [CompletionResultType]::ParameterName, 'Status Codes to send through a Replay Proxy when found (default: --status-codes value)')
-            [CompletionResult]::new('--replay-codes', 'replay-codes', [CompletionResultType]::ParameterName, 'Status Codes to send through a Replay Proxy when found (default: --status-codes value)')
-            [CompletionResult]::new('-a', 'a', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/2.10.4)')
-            [CompletionResult]::new('--user-agent', 'user-agent', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/2.10.4)')
-            [CompletionResult]::new('-x', 'x', [CompletionResultType]::ParameterName, 'File extension(s) to search for (ex: -x php -x pdf js); reads values (newline-separated) from file if input starts with an @ (ex: @ext.txt)')
-            [CompletionResult]::new('--extensions', 'extensions', [CompletionResultType]::ParameterName, 'File extension(s) to search for (ex: -x php -x pdf js); reads values (newline-separated) from file if input starts with an @ (ex: @ext.txt)')
-            [CompletionResult]::new('-m', 'm', [CompletionResultType]::ParameterName, 'Which HTTP request method(s) should be sent (default: GET)')
-            [CompletionResult]::new('--methods', 'methods', [CompletionResultType]::ParameterName, 'Which HTTP request method(s) should be sent (default: GET)')
-            [CompletionResult]::new('--data', 'data', [CompletionResultType]::ParameterName, 'Request''s Body; can read data from a file if input starts with an @ (ex: @post.bin)')
-            [CompletionResult]::new('-H', 'H ', [CompletionResultType]::ParameterName, 'Specify HTTP headers to be used in each request (ex: -H Header:val -H ''stuff: things'')')
-            [CompletionResult]::new('--headers', 'headers', [CompletionResultType]::ParameterName, 'Specify HTTP headers to be used in each request (ex: -H Header:val -H ''stuff: things'')')
-            [CompletionResult]::new('-b', 'b', [CompletionResultType]::ParameterName, 'Specify HTTP cookies to be used in each request (ex: -b stuff=things)')
-            [CompletionResult]::new('--cookies', 'cookies', [CompletionResultType]::ParameterName, 'Specify HTTP cookies to be used in each request (ex: -b stuff=things)')
-            [CompletionResult]::new('-Q', 'Q ', [CompletionResultType]::ParameterName, 'Request''s URL query parameters (ex: -Q token=stuff -Q secret=key)')
-            [CompletionResult]::new('--query', 'query', [CompletionResultType]::ParameterName, 'Request''s URL query parameters (ex: -Q token=stuff -Q secret=key)')
-            [CompletionResult]::new('--dont-scan', 'dont-scan', [CompletionResultType]::ParameterName, 'URL(s) or Regex Pattern(s) to exclude from recursion/scans')
-            [CompletionResult]::new('-S', 'S ', [CompletionResultType]::ParameterName, 'Filter out messages of a particular size (ex: -S 5120 -S 4927,1970)')
-            [CompletionResult]::new('--filter-size', 'filter-size', [CompletionResultType]::ParameterName, 'Filter out messages of a particular size (ex: -S 5120 -S 4927,1970)')
-            [CompletionResult]::new('-X', 'X ', [CompletionResultType]::ParameterName, 'Filter out messages via regular expression matching on the response''s body/headers (ex: -X ''^ignore me$'')')
-            [CompletionResult]::new('--filter-regex', 'filter-regex', [CompletionResultType]::ParameterName, 'Filter out messages via regular expression matching on the response''s body/headers (ex: -X ''^ignore me$'')')
-            [CompletionResult]::new('-W', 'W ', [CompletionResultType]::ParameterName, 'Filter out messages of a particular word count (ex: -W 312 -W 91,82)')
-            [CompletionResult]::new('--filter-words', 'filter-words', [CompletionResultType]::ParameterName, 'Filter out messages of a particular word count (ex: -W 312 -W 91,82)')
-            [CompletionResult]::new('-N', 'N ', [CompletionResultType]::ParameterName, 'Filter out messages of a particular line count (ex: -N 20 -N 31,30)')
-            [CompletionResult]::new('--filter-lines', 'filter-lines', [CompletionResultType]::ParameterName, 'Filter out messages of a particular line count (ex: -N 20 -N 31,30)')
-            [CompletionResult]::new('-C', 'C ', [CompletionResultType]::ParameterName, 'Filter out status codes (deny list) (ex: -C 200 -C 401)')
-            [CompletionResult]::new('--filter-status', 'filter-status', [CompletionResultType]::ParameterName, 'Filter out status codes (deny list) (ex: -C 200 -C 401)')
-            [CompletionResult]::new('--filter-similar-to', 'filter-similar-to', [CompletionResultType]::ParameterName, 'Filter out pages that are similar to the given page (ex. --filter-similar-to http://site.xyz/soft404)')
-            [CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Status Codes to include (allow list) (default: All Status Codes)')
-            [CompletionResult]::new('--status-codes', 'status-codes', [CompletionResultType]::ParameterName, 'Status Codes to include (allow list) (default: All Status Codes)')
-            [CompletionResult]::new('-T', 'T ', [CompletionResultType]::ParameterName, 'Number of seconds before a client''s request times out (default: 7)')
-            [CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Number of seconds before a client''s request times out (default: 7)')
-            [CompletionResult]::new('--server-certs', 'server-certs', [CompletionResultType]::ParameterName, 'Add custom root certificate(s) for servers with unknown certificates')
-            [CompletionResult]::new('--client-cert', 'client-cert', [CompletionResultType]::ParameterName, 'Add a PEM encoded certificate for mutual authentication (mTLS)')
-            [CompletionResult]::new('--client-key', 'client-key', [CompletionResultType]::ParameterName, 'Add a PEM encoded private key for mutual authentication (mTLS)')
-            [CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Number of concurrent threads (default: 50)')
-            [CompletionResult]::new('--threads', 'threads', [CompletionResultType]::ParameterName, 'Number of concurrent threads (default: 50)')
-            [CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)')
-            [CompletionResult]::new('--depth', 'depth', [CompletionResultType]::ParameterName, 'Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)')
-            [CompletionResult]::new('-L', 'L ', [CompletionResultType]::ParameterName, 'Limit total number of concurrent scans (default: 0, i.e. no limit)')
-            [CompletionResult]::new('--scan-limit', 'scan-limit', [CompletionResultType]::ParameterName, 'Limit total number of concurrent scans (default: 0, i.e. no limit)')
-            [CompletionResult]::new('--parallel', 'parallel', [CompletionResultType]::ParameterName, 'Run parallel feroxbuster instances (one child process per url passed via stdin)')
-            [CompletionResult]::new('--rate-limit', 'rate-limit', [CompletionResultType]::ParameterName, 'Limit number of requests per second (per directory) (default: 0, i.e. no limit)')
-            [CompletionResult]::new('--time-limit', 'time-limit', [CompletionResultType]::ParameterName, 'Limit total run time of all scans (ex: --time-limit 10m)')
-            [CompletionResult]::new('-w', 'w', [CompletionResultType]::ParameterName, 'Path or URL of the wordlist')
-            [CompletionResult]::new('--wordlist', 'wordlist', [CompletionResultType]::ParameterName, 'Path or URL of the wordlist')
-            [CompletionResult]::new('-B', 'B ', [CompletionResultType]::ParameterName, 'Automatically request likely backup extensions for "found" urls (default: ~, .bak, .bak2, .old, .1)')
-            [CompletionResult]::new('--collect-backups', 'collect-backups', [CompletionResultType]::ParameterName, 'Automatically request likely backup extensions for "found" urls (default: ~, .bak, .bak2, .old, .1)')
-            [CompletionResult]::new('-I', 'I ', [CompletionResultType]::ParameterName, 'File extension(s) to Ignore while collecting extensions (only used with --collect-extensions)')
-            [CompletionResult]::new('--dont-collect', 'dont-collect', [CompletionResultType]::ParameterName, 'File extension(s) to Ignore while collecting extensions (only used with --collect-extensions)')
-            [CompletionResult]::new('-o', 'o', [CompletionResultType]::ParameterName, 'Output file to write results to (use w/ --json for JSON entries)')
-            [CompletionResult]::new('--output', 'output', [CompletionResultType]::ParameterName, 'Output file to write results to (use w/ --json for JSON entries)')
-            [CompletionResult]::new('--debug-log', 'debug-log', [CompletionResultType]::ParameterName, 'Output file to write log entries (use w/ --json for JSON entries)')
-            [CompletionResult]::new('--stdin', 'stdin', [CompletionResultType]::ParameterName, 'Read url(s) from STDIN')
-            [CompletionResult]::new('--burp', 'burp', [CompletionResultType]::ParameterName, 'Set --proxy to http://127.0.0.1:8080 and set --insecure to true')
-            [CompletionResult]::new('--burp-replay', 'burp-replay', [CompletionResultType]::ParameterName, 'Set --replay-proxy to http://127.0.0.1:8080 and set --insecure to true')
-            [CompletionResult]::new('--smart', 'smart', [CompletionResultType]::ParameterName, 'Set --auto-tune, --collect-words, and --collect-backups to true')
-            [CompletionResult]::new('--thorough', 'thorough', [CompletionResultType]::ParameterName, 'Use the same settings as --smart and set --collect-extensions to true')
-            [CompletionResult]::new('-A', 'A ', [CompletionResultType]::ParameterName, 'Use a random User-Agent')
-            [CompletionResult]::new('--random-agent', 'random-agent', [CompletionResultType]::ParameterName, 'Use a random User-Agent')
-            [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Append / to each request''s URL')
-            [CompletionResult]::new('--add-slash', 'add-slash', [CompletionResultType]::ParameterName, 'Append / to each request''s URL')
-            [CompletionResult]::new('-r', 'r', [CompletionResultType]::ParameterName, 'Allow client to follow redirects')
-            [CompletionResult]::new('--redirects', 'redirects', [CompletionResultType]::ParameterName, 'Allow client to follow redirects')
-            [CompletionResult]::new('-k', 'k', [CompletionResultType]::ParameterName, 'Disables TLS certificate validation in the client')
-            [CompletionResult]::new('--insecure', 'insecure', [CompletionResultType]::ParameterName, 'Disables TLS certificate validation in the client')
-            [CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'Do not scan recursively')
-            [CompletionResult]::new('--no-recursion', 'no-recursion', [CompletionResultType]::ParameterName, 'Do not scan recursively')
-            [CompletionResult]::new('--force-recursion', 'force-recursion', [CompletionResultType]::ParameterName, 'Force recursion attempts on all ''found'' endpoints (still respects recursion depth)')
-            [CompletionResult]::new('-e', 'e', [CompletionResultType]::ParameterName, 'Extract links from response body (html, javascript, etc...); make new requests based on findings (default: true)')
-            [CompletionResult]::new('--extract-links', 'extract-links', [CompletionResultType]::ParameterName, 'Extract links from response body (html, javascript, etc...); make new requests based on findings (default: true)')
-            [CompletionResult]::new('--dont-extract-links', 'dont-extract-links', [CompletionResultType]::ParameterName, 'Don''t extract links from response body (html, javascript, etc...)')
-            [CompletionResult]::new('--auto-tune', 'auto-tune', [CompletionResultType]::ParameterName, 'Automatically lower scan rate when an excessive amount of errors are encountered')
-            [CompletionResult]::new('--auto-bail', 'auto-bail', [CompletionResultType]::ParameterName, 'Automatically stop scanning when an excessive amount of errors are encountered')
-            [CompletionResult]::new('-D', 'D ', [CompletionResultType]::ParameterName, 'Don''t auto-filter wildcard responses')
-            [CompletionResult]::new('--dont-filter', 'dont-filter', [CompletionResultType]::ParameterName, 'Don''t auto-filter wildcard responses')
-            [CompletionResult]::new('-E', 'E ', [CompletionResultType]::ParameterName, 'Automatically discover extensions and add them to --extensions (unless they''re in --dont-collect)')
-            [CompletionResult]::new('--collect-extensions', 'collect-extensions', [CompletionResultType]::ParameterName, 'Automatically discover extensions and add them to --extensions (unless they''re in --dont-collect)')
-            [CompletionResult]::new('-g', 'g', [CompletionResultType]::ParameterName, 'Automatically discover important words from within responses and add them to the wordlist')
-            [CompletionResult]::new('--collect-words', 'collect-words', [CompletionResultType]::ParameterName, 'Automatically discover important words from within responses and add them to the wordlist')
-            [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Increase verbosity level (use -vv or more for greater effect. [CAUTION] 4 -v''s is probably too much)')
-            [CompletionResult]::new('--verbosity', 'verbosity', [CompletionResultType]::ParameterName, 'Increase verbosity level (use -vv or more for greater effect. [CAUTION] 4 -v''s is probably too much)')
-            [CompletionResult]::new('--silent', 'silent', [CompletionResultType]::ParameterName, 'Only print URLs (or JSON w/ --json) + turn off logging (good for piping a list of urls to other commands)')
-            [CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Hide progress bars and banner (good for tmux windows w/ notifications)')
-            [CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Hide progress bars and banner (good for tmux windows w/ notifications)')
-            [CompletionResult]::new('--json', 'json', [CompletionResultType]::ParameterName, 'Emit JSON logs to --output and --debug-log instead of normal text')
-            [CompletionResult]::new('--no-state', 'no-state', [CompletionResultType]::ParameterName, 'Disable state output file (*.state)')
-            [CompletionResult]::new('-U', 'U ', [CompletionResultType]::ParameterName, 'Update feroxbuster to the latest version')
-            [CompletionResult]::new('--update', 'update', [CompletionResultType]::ParameterName, 'Update feroxbuster to the latest version')
-            [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
-            [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
-            [CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
-            [CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
+            [CompletionResult]::new('-u', '-u', [CompletionResultType]::ParameterName, 'The target URL (required, unless [--stdin || --resume-from || --request-file] used)')
+            [CompletionResult]::new('--url', '--url', [CompletionResultType]::ParameterName, 'The target URL (required, unless [--stdin || --resume-from || --request-file] used)')
+            [CompletionResult]::new('--resume-from', '--resume-from', [CompletionResultType]::ParameterName, 'State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state)')
+            [CompletionResult]::new('--request-file', '--request-file', [CompletionResultType]::ParameterName, 'Raw HTTP request file to use as a template for all requests')
+            [CompletionResult]::new('-p', '-p', [CompletionResultType]::ParameterName, 'Proxy to use for requests (ex: http(s)://host:port, socks5(h)://host:port)')
+            [CompletionResult]::new('--proxy', '--proxy', [CompletionResultType]::ParameterName, 'Proxy to use for requests (ex: http(s)://host:port, socks5(h)://host:port)')
+            [CompletionResult]::new('-P', '-P ', [CompletionResultType]::ParameterName, 'Send only unfiltered requests through a Replay Proxy, instead of all requests')
+            [CompletionResult]::new('--replay-proxy', '--replay-proxy', [CompletionResultType]::ParameterName, 'Send only unfiltered requests through a Replay Proxy, instead of all requests')
+            [CompletionResult]::new('-R', '-R ', [CompletionResultType]::ParameterName, 'Status Codes to send through a Replay Proxy when found (default: --status-codes value)')
+            [CompletionResult]::new('--replay-codes', '--replay-codes', [CompletionResultType]::ParameterName, 'Status Codes to send through a Replay Proxy when found (default: --status-codes value)')
+            [CompletionResult]::new('-a', '-a', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/2.11.0)')
+            [CompletionResult]::new('--user-agent', '--user-agent', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/2.11.0)')
+            [CompletionResult]::new('-x', '-x', [CompletionResultType]::ParameterName, 'File extension(s) to search for (ex: -x php -x pdf js); reads values (newline-separated) from file if input starts with an @ (ex: @ext.txt)')
+            [CompletionResult]::new('--extensions', '--extensions', [CompletionResultType]::ParameterName, 'File extension(s) to search for (ex: -x php -x pdf js); reads values (newline-separated) from file if input starts with an @ (ex: @ext.txt)')
+            [CompletionResult]::new('-m', '-m', [CompletionResultType]::ParameterName, 'Which HTTP request method(s) should be sent (default: GET)')
+            [CompletionResult]::new('--methods', '--methods', [CompletionResultType]::ParameterName, 'Which HTTP request method(s) should be sent (default: GET)')
+            [CompletionResult]::new('--data', '--data', [CompletionResultType]::ParameterName, 'Request''s Body; can read data from a file if input starts with an @ (ex: @post.bin)')
+            [CompletionResult]::new('-H', '-H ', [CompletionResultType]::ParameterName, 'Specify HTTP headers to be used in each request (ex: -H Header:val -H ''stuff: things'')')
+            [CompletionResult]::new('--headers', '--headers', [CompletionResultType]::ParameterName, 'Specify HTTP headers to be used in each request (ex: -H Header:val -H ''stuff: things'')')
+            [CompletionResult]::new('-b', '-b', [CompletionResultType]::ParameterName, 'Specify HTTP cookies to be used in each request (ex: -b stuff=things)')
+            [CompletionResult]::new('--cookies', '--cookies', [CompletionResultType]::ParameterName, 'Specify HTTP cookies to be used in each request (ex: -b stuff=things)')
+            [CompletionResult]::new('-Q', '-Q ', [CompletionResultType]::ParameterName, 'Request''s URL query parameters (ex: -Q token=stuff -Q secret=key)')
+            [CompletionResult]::new('--query', '--query', [CompletionResultType]::ParameterName, 'Request''s URL query parameters (ex: -Q token=stuff -Q secret=key)')
+            [CompletionResult]::new('--protocol', '--protocol', [CompletionResultType]::ParameterName, 'Specify the protocol to use when targeting via --request-file or --url with domain only (default: https)')
+            [CompletionResult]::new('--dont-scan', '--dont-scan', [CompletionResultType]::ParameterName, 'URL(s) or Regex Pattern(s) to exclude from recursion/scans')
+            [CompletionResult]::new('-S', '-S ', [CompletionResultType]::ParameterName, 'Filter out messages of a particular size (ex: -S 5120 -S 4927,1970)')
+            [CompletionResult]::new('--filter-size', '--filter-size', [CompletionResultType]::ParameterName, 'Filter out messages of a particular size (ex: -S 5120 -S 4927,1970)')
+            [CompletionResult]::new('-X', '-X ', [CompletionResultType]::ParameterName, 'Filter out messages via regular expression matching on the response''s body/headers (ex: -X ''^ignore me$'')')
+            [CompletionResult]::new('--filter-regex', '--filter-regex', [CompletionResultType]::ParameterName, 'Filter out messages via regular expression matching on the response''s body/headers (ex: -X ''^ignore me$'')')
+            [CompletionResult]::new('-W', '-W ', [CompletionResultType]::ParameterName, 'Filter out messages of a particular word count (ex: -W 312 -W 91,82)')
+            [CompletionResult]::new('--filter-words', '--filter-words', [CompletionResultType]::ParameterName, 'Filter out messages of a particular word count (ex: -W 312 -W 91,82)')
+            [CompletionResult]::new('-N', '-N ', [CompletionResultType]::ParameterName, 'Filter out messages of a particular line count (ex: -N 20 -N 31,30)')
+            [CompletionResult]::new('--filter-lines', '--filter-lines', [CompletionResultType]::ParameterName, 'Filter out messages of a particular line count (ex: -N 20 -N 31,30)')
+            [CompletionResult]::new('-C', '-C ', [CompletionResultType]::ParameterName, 'Filter out status codes (deny list) (ex: -C 200 -C 401)')
+            [CompletionResult]::new('--filter-status', '--filter-status', [CompletionResultType]::ParameterName, 'Filter out status codes (deny list) (ex: -C 200 -C 401)')
+            [CompletionResult]::new('--filter-similar-to', '--filter-similar-to', [CompletionResultType]::ParameterName, 'Filter out pages that are similar to the given page (ex. --filter-similar-to http://site.xyz/soft404)')
+            [CompletionResult]::new('-s', '-s', [CompletionResultType]::ParameterName, 'Status Codes to include (allow list) (default: All Status Codes)')
+            [CompletionResult]::new('--status-codes', '--status-codes', [CompletionResultType]::ParameterName, 'Status Codes to include (allow list) (default: All Status Codes)')
+            [CompletionResult]::new('-T', '-T ', [CompletionResultType]::ParameterName, 'Number of seconds before a client''s request times out (default: 7)')
+            [CompletionResult]::new('--timeout', '--timeout', [CompletionResultType]::ParameterName, 'Number of seconds before a client''s request times out (default: 7)')
+            [CompletionResult]::new('--server-certs', '--server-certs', [CompletionResultType]::ParameterName, 'Add custom root certificate(s) for servers with unknown certificates')
+            [CompletionResult]::new('--client-cert', '--client-cert', [CompletionResultType]::ParameterName, 'Add a PEM encoded certificate for mutual authentication (mTLS)')
+            [CompletionResult]::new('--client-key', '--client-key', [CompletionResultType]::ParameterName, 'Add a PEM encoded private key for mutual authentication (mTLS)')
+            [CompletionResult]::new('-t', '-t', [CompletionResultType]::ParameterName, 'Number of concurrent threads (default: 50)')
+            [CompletionResult]::new('--threads', '--threads', [CompletionResultType]::ParameterName, 'Number of concurrent threads (default: 50)')
+            [CompletionResult]::new('-d', '-d', [CompletionResultType]::ParameterName, 'Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)')
+            [CompletionResult]::new('--depth', '--depth', [CompletionResultType]::ParameterName, 'Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)')
+            [CompletionResult]::new('-L', '-L ', [CompletionResultType]::ParameterName, 'Limit total number of concurrent scans (default: 0, i.e. no limit)')
+            [CompletionResult]::new('--scan-limit', '--scan-limit', [CompletionResultType]::ParameterName, 'Limit total number of concurrent scans (default: 0, i.e. no limit)')
+            [CompletionResult]::new('--parallel', '--parallel', [CompletionResultType]::ParameterName, 'Run parallel feroxbuster instances (one child process per url passed via stdin)')
+            [CompletionResult]::new('--rate-limit', '--rate-limit', [CompletionResultType]::ParameterName, 'Limit number of requests per second (per directory) (default: 0, i.e. no limit)')
+            [CompletionResult]::new('--time-limit', '--time-limit', [CompletionResultType]::ParameterName, 'Limit total run time of all scans (ex: --time-limit 10m)')
+            [CompletionResult]::new('-w', '-w', [CompletionResultType]::ParameterName, 'Path or URL of the wordlist')
+            [CompletionResult]::new('--wordlist', '--wordlist', [CompletionResultType]::ParameterName, 'Path or URL of the wordlist')
+            [CompletionResult]::new('-B', '-B ', [CompletionResultType]::ParameterName, 'Automatically request likely backup extensions for "found" urls (default: ~, .bak, .bak2, .old, .1)')
+            [CompletionResult]::new('--collect-backups', '--collect-backups', [CompletionResultType]::ParameterName, 'Automatically request likely backup extensions for "found" urls (default: ~, .bak, .bak2, .old, .1)')
+            [CompletionResult]::new('-I', '-I ', [CompletionResultType]::ParameterName, 'File extension(s) to Ignore while collecting extensions (only used with --collect-extensions)')
+            [CompletionResult]::new('--dont-collect', '--dont-collect', [CompletionResultType]::ParameterName, 'File extension(s) to Ignore while collecting extensions (only used with --collect-extensions)')
+            [CompletionResult]::new('-o', '-o', [CompletionResultType]::ParameterName, 'Output file to write results to (use w/ --json for JSON entries)')
+            [CompletionResult]::new('--output', '--output', [CompletionResultType]::ParameterName, 'Output file to write results to (use w/ --json for JSON entries)')
+            [CompletionResult]::new('--debug-log', '--debug-log', [CompletionResultType]::ParameterName, 'Output file to write log entries (use w/ --json for JSON entries)')
+            [CompletionResult]::new('--limit-bars', '--limit-bars', [CompletionResultType]::ParameterName, 'Number of directory scan bars to show at any given time (default: no limit)')
+            [CompletionResult]::new('--stdin', '--stdin', [CompletionResultType]::ParameterName, 'Read url(s) from STDIN')
+            [CompletionResult]::new('--burp', '--burp', [CompletionResultType]::ParameterName, 'Set --proxy to http://127.0.0.1:8080 and set --insecure to true')
+            [CompletionResult]::new('--burp-replay', '--burp-replay', [CompletionResultType]::ParameterName, 'Set --replay-proxy to http://127.0.0.1:8080 and set --insecure to true')
+            [CompletionResult]::new('--smart', '--smart', [CompletionResultType]::ParameterName, 'Set --auto-tune, --collect-words, and --collect-backups to true')
+            [CompletionResult]::new('--thorough', '--thorough', [CompletionResultType]::ParameterName, 'Use the same settings as --smart and set --collect-extensions and --scan-dir-listings to true')
+            [CompletionResult]::new('-A', '-A ', [CompletionResultType]::ParameterName, 'Use a random User-Agent')
+            [CompletionResult]::new('--random-agent', '--random-agent', [CompletionResultType]::ParameterName, 'Use a random User-Agent')
+            [CompletionResult]::new('-f', '-f', [CompletionResultType]::ParameterName, 'Append / to each request''s URL')
+            [CompletionResult]::new('--add-slash', '--add-slash', [CompletionResultType]::ParameterName, 'Append / to each request''s URL')
+            [CompletionResult]::new('-r', '-r', [CompletionResultType]::ParameterName, 'Allow client to follow redirects')
+            [CompletionResult]::new('--redirects', '--redirects', [CompletionResultType]::ParameterName, 'Allow client to follow redirects')
+            [CompletionResult]::new('-k', '-k', [CompletionResultType]::ParameterName, 'Disables TLS certificate validation in the client')
+            [CompletionResult]::new('--insecure', '--insecure', [CompletionResultType]::ParameterName, 'Disables TLS certificate validation in the client')
+            [CompletionResult]::new('-n', '-n', [CompletionResultType]::ParameterName, 'Do not scan recursively')
+            [CompletionResult]::new('--no-recursion', '--no-recursion', [CompletionResultType]::ParameterName, 'Do not scan recursively')
+            [CompletionResult]::new('--force-recursion', '--force-recursion', [CompletionResultType]::ParameterName, 'Force recursion attempts on all ''found'' endpoints (still respects recursion depth)')
+            [CompletionResult]::new('-e', '-e', [CompletionResultType]::ParameterName, 'Extract links from response body (html, javascript, etc...); make new requests based on findings (default: true)')
+            [CompletionResult]::new('--extract-links', '--extract-links', [CompletionResultType]::ParameterName, 'Extract links from response body (html, javascript, etc...); make new requests based on findings (default: true)')
+            [CompletionResult]::new('--dont-extract-links', '--dont-extract-links', [CompletionResultType]::ParameterName, 'Don''t extract links from response body (html, javascript, etc...)')
+            [CompletionResult]::new('--auto-tune', '--auto-tune', [CompletionResultType]::ParameterName, 'Automatically lower scan rate when an excessive amount of errors are encountered')
+            [CompletionResult]::new('--auto-bail', '--auto-bail', [CompletionResultType]::ParameterName, 'Automatically stop scanning when an excessive amount of errors are encountered')
+            [CompletionResult]::new('-D', '-D ', [CompletionResultType]::ParameterName, 'Don''t auto-filter wildcard responses')
+            [CompletionResult]::new('--dont-filter', '--dont-filter', [CompletionResultType]::ParameterName, 'Don''t auto-filter wildcard responses')
+            [CompletionResult]::new('-E', '-E ', [CompletionResultType]::ParameterName, 'Automatically discover extensions and add them to --extensions (unless they''re in --dont-collect)')
+            [CompletionResult]::new('--collect-extensions', '--collect-extensions', [CompletionResultType]::ParameterName, 'Automatically discover extensions and add them to --extensions (unless they''re in --dont-collect)')
+            [CompletionResult]::new('-g', '-g', [CompletionResultType]::ParameterName, 'Automatically discover important words from within responses and add them to the wordlist')
+            [CompletionResult]::new('--collect-words', '--collect-words', [CompletionResultType]::ParameterName, 'Automatically discover important words from within responses and add them to the wordlist')
+            [CompletionResult]::new('--scan-dir-listings', '--scan-dir-listings', [CompletionResultType]::ParameterName, 'Force scans to recurse into directory listings')
+            [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'Increase verbosity level (use -vv or more for greater effect. [CAUTION] 4 -v''s is probably too much)')
+            [CompletionResult]::new('--verbosity', '--verbosity', [CompletionResultType]::ParameterName, 'Increase verbosity level (use -vv or more for greater effect. [CAUTION] 4 -v''s is probably too much)')
+            [CompletionResult]::new('--silent', '--silent', [CompletionResultType]::ParameterName, 'Only print URLs (or JSON w/ --json) + turn off logging (good for piping a list of urls to other commands)')
+            [CompletionResult]::new('-q', '-q', [CompletionResultType]::ParameterName, 'Hide progress bars and banner (good for tmux windows w/ notifications)')
+            [CompletionResult]::new('--quiet', '--quiet', [CompletionResultType]::ParameterName, 'Hide progress bars and banner (good for tmux windows w/ notifications)')
+            [CompletionResult]::new('--json', '--json', [CompletionResultType]::ParameterName, 'Emit JSON logs to --output and --debug-log instead of normal text')
+            [CompletionResult]::new('--no-state', '--no-state', [CompletionResultType]::ParameterName, 'Disable state output file (*.state)')
+            [CompletionResult]::new('-U', '-U ', [CompletionResultType]::ParameterName, 'Update feroxbuster to the latest version')
+            [CompletionResult]::new('--update', '--update', [CompletionResultType]::ParameterName, 'Update feroxbuster to the latest version')
+            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
+            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
+            [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')
+            [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version')
             break
         }
     })
diff --git a/shell_completions/feroxbuster.bash b/shell_completions/feroxbuster.bash
index 45f7b489..04c95065 100644
--- a/shell_completions/feroxbuster.bash
+++ b/shell_completions/feroxbuster.bash
@@ -19,7 +19,7 @@ _feroxbuster() {
 
     case "${cmd}" in
         feroxbuster)
-            opts="-u -p -P -R -a -A -x -m -H -b -Q -f -S -X -W -N -C -s -T -r -k -t -n -d -e -L -w -D -E -B -g -I -v -q -o -U -h -V --url --stdin --resume-from --burp --burp-replay --smart --thorough --proxy --replay-proxy --replay-codes --user-agent --random-agent --extensions --methods --data --headers --cookies --query --add-slash --dont-scan --filter-size --filter-regex --filter-words --filter-lines --filter-status --filter-similar-to --status-codes --timeout --redirects --insecure --server-certs --client-cert --client-key --threads --no-recursion --depth --force-recursion --extract-links --dont-extract-links --scan-limit --parallel --rate-limit --time-limit --wordlist --auto-tune --auto-bail --dont-filter --collect-extensions --collect-backups --collect-words --dont-collect --verbosity --silent --quiet --json --output --debug-log --no-state --update --help --version"
+            opts="-u -p -P -R -a -A -x -m -H -b -Q -f -S -X -W -N -C -s -T -r -k -t -n -d -e -L -w -D -E -B -g -I -v -q -o -U -h -V --url --stdin --resume-from --request-file --burp --burp-replay --smart --thorough --proxy --replay-proxy --replay-codes --user-agent --random-agent --extensions --methods --data --headers --cookies --query --add-slash --protocol --dont-scan --filter-size --filter-regex --filter-words --filter-lines --filter-status --filter-similar-to --status-codes --timeout --redirects --insecure --server-certs --client-cert --client-key --threads --no-recursion --depth --force-recursion --extract-links --dont-extract-links --scan-limit --parallel --rate-limit --time-limit --wordlist --auto-tune --auto-bail --dont-filter --collect-extensions --collect-backups --collect-words --dont-collect --scan-dir-listings --verbosity --silent --quiet --json --output --debug-log --no-state --limit-bars --update --help --version"
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -48,6 +48,21 @@ _feroxbuster() {
                     fi
                     return 0
                     ;;
+                --request-file)
+                    local oldifs
+                    if [ -n "${IFS+x}" ]; then
+                        oldifs="$IFS"
+                    fi
+                    IFS=$'\n'
+                    COMPREPLY=($(compgen -f "${cur}"))
+                    if [ -n "${oldifs+x}" ]; then
+                        IFS="$oldifs"
+                    fi
+                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
+                        compopt -o filenames
+                    fi
+                    return 0
+                    ;;
                 --proxy)
                     COMPREPLY=($(compgen -f "${cur}"))
                     return 0
@@ -124,6 +139,10 @@ _feroxbuster() {
                     COMPREPLY=($(compgen -f "${cur}"))
                     return 0
                     ;;
+                --protocol)
+                    COMPREPLY=($(compgen -f "${cur}"))
+                    return 0
+                    ;;
                 --dont-scan)
                     COMPREPLY=($(compgen -f "${cur}"))
                     return 0
@@ -360,6 +379,10 @@ _feroxbuster() {
                     fi
                     return 0
                     ;;
+                --limit-bars)
+                    COMPREPLY=($(compgen -f "${cur}"))
+                    return 0
+                    ;;
                 *)
                     COMPREPLY=()
                     ;;
diff --git a/shell_completions/feroxbuster.elv b/shell_completions/feroxbuster.elv
index c3daeaf3..7c452c71 100644
--- a/shell_completions/feroxbuster.elv
+++ b/shell_completions/feroxbuster.elv
@@ -18,17 +18,18 @@ set edit:completion:arg-completer[feroxbuster] = {|@words|
     }
     var completions = [
         &'feroxbuster'= {
-            cand -u 'The target URL (required, unless [--stdin || --resume-from] used)'
-            cand --url 'The target URL (required, unless [--stdin || --resume-from] used)'
+            cand -u 'The target URL (required, unless [--stdin || --resume-from || --request-file] used)'
+            cand --url 'The target URL (required, unless [--stdin || --resume-from || --request-file] used)'
             cand --resume-from 'State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state)'
+            cand --request-file 'Raw HTTP request file to use as a template for all requests'
             cand -p 'Proxy to use for requests (ex: http(s)://host:port, socks5(h)://host:port)'
             cand --proxy 'Proxy to use for requests (ex: http(s)://host:port, socks5(h)://host:port)'
             cand -P 'Send only unfiltered requests through a Replay Proxy, instead of all requests'
             cand --replay-proxy 'Send only unfiltered requests through a Replay Proxy, instead of all requests'
             cand -R 'Status Codes to send through a Replay Proxy when found (default: --status-codes value)'
             cand --replay-codes 'Status Codes to send through a Replay Proxy when found (default: --status-codes value)'
-            cand -a 'Sets the User-Agent (default: feroxbuster/2.10.4)'
-            cand --user-agent 'Sets the User-Agent (default: feroxbuster/2.10.4)'
+            cand -a 'Sets the User-Agent (default: feroxbuster/2.11.0)'
+            cand --user-agent 'Sets the User-Agent (default: feroxbuster/2.11.0)'
             cand -x 'File extension(s) to search for (ex: -x php -x pdf js); reads values (newline-separated) from file if input starts with an @ (ex: @ext.txt)'
             cand --extensions 'File extension(s) to search for (ex: -x php -x pdf js); reads values (newline-separated) from file if input starts with an @ (ex: @ext.txt)'
             cand -m 'Which HTTP request method(s) should be sent (default: GET)'
@@ -40,6 +41,7 @@ set edit:completion:arg-completer[feroxbuster] = {|@words|
             cand --cookies 'Specify HTTP cookies to be used in each request (ex: -b stuff=things)'
             cand -Q 'Request''s URL query parameters (ex: -Q token=stuff -Q secret=key)'
             cand --query 'Request''s URL query parameters (ex: -Q token=stuff -Q secret=key)'
+            cand --protocol 'Specify the protocol to use when targeting via --request-file or --url with domain only (default: https)'
             cand --dont-scan 'URL(s) or Regex Pattern(s) to exclude from recursion/scans'
             cand -S 'Filter out messages of a particular size (ex: -S 5120 -S 4927,1970)'
             cand --filter-size 'Filter out messages of a particular size (ex: -S 5120 -S 4927,1970)'
@@ -77,11 +79,12 @@ set edit:completion:arg-completer[feroxbuster] = {|@words|
             cand -o 'Output file to write results to (use w/ --json for JSON entries)'
             cand --output 'Output file to write results to (use w/ --json for JSON entries)'
             cand --debug-log 'Output file to write log entries (use w/ --json for JSON entries)'
+            cand --limit-bars 'Number of directory scan bars to show at any given time (default: no limit)'
             cand --stdin 'Read url(s) from STDIN'
             cand --burp 'Set --proxy to http://127.0.0.1:8080 and set --insecure to true'
             cand --burp-replay 'Set --replay-proxy to http://127.0.0.1:8080 and set --insecure to true'
             cand --smart 'Set --auto-tune, --collect-words, and --collect-backups to true'
-            cand --thorough 'Use the same settings as --smart and set --collect-extensions to true'
+            cand --thorough 'Use the same settings as --smart and set --collect-extensions and --scan-dir-listings to true'
             cand -A 'Use a random User-Agent'
             cand --random-agent 'Use a random User-Agent'
             cand -f 'Append / to each request''s URL'
@@ -104,6 +107,7 @@ set edit:completion:arg-completer[feroxbuster] = {|@words|
             cand --collect-extensions 'Automatically discover extensions and add them to --extensions (unless they''re in --dont-collect)'
             cand -g 'Automatically discover important words from within responses and add them to the wordlist'
             cand --collect-words 'Automatically discover important words from within responses and add them to the wordlist'
+            cand --scan-dir-listings 'Force scans to recurse into directory listings'
             cand -v 'Increase verbosity level (use -vv or more for greater effect. [CAUTION] 4 -v''s is probably too much)'
             cand --verbosity 'Increase verbosity level (use -vv or more for greater effect. [CAUTION] 4 -v''s is probably too much)'
             cand --silent 'Only print URLs (or JSON w/ --json) + turn off logging (good for piping a list of urls to other commands)'
diff --git a/src/banner/container.rs b/src/banner/container.rs
index a7fe5643..a427f3de 100644
--- a/src/banner/container.rs
+++ b/src/banner/container.rs
@@ -176,6 +176,15 @@ pub struct Banner {
 
     /// represents Configuration.collect_words
     force_recursion: BannerEntry,
+
+    /// represents Configuration.protocol
+    protocol: BannerEntry,
+
+    /// represents Configuration.scan_dir_listings
+    scan_dir_listings: BannerEntry,
+
+    /// represents Configuration.limit_bars
+    limit_bars: BannerEntry,
 }
 
 /// implementation of Banner
@@ -320,6 +329,12 @@ impl Banner {
             BannerEntry::new("🚫", "Do Not Recurse", &config.no_recursion.to_string())
         };
 
+        let protocol = if config.protocol.to_lowercase() == "http" {
+            BannerEntry::new("🔓", "Default Protocol", &config.protocol)
+        } else {
+            BannerEntry::new("🔒", "Default Protocol", &config.protocol)
+        };
+
         let scan_limit = BannerEntry::new(
             "🦥",
             "Concurrent Scan Limit",
@@ -331,6 +346,11 @@ impl Banner {
         let replay_proxy = BannerEntry::new("🎥", "Replay Proxy", &config.replay_proxy);
         let auto_tune = BannerEntry::new("🎶", "Auto Tune", &config.auto_tune.to_string());
         let auto_bail = BannerEntry::new("🙅", "Auto Bail", &config.auto_bail.to_string());
+        let scan_dir_listings = BannerEntry::new(
+            "📂",
+            "Scan Dir Listings",
+            &config.scan_dir_listings.to_string(),
+        );
         let cfg = BannerEntry::new("💉", "Config File", &config.config);
         let proxy = BannerEntry::new("💎", "Proxy", &config.proxy);
         let server_certs = BannerEntry::new(
@@ -341,6 +361,8 @@ impl Banner {
         let client_cert = BannerEntry::new("🏅", "Client Certificate", &config.client_cert);
         let client_key = BannerEntry::new("🔑", "Client Key", &config.client_key);
         let threads = BannerEntry::new("🚀", "Threads", &config.threads.to_string());
+        let limit_bars =
+            BannerEntry::new("📊", "Limit Dir Scan Bars", &config.limit_bars.to_string());
         let wordlist = BannerEntry::new("📖", "Wordlist", &config.wordlist);
         let timeout = BannerEntry::new("💥", "Timeout (secs)", &config.timeout.to_string());
         let user_agent = BannerEntry::new("🦡", "User-Agent", &config.user_agent);
@@ -455,6 +477,9 @@ impl Banner {
             collect_words,
             dont_collect,
             config: cfg,
+            scan_dir_listings,
+            protocol,
+            limit_bars,
             version: VERSION.to_string(),
             update_status: UpdateStatus::Unknown,
         }
@@ -595,6 +620,14 @@ by Ben "epi" Risher {}                 ver: {}"#,
         }
 
         // followed by the maybe printed or variably displayed values
+        if !config.request_file.is_empty() || !config.target_url.starts_with("http") {
+            writeln!(&mut writer, "{}", self.protocol)?;
+        }
+
+        if config.limit_bars > 0 {
+            writeln!(&mut writer, "{}", self.limit_bars)?;
+        }
+
         if !config.config.is_empty() {
             writeln!(&mut writer, "{}", self.config)?;
         }
@@ -662,6 +695,10 @@ by Ben "epi" Risher {}                 ver: {}"#,
             writeln!(&mut writer, "{}", self.output)?;
         }
 
+        if config.scan_dir_listings {
+            writeln!(&mut writer, "{}", self.scan_dir_listings)?;
+        }
+
         if !config.debug_log.is_empty() {
             writeln!(&mut writer, "{}", self.debug_log)?;
         }
diff --git a/src/config/container.rs b/src/config/container.rs
index 7bf8eb75..20ab4bd3 100644
--- a/src/config/container.rs
+++ b/src/config/container.rs
@@ -1,15 +1,16 @@
 use super::utils::{
-    backup_extensions, depth, extract_links, ignored_extensions, methods, report_and_exit,
-    save_state, serialized_type, status_codes, threads, timeout, user_agent, wordlist, OutputLevel,
+    backup_extensions, depth, determine_requester_policy, extract_links, ignored_extensions,
+    methods, parse_request_file, report_and_exit, request_protocol, save_state, serialized_type,
+    split_header, split_query, status_codes, threads, timeout, user_agent, wordlist, OutputLevel,
     RequesterPolicy,
 };
+
 use crate::config::determine_output_level;
-use crate::config::utils::determine_requester_policy;
 use crate::{
     client, parser,
     scan_manager::resume_scan,
     traits::FeroxSerialize,
-    utils::{fmt_err, parse_url_with_raw_path},
+    utils::{fmt_err, module_colorizer, parse_url_with_raw_path, status_colorizer},
     DEFAULT_CONFIG_NAME,
 };
 use anyhow::{anyhow, Context, Result};
@@ -332,6 +333,22 @@ pub struct Configuration {
     /// Auto update app feature
     #[serde(skip)]
     pub update_app: bool,
+
+    /// whether to recurse into directory listings or not
+    #[serde(default)]
+    pub scan_dir_listings: bool,
+
+    /// path to a raw request file generated by burp or similar
+    #[serde(skip)]
+    pub request_file: String,
+
+    /// default request protocol
+    #[serde(default = "request_protocol")]
+    pub protocol: String,
+
+    /// number of directory scan bars to show at any given time, 0 is no limit
+    #[serde(default)]
+    pub limit_bars: usize,
 }
 
 impl Default for Configuration {
@@ -378,10 +395,12 @@ impl Default for Configuration {
             resumed: false,
             stdin: false,
             json: false,
+            scan_dir_listings: false,
             verbosity: 0,
             scan_limit: 0,
             parallel: 0,
             rate_limit: 0,
+            limit_bars: 0,
             add_slash: false,
             insecure: false,
             redirects: false,
@@ -403,6 +422,8 @@ impl Default for Configuration {
             time_limit: String::new(),
             resume_from: String::new(),
             replay_proxy: String::new(),
+            request_file: String::new(),
+            protocol: request_protocol(),
             server_certs: Vec::new(),
             queries: Vec::new(),
             extensions: Vec::new(),
@@ -476,12 +497,16 @@ impl Configuration {
     /// - **depth**: `4` (maximum recursion depth)
     /// - **force_recursion**: `false` (still respects recursion depth)
     /// - **scan_limit**: `0` (no limit on concurrent scans imposed)
+    /// - **limit_bars**: `0` (no limit on number of directory scan bars shown)
     /// - **parallel**: `0` (no limit on parallel scans imposed)
     /// - **rate_limit**: `0` (no limit on requests per second imposed)
     /// - **time_limit**: `None` (no limit on length of scan imposed)
     /// - **replay_proxy**: `None` (no limit on concurrent scans imposed)
     /// - **replay_codes**: [`DEFAULT_RESPONSE_CODES`](constant.DEFAULT_RESPONSE_CODES.html)
     /// - **update_app**: `false`
+    /// - **scan_dir_listings**: `false`
+    /// - **request_file**: `None`
+    /// - **protocol**: `https`
     ///
     /// After which, any values defined in a
     /// [ferox-config.toml](constant.DEFAULT_CONFIG_NAME.html) config file will override the
@@ -555,6 +580,18 @@ impl Configuration {
         // merge the cli options into the config file options and return the result
         Self::merge_config(&mut config, cli_config);
 
+        // if the user provided a raw request file as the target, we'll need to parse out
+        // the provided info and update the config with those values. This call needs to
+        // come after the cli/config merge so we can allow the cli options to override
+        // the raw request values (i.e. --headers "stuff: things" should override a "stuff"
+        // header from the raw request).
+        //
+        // Additionally, this call needs to come before client rebuild so that the things
+        // like user-agent can be set at the client level instead of the header level.
+        if !config.request_file.is_empty() {
+            parse_request_file(&mut config)?;
+        }
+
         // rebuild clients is the last step in either code branch
         Self::try_rebuild_clients(&mut config);
 
@@ -614,10 +651,13 @@ impl Configuration {
         update_config_with_num_type_if_present!(&mut config.depth, args, "depth", usize);
         update_config_with_num_type_if_present!(&mut config.scan_limit, args, "scan_limit", usize);
         update_config_with_num_type_if_present!(&mut config.rate_limit, args, "rate_limit", usize);
+        update_config_with_num_type_if_present!(&mut config.limit_bars, args, "limit_bars", usize);
         update_config_if_present!(&mut config.wordlist, args, "wordlist", String);
         update_config_if_present!(&mut config.output, args, "output", String);
         update_config_if_present!(&mut config.debug_log, args, "debug_log", String);
         update_config_if_present!(&mut config.resume_from, args, "resume_from", String);
+        update_config_if_present!(&mut config.request_file, args, "request_file", String);
+        update_config_if_present!(&mut config.protocol, args, "protocol", String);
 
         if let Ok(Some(inner)) = args.try_get_one::<String>("time_limit") {
             inner.clone_into(&mut config.time_limit);
@@ -831,6 +871,10 @@ impl Configuration {
             config.save_state = false;
         }
 
+        if came_from_cli!(args, "scan_dir_listings") || came_from_cli!(args, "thorough") {
+            config.scan_dir_listings = true;
+        }
+
         if came_from_cli!(args, "dont_filter") {
             config.dont_filter = true;
         }
@@ -871,6 +915,25 @@ impl Configuration {
             // occurrences_of returns 0 if none are found; this is protected in
             // an if block for the same reason as the quiet option
             config.verbosity = args.get_count("verbosity");
+
+            // todo: starting on 2.11.0 (907-dont-skip-dir-listings), trace-level
+            //   logging started causing the following error:
+            //
+            // thread 'tokio-runtime-worker' has overflowed its stack
+            // fatal runtime error: stack overflow
+            // Aborted (core dumped)
+            //
+            // as a temporary fix, we'll disable trace logging to prevent the stack
+            // overflow until I get time to investigate the root cause
+            if config.verbosity > 3 {
+                eprintln!(
+                    "{} {}: Trace level logging is disabled; setting log level to debug",
+                    status_colorizer("WRN"),
+                    module_colorizer("Configuration::parse_cli_args"),
+                );
+
+                config.verbosity = 3;
+            }
         }
 
         if came_from_cli!(args, "no_recursion") {
@@ -932,23 +995,11 @@ impl Configuration {
 
         if let Some(headers) = args.get_many::<String>("headers") {
             for val in headers {
-                let mut split_val = val.split(':');
-
-                // explicitly take first split value as header's name
-                let name = split_val.next().unwrap().trim();
-
-                // all other items in the iterator returned by split, when combined with the
-                // original split deliminator (:), make up the header's final value
-                let value = split_val.collect::<Vec<&str>>().join(":");
-
-                if value.starts_with(' ') && !value.starts_with("  ") {
-                    // first character is a space and the second character isn't
-                    // we can trim the leading space
-                    let trimmed = value.trim_start();
-                    config.headers.insert(name.to_string(), trimmed.to_string());
-                } else {
-                    config.headers.insert(name.to_string(), value.to_string());
-                }
+                let Ok((name, value)) = split_header(val) else {
+                    log::warn!("Invalid header: {}", val);
+                    continue;
+                };
+                config.headers.insert(name, value);
             }
         }
 
@@ -982,14 +1033,11 @@ impl Configuration {
 
         if let Some(queries) = args.get_many::<String>("queries") {
             for val in queries {
-                // same basic logic used as reading in the headers HashMap above
-                let mut split_val = val.split('=');
-
-                let name = split_val.next().unwrap().trim();
-
-                let value = split_val.collect::<Vec<&str>>().join("=");
-
-                config.queries.push((name.to_string(), value.to_string()));
+                let Ok((name, value)) = split_query(val) else {
+                    log::warn!("Invalid query string: {}", val);
+                    continue;
+                };
+                config.queries.push((name, value));
             }
         }
 
@@ -1111,6 +1159,7 @@ impl Configuration {
         update_if_not_default!(&mut conf.client_cert, new.client_cert, "");
         update_if_not_default!(&mut conf.client_key, new.client_key, "");
         update_if_not_default!(&mut conf.verbosity, new.verbosity, 0);
+        update_if_not_default!(&mut conf.limit_bars, new.limit_bars, 0);
         update_if_not_default!(&mut conf.silent, new.silent, false);
         update_if_not_default!(&mut conf.quiet, new.quiet, false);
         update_if_not_default!(&mut conf.auto_bail, new.auto_bail, false);
@@ -1171,12 +1220,15 @@ impl Configuration {
             Vec::<u16>::new()
         );
         update_if_not_default!(&mut conf.dont_filter, new.dont_filter, false);
+        update_if_not_default!(&mut conf.scan_dir_listings, new.scan_dir_listings, false);
         update_if_not_default!(&mut conf.scan_limit, new.scan_limit, 0);
         update_if_not_default!(&mut conf.parallel, new.parallel, 0);
         update_if_not_default!(&mut conf.rate_limit, new.rate_limit, 0);
         update_if_not_default!(&mut conf.replay_proxy, new.replay_proxy, "");
         update_if_not_default!(&mut conf.debug_log, new.debug_log, "");
         update_if_not_default!(&mut conf.resume_from, new.resume_from, "");
+        update_if_not_default!(&mut conf.request_file, new.request_file, "");
+        update_if_not_default!(&mut conf.protocol, new.protocol, request_protocol());
 
         update_if_not_default!(&mut conf.timeout, new.timeout, timeout());
         update_if_not_default!(&mut conf.user_agent, new.user_agent, user_agent());
diff --git a/src/config/utils.rs b/src/config/utils.rs
index f3bd3b44..128249d9 100644
--- a/src/config/utils.rs
+++ b/src/config/utils.rs
@@ -1,8 +1,12 @@
+use super::Configuration;
 use crate::{
-    utils::{module_colorizer, status_colorizer},
+    utils::{module_colorizer, parse_url_with_raw_path, status_colorizer},
     DEFAULT_BACKUP_EXTENSIONS, DEFAULT_IGNORED_EXTENSIONS, DEFAULT_METHOD, DEFAULT_STATUS_CODES,
     DEFAULT_WORDLIST, VERSION,
 };
+use anyhow::{bail, Result};
+use std::collections::HashMap;
+
 #[cfg(not(test))]
 use std::process::exit;
 
@@ -45,6 +49,11 @@ pub(super) fn threads() -> usize {
     50
 }
 
+/// default protocol value
+pub(super) fn request_protocol() -> String {
+    String::from("https")
+}
+
 /// default status codes
 pub(super) fn status_codes() -> Vec<u16> {
     DEFAULT_STATUS_CODES
@@ -179,9 +188,449 @@ pub fn determine_requester_policy(auto_tune: bool, auto_bail: bool) -> Requester
     }
 }
 
+/// Splits a query string into a key-value pair.
+///
+/// This function takes a query string in the format of `"key=value"` and splits it into
+/// a tuple containing the key and value as separate strings. If the query string is
+/// malformed (e.g., empty or without a key), it returns an error.
+///
+/// # Arguments
+///
+/// * `query` - A string slice that holds the query string to be split.
+///
+/// # Returns
+///
+/// * `Result<(String, String)>` - A tuple containing the key and value as `String`s,
+///   or an error if the input is invalid.
+///
+/// # Errors
+///
+/// This function will return an error if:
+/// * The input string is empty or equal to `"="`.
+/// * The key part of the query string is empty (i.e., if the string starts with `"="`).
+///
+/// # Examples
+///
+/// ```
+/// let result = split_query("name=John");
+/// assert_eq!(result.unwrap(), ("name".to_string(), "John".to_string()));
+///
+/// let result = split_query("name=");
+/// assert_eq!(result.unwrap(), ("name".to_string(), "".to_string()));
+///
+/// let result = split_query("name=John=Doe");
+/// assert_eq!(result.unwrap(), ("name".to_string(), "John=Doe".to_string()));
+///
+/// let result = split_query("=John");
+/// assert!(result.is_err());
+///
+/// let result = split_query("");
+/// assert!(result.is_err());
+/// ```
+pub fn split_query(query: &str) -> Result<(String, String)> {
+    if query.is_empty() || query == "=" {
+        bail!("Empty query string provided");
+    }
+
+    let mut split_val = query.split('=');
+
+    let name = split_val.next().unwrap().trim();
+
+    if name.is_empty() {
+        bail!("Empty key in query string");
+    }
+
+    let value = split_val.collect::<Vec<&str>>().join("=");
+
+    Ok((name.to_string(), value.to_string()))
+}
+
+/// Splits an HTTP header string into a key-value pair.
+///
+/// This function takes a header string in the format of `"Key: Value"` and splits it into
+/// a tuple containing the key and value as separate strings. If the header string is
+/// malformed (e.g., empty or missing a key), it returns an error.
+///
+/// # Arguments
+///
+/// * `header` - A string slice that holds the header string to be split.
+///
+/// # Returns
+///
+/// * `Result<(String, String)>` - A tuple containing the key and value as `String`s,
+///   or an error if the input is invalid.
+///
+/// # Errors
+///
+/// This function will return an error if:
+/// * The input string is empty.
+/// * The key part of the header string is empty (i.e., if the string starts with `":"`).
+///
+/// # Examples
+///
+/// ```
+/// let result = split_header("Content-Type: application/json");
+/// assert_eq!(result.unwrap(), ("Content-Type".to_string(), "application/json".to_string()));
+///
+/// let result = split_header("Content-Length: 1234");
+/// assert_eq!(result.unwrap(), ("Content-Length".to_string(), "1234".to_string()));
+///
+/// let result = split_header("Authorization: Bearer token");
+/// assert_eq!(result.unwrap(), ("Authorization".to_string(), "Bearer token".to_string()));
+///
+/// let result = split_header("InvalidHeader");
+/// assert!(result.is_err());
+///
+/// let result = split_header("");
+/// assert!(result.is_err());
+/// ```
+pub fn split_header(header: &str) -> Result<(String, String)> {
+    if header.is_empty() {
+        bail!("Empty header provided");
+    }
+
+    let mut split_val = header.split(':');
+
+    // explicitly take first split value as header's name
+    let name = split_val.next().unwrap().trim().to_string();
+
+    if name.is_empty() {
+        bail!("Empty header name provided");
+    }
+
+    // all other items in the iterator returned by split, when combined with the
+    // original split deliminator (:), make up the header's final value
+    let value = split_val.collect::<Vec<&str>>().join(":");
+
+    if value.starts_with(' ') && !value.starts_with("  ") {
+        // first character is a space and the second character isn't
+        // we can trim the leading space
+        let trimmed = value.trim_start();
+        Ok((name, trimmed.to_string()))
+    } else {
+        Ok((name, value))
+    }
+}
+
+/// Combines two `Cookie` header strings into a single, unified `Cookie` header string.
+///
+/// The function parses both input strings into individual key-value pairs, ensuring that each
+/// key is unique. If a key appears in both input strings, the value from the second string
+/// will override the value from the first string. The resulting combined `Cookie` header string
+/// is returned with all key-value pairs separated by `;`.
+///
+/// # Arguments
+///
+/// * `cookie1` - A string slice representing the first `Cookie` header.
+/// * `cookie2` - A string slice representing the second `Cookie` header.
+///
+/// # Returns
+///
+/// * A `String` containing the combined `Cookie` header with unique keys.
+///
+/// # Example
+///
+/// ```
+/// let cookie1 = "super=duper; stuff=things";
+/// let cookie2 = "stuff=mothings; derp=tronic";
+/// let combined_cookie = combine_cookies(cookie1, cookie2);
+/// assert_eq!(combined_cookie, "super=duper; stuff=mothings; derp=tronic");
+/// ```
+///
+/// The output string will contain all unique keys from both input strings, with the value
+/// from the second string taking precedence in the case of key collisions.
+fn combine_cookies(cookie1: &str, cookie2: &str) -> String {
+    let mut cookie_map = HashMap::new();
+
+    // Helper function to parse a cookie string and insert it into the map
+    let parse_cookie = |cookie_str: &str, map: &mut HashMap<String, String>| {
+        for pair in cookie_str.split(';') {
+            let mut key_value = pair.trim().splitn(2, '=');
+            if let (Some(key), Some(value)) = (key_value.next(), key_value.next()) {
+                map.insert(key.to_string(), value.to_string());
+            }
+        }
+    };
+
+    // Parse both cookie strings into the map
+    parse_cookie(cookie1, &mut cookie_map);
+    parse_cookie(cookie2, &mut cookie_map);
+
+    // Build the final cookie header string
+    cookie_map
+        .into_iter()
+        .map(|(key, value)| format!("{}={}", key, value))
+        .collect::<Vec<_>>()
+        .join("; ")
+}
+
+/// Parses a raw HTTP request from a file and updates the provided configuration.
+///
+/// This function reads an HTTP request from the file specified by `config.request_file`,
+/// parses the request line, headers, and body, and updates the `config` object
+/// with the parsed values. If certain elements (e.g., headers or body) are
+/// already provided via the CLI, they take precedence over the parsed values.
+///
+/// # Arguments
+///
+/// * `config` - A mutable reference to a `Configuration` object that will be
+///   updated with the parsed request data.
+///
+/// # Returns
+///
+/// * `Result<()>` - Returns `Ok(())` if parsing and configuration updates
+///   were successful, or an error if the raw file or request is invalid.
+///
+/// # Errors
+///
+/// This function will return an error if:
+/// * The file specified in `config.request_file` is empty.
+/// * The request is malformed (e.g., missing the request line, method, or URI).
+/// * Required headers are missing (e.g., `Host` when the request line URI is not a full URL).
+///
+/// # Details
+///
+/// * The request body is only set if it hasn't been overridden by the CLI options.
+/// * The request line method is added to `config.methods` if it's not already present.
+/// * Headers from the raw request are added to `config.headers`, unless overridden
+///   by CLI options. Special handling is applied to `User-Agent`, `Content-Length`,
+///   and `Cookie` headers.
+/// * The request URI is validated and parsed. If it's not a full URL, it will be
+///   combined with the `Host` header to form a full target URL.
+/// * Query parameters are extracted from the URI and added to `config.queries`,
+///   unless overridden by CLI options.
+///
+/// # Examples
+///
+/// ```rust
+/// let mut config = Configuration::default();
+/// config.request_file = "path/to/raw/request.txt".to_string();
+///
+/// let result = parse_request_file(&mut config);
+/// assert!(result.is_ok());
+/// assert_eq!(config.methods, vec!["GET".to_string()]);
+/// assert_eq!(config.target_url, "http://example.com/path".to_string());
+/// assert_eq!(config.headers.get("User-Agent").unwrap(), "MyCustomAgent");
+/// assert_eq!(config.data, b"key=value".to_vec());
+/// ```
+pub fn parse_request_file(config: &mut Configuration) -> Result<()> {
+    // read in the file located at config.request_file
+    // parse the file into a Request struct
+    let contents = std::fs::read_to_string(&config.request_file)?;
+
+    if contents.is_empty() {
+        bail!("Empty --request-file file provided");
+    }
+
+    // this should split the body from the request line and headers
+    let lines = contents.split("\r\n\r\n").collect::<Vec<&str>>();
+
+    if lines.len() < 2 {
+        bail!("Invalid request: Missing head/body CRLF separator");
+    }
+
+    let head = lines[0];
+    let body = lines[1].as_bytes().to_vec();
+
+    // we only want to use the request's body if the user hasn't
+    // overridden it on the cli
+    if config.data.is_empty() {
+        config.data = body;
+    }
+
+    // begin parsing the request line and headers
+    let mut head_parts = head.split("\r\n");
+
+    let Some(request_line) = head_parts.next() else {
+        bail!("Invalid request: Missing request line");
+    };
+
+    if request_line.is_empty() {
+        bail!("Invalid request: Empty request line");
+    }
+
+    let mut request_parts = request_line.split_whitespace();
+
+    let Some(method) = request_parts.next() else {
+        bail!("Invalid request: Missing method");
+    };
+
+    if method.is_empty() {
+        bail!("Invalid request: Empty method");
+    }
+
+    let method = method.to_string();
+
+    if !config.methods.contains(&method) {
+        config.methods.push(method);
+    }
+
+    let Some(uri) = request_parts.next() else {
+        bail!("Invalid request: Missing request line URI");
+    };
+
+    if uri.is_empty() {
+        bail!("Invalid request: Empty request line URI");
+    }
+
+    for mut line in head_parts {
+        line = line.trim();
+
+        if line.is_empty() {
+            break; // Empty line signals the end of headers
+        }
+
+        let Ok((name, value)) = split_header(line) else {
+            log::warn!("Invalid header: {}", line);
+            continue;
+        };
+
+        if name.is_empty() {
+            log::warn!("Invalid header name: {}", line);
+            continue;
+        }
+
+        if name.to_lowercase() == "user-agent" {
+            if config.user_agent == user_agent() {
+                config.user_agent = value;
+            }
+            continue;
+        }
+
+        if name.to_lowercase() == "content-length" {
+            log::debug!("Skipping content-length header, a new one will be created");
+            continue;
+        }
+
+        if config.headers.contains_key(&name) {
+            if name.to_lowercase() == "cookie" {
+                // the cookie header already exists, so we need to extend it with
+                // our values and ensure cli-provided cookie values override those
+                // from the request
+                let existing = config.headers.get_mut(&name).unwrap();
+                // second param takes precedence over first
+                let combined = combine_cookies(&value, existing);
+                *existing = combined;
+                continue;
+            }
+            log::debug!("Found header from cli, overriding raw request with cli entry: {name}");
+            continue;
+        }
+
+        config.headers.insert(name, value);
+    }
+
+    let url = parse_url_with_raw_path(uri);
+
+    if url.is_err() {
+        // uri in request line is not a valid URL, so it's most likely a path/relative url
+        // we need to combine it with the host header
+        for (key, value) in &config.headers {
+            if key.to_lowercase() == "host" {
+                config.target_url = format!("{}{}", value, uri);
+                break;
+            }
+        }
+
+        if config.target_url.is_empty() {
+            bail!("Invalid request: Missing Host header and request line URI isn't a full URL");
+        }
+
+        // need to parse queries from the uri, if any are present
+        let mut uri_parts = uri.splitn(2, '?');
+
+        // skip the path
+        uri_parts.next();
+
+        if let Some(queries) = uri_parts.next() {
+            let query_parts = queries.split("&");
+
+            query_parts.into_iter().for_each(|query| {
+                let Ok((name, value)) = split_query(query) else {
+                    return;
+                };
+                for (k, _) in &config.queries {
+                    if k.to_lowercase() == name.to_lowercase() {
+                        // allow cli options to take precedent when query names match
+                        return;
+                    }
+                }
+
+                config.queries.push((name, value));
+            });
+        }
+    } else {
+        let mut url = url.unwrap();
+
+        if let Some(host) = config.headers.get("Host") {
+            url.set_host(Some(host)).unwrap();
+        }
+
+        url.query_pairs().for_each(|(key, value)| {
+            for (k, _) in &config.queries {
+                if k.to_lowercase() == key.to_lowercase() {
+                    // allow cli options to take precedent when query names match
+                    return;
+                }
+            }
+
+            config.queries.push((key.to_string(), value.to_string()));
+        });
+
+        url.set_query(None);
+        url.set_fragment(None);
+
+        config.target_url = url.to_string();
+    }
+
+    Ok(())
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
+    use std::env;
+    use std::fs::{self, File};
+    use std::io::{self, Write};
+    use std::path::PathBuf;
+    use std::time::{SystemTime, UNIX_EPOCH};
+
+    struct TempSetup {
+        pub path: PathBuf,
+        pub config: Configuration,
+        pub file: File,
+    }
+
+    impl TempSetup {
+        pub fn new() -> Self {
+            let mut temp_dir: PathBuf = env::temp_dir();
+
+            temp_dir.push(format!(
+                "temp_request_file_{}.txt",
+                SystemTime::now()
+                    .duration_since(UNIX_EPOCH)
+                    .unwrap()
+                    .as_nanos()
+            ));
+
+            let config: Configuration = Configuration {
+                request_file: temp_dir.to_str().unwrap().to_string(),
+                ..Default::default()
+            };
+
+            let file = File::create(&temp_dir).unwrap();
+
+            Self {
+                path: temp_dir,
+                config,
+                file,
+            }
+        }
+
+        pub fn cleanup(self) {
+            fs::remove_file(self.path).unwrap();
+        }
+    }
 
     #[test]
     /// test determine_output_level returns higher of the two levels if both given values are true
@@ -233,4 +682,609 @@ mod tests {
     fn report_and_exit_panics_under_test() {
         report_and_exit("test");
     }
+
+    #[test]
+    fn test_split_query_simple() {
+        let query = "name=value";
+        let result = split_query(query).unwrap();
+        assert_eq!(result, ("name".to_string(), "value".to_string()));
+    }
+
+    #[test]
+    fn test_split_query_with_spaces() {
+        let query = " name = value ";
+        let result = split_query(query).unwrap();
+        assert_eq!(result, ("name".to_string(), " value ".to_string()));
+    }
+
+    #[test]
+    fn test_split_query_empty_value() {
+        let query = "name=";
+        let result = split_query(query).unwrap();
+        assert_eq!(result, ("name".to_string(), "".to_string()));
+    }
+
+    #[test]
+    fn test_split_query_no_value() {
+        let query = "name";
+        let result = split_query(query).unwrap();
+        assert_eq!(result, ("name".to_string(), "".to_string()));
+    }
+
+    #[test]
+    fn test_split_query_multiple_equals() {
+        let query = "name=value=another";
+        let result = split_query(query).unwrap();
+        assert_eq!(result, ("name".to_string(), "value=another".to_string()));
+    }
+
+    #[test]
+    fn test_split_query_empty_key_and_value() {
+        let query = "=";
+        let result = split_query(query);
+        assert!(result.is_err());
+    }
+
+    #[test]
+    fn test_split_query_empty_key() {
+        let query = "=value";
+        let result = split_query(query);
+        assert!(result.is_err());
+    }
+
+    #[test]
+    fn test_split_query_trailing_equals_in_value() {
+        let query = "name=value=";
+        let result = split_query(query).unwrap();
+        assert_eq!(result, ("name".to_string(), "value=".to_string()));
+    }
+
+    #[test]
+    fn test_split_query_no_equals() {
+        let query = "just_a_key";
+        let result = split_query(query).unwrap();
+        assert_eq!(result, ("just_a_key".to_string(), "".to_string()));
+    }
+
+    #[test]
+    fn test_split_query_empty_input() {
+        let query = "";
+        assert!(split_query(query).is_err());
+    }
+
+    #[test]
+    fn test_split_header_simple() -> Result<()> {
+        let header = "Content-Type: text/html";
+        let result = split_header(header)?;
+        assert_eq!(
+            result,
+            ("Content-Type".to_string(), "text/html".to_string())
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn test_split_header_with_leading_space_in_value() -> Result<()> {
+        let header = "Content-Type:  text/html";
+        let result = split_header(header)?;
+        assert_eq!(
+            result,
+            ("Content-Type".to_string(), "  text/html".to_string())
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn test_split_header_with_trimmed_leading_space() -> Result<()> {
+        let header = "Content-Type: text/html";
+        let result = split_header(header)?;
+        assert_eq!(
+            result,
+            ("Content-Type".to_string(), "text/html".to_string())
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn test_split_header_with_multiple_colons() -> Result<()> {
+        let header = "Date: Mon, 27 Jul 2009 12:28:53 GMT";
+        let result = split_header(header)?;
+        assert_eq!(
+            result,
+            (
+                "Date".to_string(),
+                "Mon, 27 Jul 2009 12:28:53 GMT".to_string()
+            )
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn test_split_header_empty_value() -> Result<()> {
+        let header = "X-Custom-Header: ";
+        let result = split_header(header)?;
+        assert_eq!(result, ("X-Custom-Header".to_string(), "".to_string()));
+        Ok(())
+    }
+
+    #[test]
+    fn test_split_header_no_value() -> Result<()> {
+        let header = "X-Custom-Header:";
+        let result = split_header(header)?;
+        assert_eq!(result, ("X-Custom-Header".to_string(), "".to_string()));
+        Ok(())
+    }
+
+    #[test]
+    fn test_split_header_no_colon() -> Result<()> {
+        let header = "InvalidHeader";
+        let result = split_header(header)?;
+        assert_eq!(result, ("InvalidHeader".to_string(), "".to_string()));
+        Ok(())
+    }
+
+    #[test]
+    fn test_split_header_empty_key() {
+        let header = ": value";
+        let result = split_header(header);
+        assert!(result.is_err());
+        assert_eq!(
+            result.unwrap_err().to_string(),
+            "Empty header name provided"
+        );
+    }
+
+    #[test]
+    fn test_split_header_empty_key_and_value() {
+        let header = ": ";
+        let result = split_header(header);
+        assert!(result.is_err());
+        assert_eq!(
+            result.unwrap_err().to_string(),
+            "Empty header name provided"
+        );
+    }
+
+    #[test]
+    fn test_split_header_empty_input() {
+        let header = "";
+        let result = split_header(header);
+        assert!(result.is_err());
+        assert_eq!(result.unwrap_err().to_string(), "Empty header provided");
+    }
+
+    #[test]
+    fn test_split_header_value_with_leading_single_space() -> Result<()> {
+        let header = "Authorization: Bearer token";
+        let result = split_header(header)?;
+        assert_eq!(
+            result,
+            ("Authorization".to_string(), "Bearer token".to_string())
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn test_split_header_value_with_leading_multiple_spaces() -> Result<()> {
+        let header = "Authorization:  Bearer token";
+        let result = split_header(header)?;
+        assert_eq!(
+            result,
+            ("Authorization".to_string(), "  Bearer token".to_string())
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_with_empty_request() {
+        let mut config = Configuration::new().unwrap();
+        let result = parse_request_file(&mut config);
+        assert!(result.is_err());
+    }
+    #[test]
+    fn test_parse_raw_with_empty_file() -> io::Result<()> {
+        let mut tmp = TempSetup::new();
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_err());
+        assert_eq!(
+            result.unwrap_err().to_string(),
+            "Empty --request-file file provided"
+        );
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_without_head_body_crlf() -> io::Result<()> {
+        let mut tmp = TempSetup::new();
+
+        write!(tmp.file, "GET / HTTP/1.1\r\n")?;
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_err());
+        assert_eq!(
+            result.unwrap_err().to_string(),
+            "Invalid request: Missing head/body CRLF separator"
+        );
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_with_only_head_body_crlf() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        writeln!(tmp.file, "\r\n\r\n")?;
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_err());
+        assert_eq!(
+            result.unwrap_err().to_string(),
+            "Invalid request: Empty request line"
+        );
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_body_is_overridden_by_cli() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(
+            tmp.file,
+            "GET http://localhost/srv HTTP/1.0\r\n\r\nrequest-body"
+        )?;
+
+        parse_request_file(&mut tmp.config).unwrap();
+        assert_eq!(tmp.config.data, b"request-body".to_vec());
+
+        tmp.config.data = b"cli-data".to_vec();
+
+        parse_request_file(&mut tmp.config).unwrap();
+        assert_eq!(tmp.config.data, b"cli-data".to_vec());
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_with_empty_request_line() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(tmp.file, "\r\nHost: example.com\r\n\r\n")?;
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_err());
+        assert_eq!(
+            result.unwrap_err().to_string(),
+            "Invalid request: Empty request line"
+        );
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_with_missing_uri() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(tmp.file, "GET\r\nHost: example.com\r\n\r\n")?;
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_err());
+        assert_eq!(
+            result.unwrap_err().to_string(),
+            "Invalid request: Missing request line URI"
+        );
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_with_missing_method() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(tmp.file, "  \r\nHost: example.com\r\n\r\n")?;
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_err());
+        assert_eq!(
+            result.unwrap_err().to_string(),
+            "Invalid request: Missing method"
+        );
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_methods_are_appended_if_unique() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(
+            tmp.file,
+            "POST / HTTP/1.1\r\nHost: example.com\r\nUser-Agent: test-agent\r\n\r\n"
+        )?;
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_ok());
+        assert_eq!(tmp.config.methods, vec!["GET", "POST"]);
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_methods_are_ignored_if_already_present_from_cli() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(
+            tmp.file,
+            "GET / HTTP/1.1\r\nHost: example.com\r\nUser-Agent: test-agent\r\n\r\n"
+        )?;
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_ok());
+        assert_eq!(tmp.config.methods, vec!["GET"]);
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_headers_added_to_config_if_missing_else_overridden_from_cli() -> io::Result<()>
+    {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        // header from cli
+        tmp.config
+            .headers
+            .insert(String::from("stuff"), String::from("things"));
+
+        // stuff header will be overridden by the one in the cli config (i.e. the raw request's
+        // stuff header will be ignored because of the cli config)
+        write!(
+            tmp.file,
+            "GET / HTTP/1.1\r\nHost: example.com\r\nstuff: mothings\r\n\r\n"
+        )?;
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_ok());
+        assert!(tmp.config.headers.contains_key("Host"));
+        assert_eq!(tmp.config.headers.get("stuff").unwrap(), "things");
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_with_user_agent_in_request() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(
+            tmp.file,
+            "GET / HTTP/1.1\r\nHost: example.com\r\nUser-Agent: test-agent\r\n\r\n"
+        )?;
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_ok());
+        assert_eq!(tmp.config.user_agent, "test-agent");
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_with_user_agent_in_request_and_cli() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(
+            tmp.file,
+            "GET / HTTP/1.1\r\nHost: example.com\r\nUser-Agent: test-agent\r\n\r\n"
+        )?;
+
+        tmp.config.user_agent = "cli-agent".to_string();
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_ok());
+        assert_eq!(tmp.config.user_agent, "cli-agent");
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_content_length_is_always_skipped() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(
+            tmp.file,
+            "GET / HTTP/1.1\r\nHost: example.com\r\nContent-length: 21\r\n\r\n"
+        )?;
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_ok());
+        assert!(!tmp.config.headers.contains_key("Content-length"));
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_cookie_header_appended_or_overridden() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(
+            tmp.file,
+            "GET / HTTP/1.1\r\nHost: example.com\r\nCookie: derp=tronic2; super=duper2\r\n\r\n"
+        )?;
+
+        tmp.config.headers.insert(
+            "Cookie".to_string(),
+            "derp=tronic; stuff=things".to_string(),
+        );
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_ok());
+
+        let cookies = tmp.config.headers.get("Cookie").unwrap();
+
+        assert!(cookies.contains("derp=tronic"));
+        assert!(cookies.contains("stuff=things"));
+        assert!(cookies.contains("super=duper2"));
+
+        // got overridden
+        assert!(!cookies.contains("derp=tronic2"));
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_with_relative_path_and_partial_host_header() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(tmp.file, "GET /srv HTTP/1.1\r\nHost: example.com\r\n\r\n")?;
+
+        let result = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_ok());
+        assert_eq!(tmp.config.target_url, "example.com/srv");
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_with_relative_path_and_no_host_header() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(tmp.file, "GET /srv HTTP/1.1\r\n\r\n")?;
+
+        let result: std::result::Result<(), anyhow::Error> = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_err());
+        assert_eq!(
+            result.unwrap_err().to_string(),
+            "Invalid request: Missing Host header and request line URI isn't a full URL"
+        );
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_with_full_url_and_no_host_header() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(tmp.file, "GET http://localhost/srv HTTP/1.1\r\n\r\n")?;
+
+        let result: std::result::Result<(), anyhow::Error> = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_ok());
+        assert_eq!(tmp.config.target_url, "http://localhost/srv");
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_with_full_url_and_host_header() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(
+            tmp.file,
+            "GET http://localhost/srv HTTP/1.1\r\nHost: example.com\r\n\r\n"
+        )?;
+
+        let result: std::result::Result<(), anyhow::Error> = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_ok());
+        assert_eq!(tmp.config.target_url, "http://example.com/srv");
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_with_partial_url_and_queries() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(
+            tmp.file,
+            "GET /srv?mostuff=mothings&derp=tronic2 HTTP/1.1\r\nHost: example.com\r\n\r\n"
+        )?;
+
+        tmp.config
+            .queries
+            .push(("derp".to_string(), "tronic".to_string()));
+        tmp.config
+            .queries
+            .push(("stuff".to_string(), "things".to_string()));
+
+        let result: std::result::Result<(), anyhow::Error> = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_ok());
+        assert_eq!(
+            tmp.config.queries,
+            vec![
+                (String::from("derp"), String::from("tronic")),
+                (String::from("stuff"), String::from("things")),
+                (String::from("mostuff"), String::from("mothings"))
+            ]
+        );
+
+        tmp.cleanup();
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_raw_with_full_url_and_queries() -> io::Result<()> {
+        let mut tmp: TempSetup = TempSetup::new();
+
+        write!(
+            tmp.file,
+            "GET http://localhost/srv?mostuff=mothings&derp=tronic2 HTTP/1.1\r\nHost: example.com\r\n\r\n"
+        )?;
+
+        tmp.config
+            .queries
+            .push(("derp".to_string(), "tronic".to_string()));
+        tmp.config
+            .queries
+            .push(("stuff".to_string(), "things".to_string()));
+
+        let result: std::result::Result<(), anyhow::Error> = parse_request_file(&mut tmp.config);
+
+        assert!(result.is_ok());
+        assert_eq!(
+            tmp.config.queries,
+            vec![
+                (String::from("derp"), String::from("tronic")),
+                (String::from("stuff"), String::from("things")),
+                (String::from("mostuff"), String::from("mothings"))
+            ]
+        );
+
+        tmp.cleanup();
+        Ok(())
+    }
 }
diff --git a/src/event_handlers/scans.rs b/src/event_handlers/scans.rs
index 5dc8ab92..b3640ff0 100644
--- a/src/event_handlers/scans.rs
+++ b/src/event_handlers/scans.rs
@@ -120,7 +120,10 @@ impl ScanHandler {
     pub fn initialize(handles: Arc<Handles>) -> (Joiner, ScanHandle) {
         log::trace!("enter: initialize");
 
-        let data = Arc::new(FeroxScans::new(handles.config.output_level));
+        let data = Arc::new(FeroxScans::new(
+            handles.config.output_level,
+            handles.config.limit_bars,
+        ));
         let (tx, rx): FeroxChannel<Command> = mpsc::unbounded_channel();
 
         let max_depth = handles.config.depth;
@@ -322,7 +325,9 @@ impl ScanHandler {
             let scan = if let Some(ferox_scan) = self.data.get_scan_by_url(&target) {
                 ferox_scan // scan already known
             } else {
-                self.data.add_directory_scan(&target, order).1 // add the new target; return FeroxScan
+                self.data
+                    .add_directory_scan(&target, order, self.handles.clone())
+                    .1 // add the new target; return FeroxScan
             };
 
             if should_test_deny
diff --git a/src/extractor/container.rs b/src/extractor/container.rs
index ad9f4f15..70871c8e 100644
--- a/src/extractor/container.rs
+++ b/src/extractor/container.rs
@@ -228,8 +228,11 @@ impl<'a> Extractor<'a> {
                                 if resp.is_file() || !resp.is_directory() {
                                     log::debug!("Extracted File: {}", resp);
 
-                                    c_scanned_urls
-                                        .add_file_scan(resp.url().as_str(), ScanOrder::Latest);
+                                    c_scanned_urls.add_file_scan(
+                                        resp.url().as_str(),
+                                        ScanOrder::Latest,
+                                        c_handles.clone(),
+                                    );
 
                                     if c_handles.config.collect_extensions {
                                         // no real reason this should fail
diff --git a/src/main.rs b/src/main.rs
index 0e779e16..ec2cddcf 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -197,9 +197,9 @@ async fn get_targets(handles: Arc<Handles>) -> Result<Vec<String>> {
             }
         }
 
-        if !target.starts_with("http") && !target.starts_with("https") {
+        if !target.starts_with("http") {
             // --url hackerone.com
-            *target = format!("https://{target}");
+            *target = format!("{}://{target}", handles.config.protocol);
         }
     }
 
diff --git a/src/parser.rs b/src/parser.rs
index 8b4a2525..31cf64b3 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -40,12 +40,12 @@ pub fn initialize() -> Command {
             Arg::new("url")
                 .short('u')
                 .long("url")
-                .required_unless_present_any(["stdin", "resume_from", "update_app"])
+                .required_unless_present_any(["stdin", "resume_from", "update_app", "request_file"])
                 .help_heading("Target selection")
                 .value_name("URL")
                 .use_value_delimiter(true)
                 .value_hint(ValueHint::Url)
-                .help("The target URL (required, unless [--stdin || --resume-from] used)"),
+                .help("The target URL (required, unless [--stdin || --resume-from || --request-file] used)"),
         )
         .arg(
             Arg::new("stdin")
@@ -64,6 +64,15 @@ pub fn initialize() -> Command {
                 .help("State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state)")
                 .conflicts_with("url")
                 .num_args(1),
+        ).arg(
+            Arg::new("request_file")
+                .long("request-file")
+                .help_heading("Target selection")
+                .value_hint(ValueHint::FilePath)
+                .conflicts_with("url")
+                .num_args(1)
+                .value_name("REQUEST_FILE")
+                .help("Raw HTTP request file to use as a template for all requests"),
         );
 
     /////////////////////////////////////////////////////////////////////
@@ -100,7 +109,7 @@ pub fn initialize() -> Command {
                 .num_args(0)
                 .help_heading("Composite settings")
                 .conflicts_with_all(["rate_limit", "auto_bail"])
-                .help("Use the same settings as --smart and set --collect-extensions to true"),
+                .help("Use the same settings as --smart and set --collect-extensions and --scan-dir-listings to true"),
         );
 
     /////////////////////////////////////////////////////////////////////
@@ -248,6 +257,13 @@ pub fn initialize() -> Command {
                 .help_heading("Request settings")
                 .num_args(0)
                 .help("Append / to each request's URL")
+        ).arg(
+            Arg::new("protocol")
+                .long("protocol")
+                .value_name("PROTOCOL")
+                .num_args(1)
+                .help_heading("Request settings")
+                .help("Specify the protocol to use when targeting via --request-file or --url with domain only (default: https)"),
         );
 
     /////////////////////////////////////////////////////////////////////
@@ -574,6 +590,12 @@ pub fn initialize() -> Command {
                 .help(
                     "File extension(s) to Ignore while collecting extensions (only used with --collect-extensions)",
                 ),
+        ).arg(
+            Arg::new("scan_dir_listings")
+                .long("scan-dir-listings")
+                .num_args(0)
+                .help_heading("Scan settings")
+                .help("Force scans to recurse into directory listings")
         );
 
     /////////////////////////////////////////////////////////////////////
@@ -638,6 +660,13 @@ pub fn initialize() -> Command {
                 .num_args(0)
                 .help_heading("Output settings")
                 .help("Disable state output file (*.state)")
+        ).arg(
+            Arg::new("limit_bars")
+                .long("limit-bars")
+                .value_name("NUM_BARS_TO_SHOW")
+                .num_args(1)
+                .help_heading("Output settings")
+                .help("Number of directory scan bars to show at any given time (default: no limit)"),
         );
 
     /////////////////////////////////////////////////////////////////////
diff --git a/src/progress.rs b/src/progress.rs
index 42e235ad..372caf26 100644
--- a/src/progress.rs
+++ b/src/progress.rs
@@ -31,6 +31,15 @@ pub enum BarType {
 /// Add an [indicatif::ProgressBar](https://docs.rs/indicatif/latest/indicatif/struct.ProgressBar.html)
 /// to the global [PROGRESS_BAR](../config/struct.PROGRESS_BAR.html)
 pub fn add_bar(prefix: &str, length: u64, bar_type: BarType) -> ProgressBar {
+    let pb = ProgressBar::new(length).with_prefix(prefix.to_string());
+
+    update_style(&pb, bar_type);
+
+    PROGRESS_BAR.add(pb)
+}
+
+/// Update the style of a progress bar based on the `BarType`
+pub fn update_style(bar: &ProgressBar, bar_type: BarType) {
     let mut style = ProgressStyle::default_bar().progress_chars("#>-").with_key(
         "smoothed_per_sec",
         |state: &indicatif::ProgressState, w: &mut dyn std::fmt::Write| match (
@@ -66,11 +75,7 @@ pub fn add_bar(prefix: &str, length: u64, bar_type: BarType) -> ProgressBar {
         BarType::Quiet => style.template("Scanning: {prefix}").unwrap(),
     };
 
-    PROGRESS_BAR.add(
-        ProgressBar::new(length)
-            .with_style(style)
-            .with_prefix(prefix.to_string()),
-    )
+    bar.set_style(style);
 }
 
 #[cfg(test)]
diff --git a/src/scan_manager/scan.rs b/src/scan_manager/scan.rs
index f2798169..20e7629c 100644
--- a/src/scan_manager/scan.rs
+++ b/src/scan_manager/scan.rs
@@ -1,7 +1,10 @@
 use super::*;
 use crate::{
     config::OutputLevel,
+    event_handlers::Handles,
+    progress::update_style,
     progress::{add_bar, BarType},
+    scan_manager::utils::determine_bar_type,
     scanner::PolicyTrigger,
 };
 use anyhow::Result;
@@ -16,10 +19,20 @@ use std::{
     time::Instant,
 };
 
-use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
 use tokio::{sync, task::JoinHandle};
 use uuid::Uuid;
 
+#[derive(Debug, Default, Copy, Clone)]
+pub enum Visibility {
+    /// whether a FeroxScan's progress bar is currently shown
+    #[default]
+    Visible,
+
+    /// whether a FeroxScan's progress bar is currently hidden
+    Hidden,
+}
+
 /// Struct to hold scan-related state
 ///
 /// The purpose of this container is to open up the pathway to aborting currently running tasks and
@@ -58,7 +71,7 @@ pub struct FeroxScan {
     pub(super) task: sync::Mutex<Option<JoinHandle<()>>>,
 
     /// The progress bar associated with this scan
-    pub(super) progress_bar: Mutex<Option<ProgressBar>>,
+    pub progress_bar: Mutex<Option<ProgressBar>>,
 
     /// whether or not the user passed --silent|--quiet on the command line
     pub(super) output_level: OutputLevel,
@@ -74,6 +87,12 @@ pub struct FeroxScan {
 
     /// tracker for the time at which this scan was started
     pub(super) start_time: Instant,
+
+    /// whether the progress bar is currently visible or hidden
+    pub(super) visible: AtomicBool,
+
+    /// handles object pointer
+    pub(super) handles: Option<Arc<Handles>>,
 }
 
 /// Default implementation for FeroxScan
@@ -86,6 +105,7 @@ impl Default for FeroxScan {
             id: new_id,
             task: sync::Mutex::new(None), // tokio mutex
             status: Mutex::new(ScanStatus::default()),
+            handles: None,
             num_requests: 0,
             requests_made_so_far: 0,
             scan_order: ScanOrder::Latest,
@@ -98,14 +118,54 @@ impl Default for FeroxScan {
             status_429s: Default::default(),
             status_403s: Default::default(),
             start_time: Instant::now(),
+            visible: AtomicBool::new(true),
         }
     }
 }
 
 /// Implementation of FeroxScan
 impl FeroxScan {
+    /// return the visibility of the scan as a boolean
+    pub fn visible(&self) -> bool {
+        self.visible.load(Ordering::Relaxed)
+    }
+
+    pub fn swap_visibility(&self) {
+        // fetch_xor toggles the boolean to its opposite and returns the previous value
+        let visible = self.visible.fetch_xor(true, Ordering::Relaxed);
+
+        let Ok(bar) = self.progress_bar.lock() else {
+            log::warn!("couldn't unlock progress bar for {}", self.url);
+            return;
+        };
+
+        if bar.is_none() {
+            log::warn!("there is no progress bar for {}", self.url);
+            return;
+        }
+
+        let Some(handles) = self.handles.as_ref() else {
+            log::warn!("couldn't access handles pointer for {}", self.url);
+            return;
+        };
+
+        let bar_type = if !visible {
+            // visibility was false before we xor'd the value
+            match handles.config.output_level {
+                OutputLevel::Default => BarType::Default,
+                OutputLevel::Quiet => BarType::Quiet,
+                OutputLevel::Silent | OutputLevel::SilentJSON => BarType::Hidden,
+            }
+        } else {
+            // visibility was true before we xor'd the value
+            BarType::Hidden
+        };
+
+        update_style(bar.as_ref().unwrap(), bar_type);
+    }
+
     /// Stop a currently running scan
-    pub async fn abort(&self) -> Result<()> {
+    pub async fn abort(&self, active_bars: usize) -> Result<()> {
         log::trace!("enter: abort");
 
         match self.task.try_lock() {
@@ -114,7 +174,7 @@ impl FeroxScan {
                     log::trace!("aborting {:?}", self);
                     task.abort();
                     self.set_status(ScanStatus::Cancelled)?;
-                    self.stop_progress_bar();
+                    self.stop_progress_bar(active_bars);
                 }
             }
             Err(e) => {
@@ -151,15 +211,26 @@ impl FeroxScan {
     }
 
     /// Simple helper to call .finish on the scan's progress bar
-    pub(super) fn stop_progress_bar(&self) {
+    pub(super) fn stop_progress_bar(&self, active_bars: usize) {
         if let Ok(guard) = self.progress_bar.lock() {
             if guard.is_some() {
                 let pb = (*guard).as_ref().unwrap();
 
+                let bar_limit = if let Some(handles) = self.handles.as_ref() {
+                    handles.config.limit_bars
+                } else {
+                    0
+                };
+
+                if bar_limit > 0 && bar_limit < active_bars {
+                    pb.finish_and_clear();
+                    return;
+                }
+
                 if pb.position() > self.num_requests {
-                    pb.finish()
+                    pb.finish();
                 } else {
-                    pb.abandon()
+                    pb.abandon();
                 }
             }
         }
@@ -172,12 +243,18 @@ impl FeroxScan {
                 if guard.is_some() {
                     (*guard).as_ref().unwrap().clone()
                 } else {
-                    let bar_type = match self.output_level {
-                        OutputLevel::Default => BarType::Default,
-                        OutputLevel::Quiet => BarType::Quiet,
-                        OutputLevel::Silent | OutputLevel::SilentJSON => BarType::Hidden,
+                    let (active_bars, bar_limit) = if let Some(handles) = self.handles.as_ref() {
+                        if let Ok(scans) = handles.ferox_scans() {
+                            (scans.number_of_bars(), handles.config.limit_bars)
+                        } else {
+                            (0, handles.config.limit_bars)
+                        }
+                    } else {
+                        (0, 0)
                     };
 
+                    let bar_type = determine_bar_type(bar_limit, active_bars, self.output_level);
+
                     let pb = add_bar(&self.url, self.num_requests, bar_type);
                     pb.reset_elapsed();
 
@@ -191,12 +268,18 @@ impl FeroxScan {
             Err(_) => {
                 log::warn!("Could not unlock progress bar on {:?}", self);
 
-                let bar_type = match self.output_level {
-                    OutputLevel::Default => BarType::Default,
-                    OutputLevel::Quiet => BarType::Quiet,
-                    OutputLevel::Silent | OutputLevel::SilentJSON => BarType::Hidden,
+                let (active_bars, bar_limit) = if let Some(handles) = self.handles.as_ref() {
+                    if let Ok(scans) = handles.ferox_scans() {
+                        (scans.number_of_bars(), handles.config.limit_bars)
+                    } else {
+                        (0, handles.config.limit_bars)
+                    }
+                } else {
+                    (0, 0)
                 };
 
+                let bar_type = determine_bar_type(bar_limit, active_bars, self.output_level);
+
                 let pb = add_bar(&self.url, self.num_requests, bar_type);
                 pb.reset_elapsed();
 
@@ -206,6 +289,7 @@ impl FeroxScan {
     }
 
     /// Given a URL and ProgressBar, create a new FeroxScan, wrap it in an Arc and return it
+    #[allow(clippy::too_many_arguments)]
     pub fn new(
         url: &str,
         scan_type: ScanType,
@@ -213,6 +297,8 @@ impl FeroxScan {
         num_requests: u64,
         output_level: OutputLevel,
         pb: Option<ProgressBar>,
+        visibility: bool,
+        handles: Arc<Handles>,
     ) -> Arc<Self> {
         Arc::new(Self {
             url: url.to_string(),
@@ -222,14 +308,16 @@ impl FeroxScan {
             num_requests,
             output_level,
             progress_bar: Mutex::new(pb),
+            visible: AtomicBool::new(visibility),
+            handles: Some(handles),
             ..Default::default()
         })
     }
 
     /// Mark the scan as complete and stop the scan's progress bar
-    pub fn finish(&self) -> Result<()> {
+    pub fn finish(&self, active_bars: usize) -> Result<()> {
         self.set_status(ScanStatus::Complete)?;
-        self.stop_progress_bar();
+        self.stop_progress_bar(active_bars);
         Ok(())
     }
 
@@ -262,6 +350,22 @@ impl FeroxScan {
         false
     }
 
+    /// small wrapper to inspect ScanStatus and see if it's Running
+    pub fn is_running(&self) -> bool {
+        if let Ok(guard) = self.status.lock() {
+            return matches!(*guard, ScanStatus::Running);
+        }
+        false
+    }
+
+    /// small wrapper to inspect ScanStatus and see if it's NotStarted
+    pub fn is_not_started(&self) -> bool {
+        if let Ok(guard) = self.status.lock() {
+            return matches!(*guard, ScanStatus::NotStarted);
+        }
+        false
+    }
+
     /// await a task's completion, similar to a thread's join; perform necessary bookkeeping
     pub async fn join(&self) {
         log::trace!("enter join({:?})", self);
@@ -507,6 +611,8 @@ mod tests {
             1000,
             OutputLevel::Default,
             None,
+            true,
+            Arc::new(Handles::for_testing(None, None).0),
         );
 
         scan.add_error();
@@ -532,6 +638,7 @@ mod tests {
             scan_order: ScanOrder::Initial,
             num_requests: 0,
             requests_made_so_far: 0,
+            visible: AtomicBool::new(true),
             status: Mutex::new(ScanStatus::Running),
             task: Default::default(),
             progress_bar: Mutex::new(None),
@@ -540,6 +647,7 @@ mod tests {
             status_429s: Default::default(),
             errors: Default::default(),
             start_time: Instant::now(),
+            handles: None,
         };
 
         let pb = scan.progress_bar();
@@ -551,7 +659,62 @@ mod tests {
 
         assert_eq!(req_sec, 100);
 
-        scan.finish().unwrap();
+        scan.finish(0).unwrap();
         assert_eq!(scan.requests_per_second(), 0);
     }
+
+    #[test]
+    fn test_swap_visibility() {
+        let scan = FeroxScan::new(
+            "http://localhost",
+            ScanType::Directory,
+            ScanOrder::Latest,
+            1000,
+            OutputLevel::Default,
+            None,
+            true,
+            Arc::new(Handles::for_testing(None, None).0),
+        );
+
+        assert!(scan.visible());
+
+        scan.swap_visibility();
+        assert!(!scan.visible());
+
+        scan.swap_visibility();
+        assert!(scan.visible());
+
+        scan.swap_visibility();
+        assert!(!scan.visible());
+
+        scan.swap_visibility();
+        assert!(scan.visible());
+    }
+
+    #[test]
+    /// test for is_running method
+    fn test_is_running() {
+        let scan = FeroxScan::new(
+            "http://localhost",
+            ScanType::Directory,
+            ScanOrder::Latest,
+            1000,
+            OutputLevel::Default,
+            None,
+            true,
+            Arc::new(Handles::for_testing(None, None).0),
+        );
+
+        assert!(scan.is_not_started());
+        assert!(!scan.is_running());
+        assert!(!scan.is_complete());
+        assert!(!scan.is_cancelled());
+
+        *scan.status.lock().unwrap() = ScanStatus::Running;
+
+        assert!(!scan.is_not_started());
+        assert!(scan.is_running());
+        assert!(!scan.is_complete());
+        assert!(!scan.is_cancelled());
+    }
 }
diff --git a/src/scan_manager/scan_container.rs b/src/scan_manager/scan_container.rs
index 34107ccd..d2a078e8 100644
--- a/src/scan_manager/scan_container.rs
+++ b/src/scan_manager/scan_container.rs
@@ -12,6 +12,7 @@ use crate::{
     config::OutputLevel,
     progress::PROGRESS_PRINTER,
     progress::{add_bar, BarType},
+    scan_manager::utils::determine_bar_type,
     scan_manager::{MenuCmd, MenuCmdResult},
     scanner::RESPONSES,
     traits::FeroxSerialize,
@@ -61,6 +62,9 @@ pub struct FeroxScans {
 
     /// vector of extensions discovered and collected during scans
     pub(crate) collected_extensions: RwLock<HashSet<String>>,
+
+    /// stored value for Configuration.limit_bars
+    bar_limit: usize,
 }
 
 /// Serialize implementation for FeroxScans
@@ -93,9 +97,10 @@ impl Serialize for FeroxScans {
 /// Implementation of `FeroxScans`
 impl FeroxScans {
     /// given an OutputLevel, create a new FeroxScans object
-    pub fn new(output_level: OutputLevel) -> Self {
+    pub fn new(output_level: OutputLevel, bar_limit: usize) -> Self {
         Self {
             output_level,
+            bar_limit,
             ..Default::default()
         }
     }
@@ -388,8 +393,9 @@ impl FeroxScans {
 
             if input == 'y' || input == '\n' {
                 self.menu.println(&format!("Stopping {}...", selected.url));
+                let active_bars = self.number_of_bars();
                 selected
-                    .abort()
+                    .abort(active_bars)
                     .await
                     .unwrap_or_else(|e| log::warn!("Could not cancel task: {}", e));
 
@@ -521,14 +527,22 @@ impl FeroxScans {
 
     /// if a resumed scan is already complete, display a completed progress bar to the user
     pub fn print_completed_bars(&self, bar_length: usize) -> Result<()> {
-        let bar_type = match self.output_level {
-            OutputLevel::Default => BarType::Message,
-            OutputLevel::Quiet => BarType::Quiet,
-            OutputLevel::Silent | OutputLevel::SilentJSON => return Ok(()), // fast exit when --silent was used
-        };
+        if self.output_level == OutputLevel::SilentJSON || self.output_level == OutputLevel::Silent
+        {
+            // fast exit when --silent was used
+            return Ok(());
+        }
+
+        let bar_type: BarType =
+            determine_bar_type(self.bar_limit, self.number_of_bars(), self.output_level);
 
         if let Ok(scans) = self.scans.read() {
             for scan in scans.iter() {
+                if matches!(bar_type, BarType::Hidden) {
+                    // no need to show hidden bars
+                    continue;
+                }
+
                 if scan.is_complete() {
                     // these scans are complete, and just need to be shown to the user
                     let pb = add_bar(
@@ -605,6 +619,7 @@ impl FeroxScans {
         url: &str,
         scan_type: ScanType,
         scan_order: ScanOrder,
+        handles: Arc<Handles>,
     ) -> (bool, Arc<FeroxScan>) {
         let bar_length = if let Ok(guard) = self.bar_length.lock() {
             *guard
@@ -612,14 +627,11 @@ impl FeroxScans {
             0
         };
 
+        let active_bars = self.number_of_bars();
+        let bar_type = determine_bar_type(self.bar_limit, active_bars, self.output_level);
+
         let bar = match scan_type {
             ScanType::Directory => {
-                let bar_type = match self.output_level {
-                    OutputLevel::Default => BarType::Default,
-                    OutputLevel::Quiet => BarType::Quiet,
-                    OutputLevel::Silent | OutputLevel::SilentJSON => BarType::Hidden,
-                };
-
                 let progress_bar = add_bar(url, bar_length, bar_type);
 
                 progress_bar.reset_elapsed();
@@ -629,6 +641,8 @@ impl FeroxScans {
             ScanType::File => None,
         };
 
+        let is_visible = !matches!(bar_type, BarType::Hidden);
+
         let ferox_scan = FeroxScan::new(
             url,
             scan_type,
@@ -636,6 +650,8 @@ impl FeroxScans {
             bar_length,
             self.output_level,
             bar,
+            is_visible,
+            handles,
         );
 
         // If the set did not contain the scan, true is returned.
@@ -650,9 +666,14 @@ impl FeroxScans {
     /// If `FeroxScans` did not already contain the scan, return true; otherwise return false
     ///
     /// Also return a reference to the new `FeroxScan`
-    pub fn add_directory_scan(&self, url: &str, scan_order: ScanOrder) -> (bool, Arc<FeroxScan>) {
+    pub fn add_directory_scan(
+        &self,
+        url: &str,
+        scan_order: ScanOrder,
+        handles: Arc<Handles>,
+    ) -> (bool, Arc<FeroxScan>) {
         let normalized = format!("{}/", url.trim_end_matches('/'));
-        self.add_scan(&normalized, ScanType::Directory, scan_order)
+        self.add_scan(&normalized, ScanType::Directory, scan_order, handles)
     }
 
     /// Given a url, create a new `FeroxScan` and add it to `FeroxScans` as a File Scan
@@ -660,8 +681,65 @@ impl FeroxScans {
     /// If `FeroxScans` did not already contain the scan, return true; otherwise return false
     ///
     /// Also return a reference to the new `FeroxScan`
-    pub fn add_file_scan(&self, url: &str, scan_order: ScanOrder) -> (bool, Arc<FeroxScan>) {
-        self.add_scan(url, ScanType::File, scan_order)
+    pub fn add_file_scan(
+        &self,
+        url: &str,
+        scan_order: ScanOrder,
+        handles: Arc<Handles>,
+    ) -> (bool, Arc<FeroxScan>) {
+        self.add_scan(url, ScanType::File, scan_order, handles)
+    }
+
+    /// returns the number of active AND visible scans; supports --limit-bars functionality
+    pub fn number_of_bars(&self) -> usize {
+        let Ok(scans) = self.scans.read() else {
+            return 0;
+        };
+
+        // starting at one ensures we don't have an extra bar
+        // due to counting up from 0 when there's actually 1 bar
+        let mut count = 1;
+
+        for scan in &*scans {
+            if scan.is_active() && scan.visible() {
+                count += 1;
+            }
+        }
+
+        count
+    }
+
+    /// make one hidden bar visible; supports --limit-bars functionality
+    pub fn make_visible(&self) {
+        if let Ok(guard) = self.scans.read() {
+            // when swapping visibility, we'll prefer an actively running scan
+            // if none are found, we'll
+            let mut queued = None;
+
+            for scan in &*guard {
+                if !matches!(scan.scan_type, ScanType::Directory) {
+                    // visibility only makes sense for directory scans
+                    continue;
+                }
+
+                if scan.visible() {
+                    continue;
+                }
+
+                if scan.is_running() {
+                    scan.swap_visibility();
+                    return;
+                }
+
+                if queued.is_none() && scan.is_not_started() {
+                    queued = Some(scan.clone());
+                }
+            }
+
+            if let Some(scan) = queued {
+                scan.swap_visibility();
+            }
+        }
     }
 
     /// small helper to determine whether any scans are active or not
@@ -726,7 +804,7 @@ mod tests {
     #[test]
     /// unknown extension should be added to collected_extensions
     fn unknown_extension_is_added_to_collected_extensions() {
-        let scans = FeroxScans::new(OutputLevel::Default);
+        let scans = FeroxScans::new(OutputLevel::Default, 0);
 
         assert_eq!(0, scans.collected_extensions.read().unwrap().len());
 
@@ -739,7 +817,7 @@ mod tests {
     #[test]
     /// known extension should not be added to collected_extensions
     fn known_extension_is_added_to_collected_extensions() {
-        let scans = FeroxScans::new(OutputLevel::Default);
+        let scans = FeroxScans::new(OutputLevel::Default, 0);
         scans
             .collected_extensions
             .write()
diff --git a/src/scan_manager/utils.rs b/src/scan_manager/utils.rs
index 18449576..6f06e087 100644
--- a/src/scan_manager/utils.rs
+++ b/src/scan_manager/utils.rs
@@ -1,7 +1,12 @@
 #[cfg(not(test))]
 use crate::event_handlers::TermInputHandler;
 use crate::{
-    config::Configuration, event_handlers::Handles, parser::TIMESPEC_REGEX, scanner::RESPONSES,
+    config::{Configuration, OutputLevel},
+    event_handlers::Handles,
+    parser::TIMESPEC_REGEX,
+    progress::BarType,
+    scan_manager::scan::Visibility,
+    scanner::RESPONSES,
 };
 
 use std::{fs::File, io::BufReader, sync::Arc};
@@ -90,3 +95,79 @@ pub fn resume_scan(filename: &str) -> Configuration {
     log::trace!("exit: resume_scan -> {:?}", config);
     config
 }
+
+/// determine the type of progress bar to display
+/// takes both --limit-bars and output-level (--quiet|--silent|etc)
+/// into account to arrive at a `BarType`
+pub fn determine_bar_type(
+    bar_limit: usize,
+    number_of_bars: usize,
+    output_level: OutputLevel,
+) -> BarType {
+    let visibility = if bar_limit == 0 {
+        // no limit from cli, just set the value to visible
+        // this protects us from a mutex unlock in number_of_bars
+        // in the normal case
+        Visibility::Visible
+    } else if bar_limit < number_of_bars {
+        // active bars exceed limit; hidden
+        Visibility::Hidden
+    } else {
+        Visibility::Visible
+    };
+
+    match (output_level, visibility) {
+        (OutputLevel::Default, Visibility::Visible) => BarType::Default,
+        (OutputLevel::Quiet, Visibility::Visible) => BarType::Quiet,
+        (OutputLevel::Default, Visibility::Hidden) => BarType::Hidden,
+        (OutputLevel::Quiet, Visibility::Hidden) => BarType::Hidden,
+        (OutputLevel::Silent | OutputLevel::SilentJSON, _) => BarType::Hidden,
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_no_limit_visible() {
+        let bar_type = determine_bar_type(0, 1, OutputLevel::Default);
+        assert!(matches!(bar_type, BarType::Default));
+    }
+
+    #[test]
+    fn test_limit_exceeded_hidden() {
+        let bar_type = determine_bar_type(1, 2, OutputLevel::Default);
+        assert!(matches!(bar_type, BarType::Hidden));
+    }
+
+    #[test]
+    fn test_limit_not_exceeded_visible() {
+        let bar_type = determine_bar_type(2, 1, OutputLevel::Default);
+        assert!(matches!(bar_type, BarType::Default));
+    }
+
+    #[test]
+    fn test_quiet_visible() {
+        let bar_type = determine_bar_type(0, 1, OutputLevel::Quiet);
+        assert!(matches!(bar_type, BarType::Quiet));
+    }
+
+    #[test]
+    fn test_quiet_hidden() {
+        let bar_type = determine_bar_type(1, 2, OutputLevel::Quiet);
+        assert!(matches!(bar_type, BarType::Hidden));
+    }
+
+    #[test]
+    fn test_silent_hidden() {
+        let bar_type = determine_bar_type(0, 1, OutputLevel::Silent);
+        assert!(matches!(bar_type, BarType::Hidden));
+    }
+
+    #[test]
+    fn test_silent_json_hidden() {
+        let bar_type = determine_bar_type(0, 1, OutputLevel::SilentJSON);
+        assert!(matches!(bar_type, BarType::Hidden));
+    }
+}
diff --git a/src/scanner/ferox_scanner.rs b/src/scanner/ferox_scanner.rs
index 03b3a863..7959f606 100644
--- a/src/scanner/ferox_scanner.rs
+++ b/src/scanner/ferox_scanner.rs
@@ -283,6 +283,14 @@ impl FeroxScanner {
 
                 let mut message = format!("=> {}", style("Directory listing").blue().bright());
 
+                if !self.handles.config.scan_dir_listings {
+                    write!(
+                        message,
+                        " (add {} to scan)",
+                        style("--scan-dir-listings").bright().yellow()
+                    )?;
+                }
+
                 if !self.handles.config.extract_links {
                     write!(
                         message,
@@ -291,7 +299,7 @@ impl FeroxScanner {
                     )?;
                 }
 
-                if !self.handles.config.force_recursion {
+                if !self.handles.config.force_recursion && !self.handles.config.scan_dir_listings {
                     for handle in extraction_tasks.into_iter().flatten() {
                         _ = handle.await;
                     }
@@ -299,7 +307,14 @@ impl FeroxScanner {
                     progress_bar.reset_eta();
                     progress_bar.finish_with_message(message);
 
-                    ferox_scan.finish()?;
+                    if self.handles.config.limit_bars > 0 {
+                        let scans = self.handles.ferox_scans()?;
+                        let num_bars = scans.number_of_bars();
+                        ferox_scan.finish(num_bars)?;
+                        scans.make_visible();
+                    } else {
+                        ferox_scan.finish(0)?;
+                    }
 
                     return Ok(()); // nothing left to do if we found a dir listing
                 }
@@ -382,7 +397,14 @@ impl FeroxScanner {
             _ = handle.await;
         }
 
-        ferox_scan.finish()?;
+        if self.handles.config.limit_bars > 0 {
+            let scans = self.handles.ferox_scans()?;
+            let num_bars = scans.number_of_bars();
+            ferox_scan.finish(num_bars)?;
+            scans.make_visible();
+        } else {
+            ferox_scan.finish(0)?;
+        }
 
         log::trace!("exit: scan_url");
 
diff --git a/src/scanner/requester.rs b/src/scanner/requester.rs
index 0517ecab..2fe0f30d 100644
--- a/src/scanner/requester.rs
+++ b/src/scanner/requester.rs
@@ -313,9 +313,12 @@ impl Requester {
                 .set_status(ScanStatus::Cancelled)
                 .unwrap_or_else(|e| log::warn!("Could not set scan status: {}", e));
 
+            let scans = self.handles.ferox_scans()?;
+            let active_bars = scans.number_of_bars();
+
             // kill the scan
             self.ferox_scan
-                .abort()
+                .abort(active_bars)
                 .await
                 .unwrap_or_else(|e| log::warn!("Could not bail on scan: {}", e));
 
@@ -646,6 +649,8 @@ mod tests {
             1000,
             OutputLevel::Default,
             None,
+            true,
+            handles.clone(),
         );
 
         scan.set_status(ScanStatus::Running).unwrap();
@@ -1144,6 +1149,8 @@ mod tests {
             1000,
             OutputLevel::Default,
             None,
+            true,
+            Arc::new(Handles::for_testing(None, None).0),
         );
         scan.set_status(ScanStatus::Running).unwrap();
         scan.add_429();
@@ -1177,7 +1184,7 @@ mod tests {
             200
         );
 
-        scan.finish().unwrap();
+        scan.finish(0).unwrap();
         assert!(start.elapsed().as_millis() >= 2000);
     }
 }
diff --git a/src/utils.rs b/src/utils.rs
index b9ae6fa9..b0cc7fc3 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -975,7 +975,11 @@ mod tests {
         let tested_url = Url::parse("https://testdomain.com/denied/").unwrap();
 
         let scans = Arc::new(FeroxScans::default());
-        scans.add_directory_scan(scan_url, ScanOrder::Initial);
+        scans.add_directory_scan(
+            scan_url,
+            ScanOrder::Initial,
+            Arc::new(Handles::for_testing(None, None).0),
+        );
 
         let mut config = Configuration::new().unwrap();
         config.url_denylist = vec![Url::parse(deny_url).unwrap()];
@@ -994,7 +998,11 @@ mod tests {
         let tested_url = Url::parse("https://testdomain.com/denied/").unwrap();
 
         let scans = Arc::new(FeroxScans::default());
-        scans.add_directory_scan(scan_url, ScanOrder::Initial);
+        scans.add_directory_scan(
+            scan_url,
+            ScanOrder::Initial,
+            Arc::new(Handles::for_testing(None, None).0),
+        );
 
         let mut config = Configuration::new().unwrap();
         config.url_denylist = vec![Url::parse(deny_url).unwrap()];
@@ -1013,7 +1021,11 @@ mod tests {
         let tested_url = Url::parse("https://testdomain.com/denied/").unwrap();
 
         let scans = Arc::new(FeroxScans::default());
-        scans.add_directory_scan(scan_url, ScanOrder::Initial);
+        scans.add_directory_scan(
+            scan_url,
+            ScanOrder::Initial,
+            Arc::new(Handles::for_testing(None, None).0),
+        );
 
         let mut config = Configuration::new().unwrap();
         config.url_denylist = vec![Url::parse(deny_url).unwrap()];
@@ -1034,7 +1046,11 @@ mod tests {
         let tested_url = Url::parse("https://testdomain.com/denied/").unwrap();
 
         let scans = Arc::new(FeroxScans::default());
-        scans.add_directory_scan(scan_url, ScanOrder::Initial);
+        scans.add_directory_scan(
+            scan_url,
+            ScanOrder::Initial,
+            Arc::new(Handles::for_testing(None, None).0),
+        );
 
         let mut config = Configuration::new().unwrap();
         config.url_denylist = vec![Url::parse(deny_url).unwrap()];
@@ -1062,7 +1078,11 @@ mod tests {
         let tested_url = Url::parse("https://testdomain.com/denied/").unwrap();
 
         let scans = Arc::new(FeroxScans::default());
-        scans.add_directory_scan(scan_url, ScanOrder::Initial);
+        scans.add_directory_scan(
+            scan_url,
+            ScanOrder::Initial,
+            Arc::new(Handles::for_testing(None, None).0),
+        );
 
         let mut config = Configuration::new().unwrap();
         config.url_denylist = vec![Url::parse(deny_url).unwrap()];
@@ -1080,7 +1100,11 @@ mod tests {
         let tested_url = Url::parse("https://testdomain.com/api/denied/").unwrap();
 
         let scans = Arc::new(FeroxScans::default());
-        scans.add_directory_scan(scan_url, ScanOrder::Initial);
+        scans.add_directory_scan(
+            scan_url,
+            ScanOrder::Initial,
+            Arc::new(Handles::for_testing(None, None).0),
+        );
 
         let mut config = Configuration::new().unwrap();
         config.url_denylist = vec![Url::parse(deny_url).unwrap()];
@@ -1099,7 +1123,11 @@ mod tests {
         let tested_url = Url::parse("https://testdomain.com/not-denied/").unwrap();
 
         let scans = Arc::new(FeroxScans::default());
-        scans.add_directory_scan(scan_url, ScanOrder::Initial);
+        scans.add_directory_scan(
+            scan_url,
+            ScanOrder::Initial,
+            Arc::new(Handles::for_testing(None, None).0),
+        );
 
         let mut config = Configuration::new().unwrap();
         config.url_denylist = vec![Url::parse(deny_url).unwrap()];
@@ -1118,7 +1146,11 @@ mod tests {
         let tested_url = Url::parse("https://testdomain.com/stuff/").unwrap();
 
         let scans = Arc::new(FeroxScans::default());
-        scans.add_directory_scan(scan_url, ScanOrder::Initial);
+        scans.add_directory_scan(
+            scan_url,
+            ScanOrder::Initial,
+            Arc::new(Handles::for_testing(None, None).0),
+        );
 
         let mut config = Configuration::new().unwrap();
         config.url_denylist = vec![Url::parse(deny_url).unwrap()];
@@ -1137,7 +1169,11 @@ mod tests {
         let tested_url = Url::parse("https://testdomain.com/api/not-denied/").unwrap();
 
         let scans = Arc::new(FeroxScans::default());
-        scans.add_directory_scan(scan_url, ScanOrder::Initial);
+        scans.add_directory_scan(
+            scan_url,
+            ScanOrder::Initial,
+            Arc::new(Handles::for_testing(None, None).0),
+        );
 
         let mut config = Configuration::new().unwrap();
         config.url_denylist = vec![Url::parse(deny_url).unwrap()];
@@ -1157,7 +1193,11 @@ mod tests {
         let tested_url = Url::parse("https://testdomain.com/denied/").unwrap();
 
         let scans = Arc::new(FeroxScans::default());
-        scans.add_directory_scan(scan_url, ScanOrder::Initial);
+        scans.add_directory_scan(
+            scan_url,
+            ScanOrder::Initial,
+            Arc::new(Handles::for_testing(None, None).0),
+        );
 
         let mut config = Configuration::new().unwrap();
         config.regex_denylist = vec![Regex::new(deny_pattern).unwrap()];
@@ -1178,7 +1218,11 @@ mod tests {
         let tested_https_url = Url::parse("https://testdomain.com/denied/").unwrap();
 
         let scans = Arc::new(FeroxScans::default());
-        scans.add_directory_scan(scan_url, ScanOrder::Initial);
+        scans.add_directory_scan(
+            scan_url,
+            ScanOrder::Initial,
+            Arc::new(Handles::for_testing(None, None).0),
+        );
 
         let mut config = Configuration::new().unwrap();
         config.regex_denylist = vec![Regex::new(deny_pattern).unwrap()];


Test patch:
diff --git a/src/config/tests.rs b/src/config/tests.rs
index cbde9312..06affb33 100644
--- a/src/config/tests.rs
+++ b/src/config/tests.rs
@@ -49,6 +49,10 @@ fn setup_config_test() -> Configuration {
             json = true
             save_state = false
             depth = 1
+            limit_bars = 3
+            protocol = "http"
+            request_file = "/some/request/file"
+            scan_dir_listings = true
             force_recursion = true
             filter_size = [4120]
             filter_regex = ["^ignore me$"]
@@ -87,6 +91,7 @@ fn default_configuration() {
     assert_eq!(config.timeout, timeout());
     assert_eq!(config.verbosity, 0);
     assert_eq!(config.scan_limit, 0);
+    assert_eq!(config.limit_bars, 0);
     assert!(!config.silent);
     assert!(!config.quiet);
     assert_eq!(config.output_level, OutputLevel::Default);
@@ -107,6 +112,7 @@ fn default_configuration() {
     assert!(!config.collect_extensions);
     assert!(!config.collect_backups);
     assert!(!config.collect_words);
+    assert!(!config.scan_dir_listings);
     assert!(config.regex_denylist.is_empty());
     assert_eq!(config.queries, Vec::new());
     assert_eq!(config.filter_size, Vec::<u64>::new());
@@ -125,6 +131,8 @@ fn default_configuration() {
     assert_eq!(config.client_cert, String::new());
     assert_eq!(config.client_key, String::new());
     assert_eq!(config.backup_extensions, backup_extensions());
+    assert_eq!(config.protocol, request_protocol());
+    assert_eq!(config.request_file, String::new());
 }
 
 #[test]
@@ -260,6 +268,13 @@ fn config_reads_verbosity() {
     assert_eq!(config.verbosity, 1);
 }
 
+#[test]
+/// parse the test config and see that the value parsed is correct
+fn config_reads_limit_bars() {
+    let config = setup_config_test();
+    assert_eq!(config.limit_bars, 3);
+}
+
 #[test]
 /// parse the test config and see that the value parsed is correct
 fn config_reads_output() {
@@ -444,6 +459,27 @@ fn config_reads_time_limit() {
     assert_eq!(config.time_limit, "10m");
 }
 
+#[test]
+/// parse the test config and see that the value parsed is correct
+fn config_reads_scan_dir_listings() {
+    let config = setup_config_test();
+    assert!(config.scan_dir_listings);
+}
+
+#[test]
+/// parse the test config and see that the value parsed is correct
+fn config_reads_protocol() {
+    let config = setup_config_test();
+    assert_eq!(config.protocol, "http");
+}
+
+#[test]
+/// parse the test config and see that the value parsed is correct
+fn config_reads_request_file() {
+    let config = setup_config_test();
+    assert_eq!(config.request_file, String::new());
+}
+
 #[test]
 /// parse the test config and see that the value parsed is correct
 fn config_reads_resume_from() {
diff --git a/src/extractor/tests.rs b/src/extractor/tests.rs
index 68221f59..5b8b19a3 100644
--- a/src/extractor/tests.rs
+++ b/src/extractor/tests.rs
@@ -386,7 +386,11 @@ async fn request_link_bails_on_seen_url() -> Result<()> {
     });
 
     let scans = Arc::new(FeroxScans::default());
-    scans.add_file_scan(&served, ScanOrder::Latest);
+    scans.add_file_scan(
+        &served,
+        ScanOrder::Latest,
+        Arc::new(Handles::for_testing(None, None).0),
+    );
 
     let robots = setup_extractor(ExtractionTarget::RobotsTxt, scans.clone());
     let body = setup_extractor(ExtractionTarget::ResponseBody, scans);
diff --git a/src/scan_manager/tests.rs b/src/scan_manager/tests.rs
index a3bb460f..25f37ceb 100644
--- a/src/scan_manager/tests.rs
+++ b/src/scan_manager/tests.rs
@@ -15,6 +15,7 @@ use crate::{
 use indicatif::ProgressBar;
 use predicates::prelude::*;
 use regex::Regex;
+use std::sync::atomic::AtomicBool;
 use std::sync::{atomic::Ordering, Arc};
 use std::thread::sleep;
 use std::time::Instant;
@@ -57,7 +58,12 @@ async fn scanner_pause_scan_with_finished_spinner() {
 fn add_url_to_list_of_scanned_urls_with_unknown_url() {
     let urls = FeroxScans::default();
     let url = "http://unknown_url";
-    let (result, _scan) = urls.add_scan(url, ScanType::Directory, ScanOrder::Latest);
+    let (result, _scan) = urls.add_scan(
+        url,
+        ScanType::Directory,
+        ScanOrder::Latest,
+        Arc::new(Handles::for_testing(None, None).0),
+    );
     assert!(result);
 }
 
@@ -75,11 +81,18 @@ fn add_url_to_list_of_scanned_urls_with_known_url() {
         pb.length().unwrap(),
         OutputLevel::Default,
         Some(pb),
+        true,
+        Arc::new(Handles::for_testing(None, None).0),
     );
 
     assert!(urls.insert(scan));
 
-    let (result, _scan) = urls.add_scan(url, ScanType::Directory, ScanOrder::Latest);
+    let (result, _scan) = urls.add_scan(
+        url,
+        ScanType::Directory,
+        ScanOrder::Latest,
+        Arc::new(Handles::for_testing(None, None).0),
+    );
 
     assert!(!result);
 }
@@ -97,6 +110,8 @@ fn stop_progress_bar_stops_bar() {
         pb.length().unwrap(),
         OutputLevel::Default,
         Some(pb),
+        true,
+        Arc::new(Handles::for_testing(None, None).0),
     );
 
     assert!(!scan
@@ -107,7 +122,7 @@ fn stop_progress_bar_stops_bar() {
         .unwrap()
         .is_finished());
 
-    scan.stop_progress_bar();
+    scan.stop_progress_bar(0);
 
     assert!(scan
         .progress_bar
@@ -124,18 +139,25 @@ fn add_url_to_list_of_scanned_urls_with_known_url_without_slash() {
     let urls = FeroxScans::default();
     let url = "http://unknown_url";
 
-    let scan = FeroxScan::new(
+    let scan: Arc<FeroxScan> = FeroxScan::new(
         url,
         ScanType::File,
         ScanOrder::Latest,
         0,
         OutputLevel::Default,
         None,
+        true,
+        Arc::new(Handles::for_testing(None, None).0),
     );
 
     assert!(urls.insert(scan));
 
-    let (result, _scan) = urls.add_scan(url, ScanType::File, ScanOrder::Latest);
+    let (result, _scan) = urls.add_scan(
+        url,
+        ScanType::File,
+        ScanOrder::Latest,
+        Arc::new(Handles::for_testing(None, None).0),
+    );
 
     assert!(!result);
 }
@@ -155,6 +177,8 @@ async fn call_display_scans() {
         pb.length().unwrap(),
         OutputLevel::Default,
         Some(pb),
+        true,
+        Arc::new(Handles::for_testing(None, None).0),
     );
     let scan_two = FeroxScan::new(
         url_two,
@@ -163,9 +187,11 @@ async fn call_display_scans() {
         pb_two.length().unwrap(),
         OutputLevel::Default,
         Some(pb_two),
+        true,
+        Arc::new(Handles::for_testing(None, None).0),
     );
 
-    scan_two.finish().unwrap(); // one complete, one incomplete
+    scan_two.finish(0).unwrap(); // one complete, one incomplete
     scan_two
         .set_task(tokio::spawn(async move {
             sleep(Duration::from_millis(SLEEP_DURATION));
@@ -190,6 +216,8 @@ fn partial_eq_compares_the_id_field() {
         0,
         OutputLevel::Default,
         None,
+        true,
+        Arc::new(Handles::for_testing(None, None).0),
     );
     let scan_two = FeroxScan::new(
         url,
@@ -198,6 +226,8 @@ fn partial_eq_compares_the_id_field() {
         0,
         OutputLevel::Default,
         None,
+        true,
+        Arc::new(Handles::for_testing(None, None).0),
     );
 
     assert!(!scan.eq(&scan_two));
@@ -280,6 +310,8 @@ fn ferox_scan_serialize() {
         0,
         OutputLevel::Default,
         None,
+        true,
+        Arc::new(Handles::for_testing(None, None).0),
     );
     let fs_json = format!(
         r#"{{"id":"{}","url":"https://spiritanimal.com","normalized_url":"https://spiritanimal.com/","scan_type":"Directory","status":"NotStarted","num_requests":0,"requests_made_so_far":0}}"#,
@@ -298,6 +330,8 @@ fn ferox_scans_serialize() {
         0,
         OutputLevel::Default,
         None,
+        true,
+        Arc::new(Handles::for_testing(None, None).0),
     );
     let ferox_scans = FeroxScans::default();
     let ferox_scans_json = format!(
@@ -360,6 +394,8 @@ fn feroxstates_feroxserialize_implementation() {
         0,
         OutputLevel::Default,
         None,
+        true,
+        Arc::new(Handles::for_testing(None, None).0),
     );
     let ferox_scans = FeroxScans::default();
     let saved_id = ferox_scan.id.clone();
@@ -501,12 +537,15 @@ fn feroxstates_feroxserialize_implementation() {
         r#""method":"GET""#,
         r#""content_length":173"#,
         r#""line_count":10"#,
+        r#""limit_bars":0"#,
         r#""word_count":16"#,
         r#""headers""#,
         r#""server":"nginx/1.16.1"#,
         r#""collect_extensions":true"#,
         r#""collect_backups":false"#,
         r#""collect_words":false"#,
+        r#""scan_dir_listings":false"#,
+        r#""protocol":"https""#,
         r#""filters":[{"filter_code":100},{"word_count":200},{"content_length":300},{"line_count":400},{"compiled":".*","raw_string":".*"},{"hash":1,"original_url":"http://localhost:12345/"}]"#,
         r#""collected_extensions":["php"]"#,
         r#""dont_collect":["tif","tiff","ico","cur","bmp","webp","svg","png","jpg","jpeg","jfif","gif","avif","apng","pjpeg","pjp","mov","wav","mpg","mpeg","mp3","mp4","m4a","m4p","m4v","ogg","webm","ogv","oga","flac","aac","3gp","css","zip","xls","xml","gz","tgz"]"#,
@@ -567,8 +606,10 @@ fn feroxscan_display() {
         normalized_url: String::from("http://localhost/"),
         scan_order: ScanOrder::Latest,
         scan_type: Default::default(),
+        handles: Some(Arc::new(Handles::for_testing(None, None).0)),
         num_requests: 0,
         requests_made_so_far: 0,
+        visible: AtomicBool::new(true),
         start_time: Instant::now(),
         output_level: OutputLevel::Default,
         status_403s: Default::default(),
@@ -617,6 +658,7 @@ async fn ferox_scan_abort() {
         requests_made_so_far: 0,
         start_time: Instant::now(),
         output_level: OutputLevel::Default,
+        visible: AtomicBool::new(true),
         status_403s: Default::default(),
         status_429s: Default::default(),
         status: std::sync::Mutex::new(ScanStatus::Running),
@@ -625,9 +667,10 @@ async fn ferox_scan_abort() {
         }))),
         progress_bar: std::sync::Mutex::new(None),
         errors: Default::default(),
+        handles: Some(Arc::new(Handles::for_testing(None, None).0)),
     };
 
-    scan.abort().await.unwrap();
+    scan.abort(0).await.unwrap();
 
     assert!(matches!(
         *scan.status.lock().unwrap(),
@@ -718,15 +761,26 @@ fn split_to_nums_is_correct() {
 #[test]
 /// given a deep url, find the correct scan
 fn get_base_scan_by_url_finds_correct_scan() {
+    let handles = Arc::new(Handles::for_testing(None, None).0);
     let urls = FeroxScans::default();
     let url = "http://localhost";
     let url1 = "http://localhost/stuff";
     let url2 = "http://shlocalhost/stuff/things";
     let url3 = "http://shlocalhost/stuff/things/mostuff";
-    let (_, scan) = urls.add_scan(url, ScanType::Directory, ScanOrder::Latest);
-    let (_, scan1) = urls.add_scan(url1, ScanType::Directory, ScanOrder::Latest);
-    let (_, scan2) = urls.add_scan(url2, ScanType::Directory, ScanOrder::Latest);
-    let (_, scan3) = urls.add_scan(url3, ScanType::Directory, ScanOrder::Latest);
+    let (_, scan) = urls.add_scan(url, ScanType::Directory, ScanOrder::Latest, handles.clone());
+    let (_, scan1) = urls.add_scan(
+        url1,
+        ScanType::Directory,
+        ScanOrder::Latest,
+        handles.clone(),
+    );
+    let (_, scan2) = urls.add_scan(
+        url2,
+        ScanType::Directory,
+        ScanOrder::Latest,
+        handles.clone(),
+    );
+    let (_, scan3) = urls.add_scan(url3, ScanType::Directory, ScanOrder::Latest, handles);
 
     assert_eq!(
         urls.get_base_scan_by_url("http://localhost/things.php")
@@ -759,7 +813,12 @@ fn get_base_scan_by_url_finds_correct_scan() {
 fn get_base_scan_by_url_finds_correct_scan_without_trailing_slash() {
     let urls = FeroxScans::default();
     let url = "http://localhost";
-    let (_, scan) = urls.add_scan(url, ScanType::Directory, ScanOrder::Latest);
+    let (_, scan) = urls.add_scan(
+        url,
+        ScanType::Directory,
+        ScanOrder::Latest,
+        Arc::new(Handles::for_testing(None, None).0),
+    );
     assert_eq!(
         urls.get_base_scan_by_url("http://localhost/BKPMiherrortBPKcw")
             .unwrap()
@@ -773,7 +832,12 @@ fn get_base_scan_by_url_finds_correct_scan_without_trailing_slash() {
 fn get_base_scan_by_url_finds_correct_scan_with_trailing_slash() {
     let urls = FeroxScans::default();
     let url = "http://127.0.0.1:41971/";
-    let (_, scan) = urls.add_scan(url, ScanType::Directory, ScanOrder::Latest);
+    let (_, scan) = urls.add_scan(
+        url,
+        ScanType::Directory,
+        ScanOrder::Latest,
+        Arc::new(Handles::for_testing(None, None).0),
+    );
     assert_eq!(
         urls.get_base_scan_by_url("http://127.0.0.1:41971/BKPMiherrortBPKcw")
             .unwrap()
diff --git a/src/scanner/tests.rs b/src/scanner/tests.rs
index d7e0323c..698f1641 100644
--- a/src/scanner/tests.rs
+++ b/src/scanner/tests.rs
@@ -15,7 +15,7 @@ use super::*;
 /// try to hit struct field coverage of FileOutHandler
 async fn get_scan_by_url_bails_on_unfound_url() {
     let sem = Semaphore::new(10);
-    let urls = FeroxScans::new(OutputLevel::Default);
+    let urls = FeroxScans::new(OutputLevel::Default, 0);
 
     let scanner = FeroxScanner::new(
         "http://localhost",
diff --git a/tests/test_banner.rs b/tests/test_banner.rs
index 96d8a4f0..19623ebb 100644
--- a/tests/test_banner.rs
+++ b/tests/test_banner.rs
@@ -1487,6 +1487,89 @@ fn banner_prints_force_recursion() {
         );
 }
 
+#[test]
+/// test allows non-existent wordlist to trigger the banner printing to stderr
+/// expect to see all mandatory prints + scan-dir-listings
+fn banner_prints_scan_dir_listings() {
+    Command::cargo_bin("feroxbuster")
+        .unwrap()
+        .arg("--url")
+        .arg("http://localhost")
+        .arg("--scan-dir-listings")
+        .arg("--wordlist")
+        .arg("/definitely/doesnt/exist/0cd7fed0-47f4-4b18-a1b0-ac39708c1676")
+        .assert()
+        .success()
+        .stderr(
+            predicate::str::contains("─┬─")
+                .and(predicate::str::contains("Target Url"))
+                .and(predicate::str::contains("http://localhost"))
+                .and(predicate::str::contains("Threads"))
+                .and(predicate::str::contains("Wordlist"))
+                .and(predicate::str::contains("Status Codes"))
+                .and(predicate::str::contains("Timeout (secs)"))
+                .and(predicate::str::contains("User-Agent"))
+                .and(predicate::str::contains("Scan Dir Listings"))
+                .and(predicate::str::contains("─┴─")),
+        );
+}
+
+#[test]
+/// test allows non-existent wordlist to trigger the banner printing to stderr
+/// expect to see all mandatory prints + protocol
+fn banner_prints_protocol() {
+    Command::cargo_bin("feroxbuster")
+        .unwrap()
+        .arg("--url")
+        .arg("localhost")
+        .arg("--protocol")
+        .arg("http")
+        .arg("--wordlist")
+        .arg("/definitely/doesnt/exist/0cd7fed0-47f4-4b18-a1b0-ac39708c1676")
+        .assert()
+        .success()
+        .stderr(
+            predicate::str::contains("─┬─")
+                .and(predicate::str::contains("Target Url"))
+                .and(predicate::str::contains("http://localhost"))
+                .and(predicate::str::contains("Threads"))
+                .and(predicate::str::contains("Wordlist"))
+                .and(predicate::str::contains("Status Codes"))
+                .and(predicate::str::contains("Timeout (secs)"))
+                .and(predicate::str::contains("User-Agent"))
+                .and(predicate::str::contains("Default Protocol"))
+                .and(predicate::str::contains("─┴─")),
+        );
+}
+
+#[test]
+/// test allows non-existent wordlist to trigger the banner printing to stderr
+/// expect to see all mandatory prints + protocol
+fn banner_prints_limit_dirs() {
+    Command::cargo_bin("feroxbuster")
+        .unwrap()
+        .arg("--url")
+        .arg("localhost")
+        .arg("--limit-bars")
+        .arg("3")
+        .arg("--wordlist")
+        .arg("/definitely/doesnt/exist/0cd7fed0-47f4-4b18-a1b0-ac39708c1676")
+        .assert()
+        .success()
+        .stderr(
+            predicate::str::contains("─┬─")
+                .and(predicate::str::contains("Target Url"))
+                .and(predicate::str::contains("http://localhost"))
+                .and(predicate::str::contains("Threads"))
+                .and(predicate::str::contains("Wordlist"))
+                .and(predicate::str::contains("Status Codes"))
+                .and(predicate::str::contains("Timeout (secs)"))
+                .and(predicate::str::contains("User-Agent"))
+                .and(predicate::str::contains("Limit Dir Scan Bars"))
+                .and(predicate::str::contains("─┴─")),
+        );
+}
+
 #[test]
 /// test allows non-existent wordlist to trigger the banner printing to stderr
 /// expect to see all mandatory prints + force recursion
diff --git a/tests/test_policies.rs b/tests/test_policies.rs
index 1de65ddd..9849d7df 100644
--- a/tests/test_policies.rs
+++ b/tests/test_policies.rs
@@ -58,7 +58,7 @@ fn auto_bail_cancels_scan_with_timeouts() {
     Command::cargo_bin("feroxbuster")
         .unwrap()
         .arg("--url")
-        .arg(&srv.url("/"))
+        .arg(srv.url("/"))
         .arg("--wordlist")
         .arg(file.as_os_str())
         .arg("--auto-bail")
diff --git a/tests/test_scanner.rs b/tests/test_scanner.rs
index b72fd456..058029d1 100644
--- a/tests/test_scanner.rs
+++ b/tests/test_scanner.rs
@@ -430,6 +430,7 @@ fn scanner_single_request_scan_with_filtered_result() -> Result<(), Box<dyn std:
 }
 
 #[test]
+#[should_panic] // added in 2.11.0 for panicking trace-level logging
 /// send a single valid request, get a response, and write the logging messages to disk
 fn scanner_single_request_scan_with_debug_logging() {
     let srv = MockServer::start();
@@ -467,6 +468,7 @@ fn scanner_single_request_scan_with_debug_logging() {
 }
 
 #[test]
+#[should_panic] // added in 2.11.0 for panicking trace-level logging
 /// send a single valid request, get a response, and write the logging messages to disk as NDJSON
 fn scanner_single_request_scan_with_debug_logging_as_json() {
     let srv = MockServer::start();