From d109608e2a2cc92690c690776783033a5c099385 Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 15 Jan 2022 17:27:07 -0600 Subject: [PATCH 1/3] added option groups; updated deps; clippy --- Cargo.lock | 99 +++-- Cargo.toml | 9 +- build.rs | 21 +- shell_completions/_feroxbuster | 153 ++++--- shell_completions/_feroxbuster.ps1 | 92 ++-- shell_completions/feroxbuster.bash | 111 +++-- shell_completions/feroxbuster.elv | 103 +++++ src/banner/container.rs | 17 +- src/config/container.rs | 50 ++- src/main.rs | 1 + src/parser.rs | 672 +++++++++++++++++------------ tests/test_scanner.rs | 2 +- 12 files changed, 779 insertions(+), 551 deletions(-) create mode 100644 shell_completions/feroxbuster.elv diff --git a/Cargo.lock b/Cargo.lock index db92d1fc..f7dfb8be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,15 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anyhow" version = "1.0.52" @@ -47,9 +38,9 @@ dependencies = [ [[package]] name = "assert_cmd" -version = "2.0.2" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e996dc7940838b7ef1096b882e29ec30a3149a3a443cdc8dba19ed382eca1fe2" +checksum = "93ae1ddd39efd67689deb1979d80bad3bf7f2b09c6e6117c8d1f2443b5e2f83e" dependencies = [ "bstr", "doc-comment", @@ -332,17 +323,28 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "2.34.0" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "12e8611f9ae4e068fa3e56931fded356ff745e70987ff76924a6e0ab1c8ef2e3" dependencies = [ - "ansi_term", "atty", "bitflags", + "indexmap", + "lazy_static", + "os_str_bytes", "strsim", + "termcolor", + "terminal_size", "textwrap", - "unicode-width", - "vec_map", +] + +[[package]] +name = "clap_complete" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fff450c061c4de8162fd6fa0a7a73f70f10c211869a34897724823ed9ec0046" +dependencies = [ + "clap", ] [[package]] @@ -601,6 +603,7 @@ dependencies = [ "anyhow", "assert_cmd", "clap", + "clap_complete", "console", "crossterm", "ctrlc", @@ -787,9 +790,9 @@ checksum = "8fb6c4351f4f134772edf9bcd17de13b7fbcb2c56928b440d6823bd4dc9ebd80" [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" dependencies = [ "cfg-if", "libc", @@ -879,9 +882,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "httpmock" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a24a520a3193799852cc4baf0d8356d7578a8847dfc5603d087529f099661e42" +checksum = "c159c4fc205e6c1a9b325cb7ec135d13b5f47188ce175dabb76ec847f331d9bd" dependencies = [ "assert-json-diff", "async-object-pool", @@ -896,13 +899,13 @@ dependencies = [ "lazy_static", "levenshtein", "log", - "qstring", "regex", "serde", "serde_json", "serde_regex", "similar", "tokio", + "url", ] [[package]] @@ -1315,9 +1318,9 @@ dependencies = [ [[package]] name = "openssl-probe" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" @@ -1342,6 +1345,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "os_str_bytes" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +dependencies = [ + "memchr", +] + [[package]] name = "parking" version = "2.0.0" @@ -1463,9 +1475,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95e5a7689e456ab905c22c2b48225bb921aba7c8dfa58440d68ba13f6222a715" +checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c" dependencies = [ "difflib", "float-cmp", @@ -1477,15 +1489,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451" +checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" [[package]] name = "predicates-tree" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338c7be2905b732ae3984a2f40032b5e94fd8f52505b186c7d4d68d193445df7" +checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" dependencies = [ "predicates-core", "termtree", @@ -1500,15 +1512,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "qstring" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" -dependencies = [ - "percent-encoding", -] - [[package]] name = "quote" version = "1.0.14" @@ -1780,9 +1783,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" [[package]] name = "socket2" @@ -1809,9 +1812,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" @@ -1876,11 +1879,11 @@ checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" [[package]] name = "textwrap" -version = "0.11.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" dependencies = [ - "unicode-width", + "terminal_size", ] [[package]] @@ -2133,12 +2136,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index bf7248f4..433f5702 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "feroxbuster" version = "2.5.0" -authors = ["Ben 'epi' Risher "] +authors = ["Ben 'epi' Risher (@epi052)"] license = "MIT" -edition = "2018" +edition = "2021" homepage = "https://github.com/epi052/feroxbuster" repository = "https://github.com/epi052/feroxbuster" description = "A fast, simple, recursive content discovery tool." @@ -16,7 +16,8 @@ build = "build.rs" maintenance = { status = "actively-developed" } [build-dependencies] -clap = "2.33" +clap = {version = "3.0", features = ["cargo"]} +clap_complete = "3.0" regex = "1" lazy_static = "1.4" dirs = "4.0" @@ -30,7 +31,7 @@ env_logger = "0.9" reqwest = { version = "0.11", features = ["socks"] } url = { version = "2.2", features = ["serde"]} # uses feature unification to add 'serde' to reqwest::Url serde_regex = "1.1" -clap = "2.34" +clap = {version = "3.0", features = ["wrap_help", "cargo"]} lazy_static = "1.4" toml = "0.5" serde = { version = "1.0", features = ["derive", "rc"] } diff --git a/build.rs b/build.rs index 591a1249..b0d409e0 100644 --- a/build.rs +++ b/build.rs @@ -1,9 +1,7 @@ use std::fs::{copy, create_dir_all, OpenOptions}; use std::io::{Read, Seek, SeekFrom, Write}; -extern crate clap; -extern crate dirs; -use clap::Shell; +use clap_complete::{generate_to, shells}; include!("src/parser.rs"); @@ -18,11 +16,20 @@ fn main() { let mut app = initialize(); - let shells: [Shell; 4] = [Shell::Bash, Shell::Fish, Shell::Zsh, Shell::PowerShell]; + let path = generate_to(shells::Bash, &mut app, "feroxbuster", outdir).unwrap(); + println!("cargo:warning=completion file is generated: {path:?}"); - for shell in &shells { - app.gen_completions("feroxbuster", *shell, outdir); - } + let path = generate_to(shells::Zsh, &mut app, "feroxbuster", outdir).unwrap(); + println!("cargo:warning=completion file is generated: {path:?}"); + + let path = generate_to(shells::Zsh, &mut app, "feroxbuster", outdir).unwrap(); + println!("cargo:warning=completion file is generated: {path:?}"); + + let path = generate_to(shells::PowerShell, &mut app, "feroxbuster", outdir).unwrap(); + println!("cargo:warning=completion file is generated: {path:?}"); + + let path = generate_to(shells::Elvish, &mut app, "feroxbuster", outdir).unwrap(); + println!("cargo:warning=completion file is generated: {path:?}"); // 0xdf pointed out an oddity when tab-completing options that expect file paths, the fix we // landed on was to add -o plusdirs to the bash completion script. The following code aims to diff --git a/shell_completions/_feroxbuster b/shell_completions/_feroxbuster index 62a940c1..cbc2fb65 100644 --- a/shell_completions/_feroxbuster +++ b/shell_completions/_feroxbuster @@ -15,95 +15,92 @@ _feroxbuster() { local context curcontext="$curcontext" state line _arguments "${_arguments_options[@]}" \ -'-w+[Path to the wordlist]' \ -'--wordlist=[Path to the wordlist]' \ -'*-u+[The target URL(s) (required, unless --stdin used)]' \ -'*--url=[The target URL(s) (required, unless --stdin used)]' \ -'-t+[Number of concurrent threads (default: 50)]' \ -'--threads=[Number of concurrent threads (default: 50)]' \ -'-d+[Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)]' \ -'--depth=[Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)]' \ -'-T+[Number of seconds before a request times out (default: 7)]' \ -'--timeout=[Number of seconds before a request times out (default: 7)]' \ -'-p+[Proxy to use for requests (ex: http(s)://host:port, socks5(h)://host:port)]' \ -'--proxy=[Proxy to use for requests (ex: http(s)://host:port, socks5(h)://host:port)]' \ -'-P+[Send only unfiltered requests through a Replay Proxy, instead of all requests]' \ -'--replay-proxy=[Send only unfiltered requests through a Replay Proxy, instead of all requests]' \ -'*-R+[Status Codes to send through a Replay Proxy when found (default: --status-codes value)]' \ -'*--replay-codes=[Status Codes to send through a Replay Proxy when found (default: --status-codes value)]' \ -'*-s+[Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405)]' \ -'*--status-codes=[Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405)]' \ -'-o+[Output file to write results to (use w/ --json for JSON entries)]' \ -'--output=[Output file to write results to (use w/ --json for JSON entries)]' \ -'(-u --url)--resume-from=[State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state)]' \ -'--debug-log=[Output file to write log entries (use w/ --json for JSON entries)]' \ -'-a+[Sets the User-Agent (default: feroxbuster/VERSION)]' \ -'--user-agent=[Sets the User-Agent (default: feroxbuster/VERSION)]' \ -'*-x+[File extension(s) to search for (ex: -x php -x pdf js)]' \ -'*--extensions=[File extension(s) to search for (ex: -x php -x pdf js)]' \ -'*-m+[HTTP request method(s) (default: GET)]' \ -'*--methods=[HTTP request method(s) (default: GET)]' \ -'--data=[HTTP Body data; can read data from a file if input starts with an @ (ex: @post.bin)]' \ -'*--dont-scan=[URL(s) or Regex Pattern(s) to exclude from recursion/scans]' \ -'*-H+[Specify HTTP headers (ex: -H Header:val '\''stuff: things'\'')]' \ -'*--headers=[Specify HTTP headers (ex: -H Header:val '\''stuff: things'\'')]' \ -'*-b+[Specify HTTP cookies (ex: -b stuff=things)]' \ -'*--cookies=[Specify HTTP cookies (ex: -b stuff=things)]' \ -'*-Q+[Specify URL query parameters (ex: -Q token=stuff -Q secret=key)]' \ -'*--query=[Specify URL query parameters (ex: -Q token=stuff -Q secret=key)]' \ -'*-S+[Filter out messages of a particular size (ex: -S 5120 -S 4927,1970)]' \ -'*--filter-size=[Filter out messages of a particular size (ex: -S 5120 -S 4927,1970)]' \ -'*-X+[Filter out messages via regular expression matching on the response'\''s body (ex: -X '\''^ignore me$'\'')]' \ -'*--filter-regex=[Filter out messages via regular expression matching on the response'\''s body (ex: -X '\''^ignore me$'\'')]' \ -'*-W+[Filter out messages of a particular word count (ex: -W 312 -W 91,82)]' \ -'*--filter-words=[Filter out messages of a particular word count (ex: -W 312 -W 91,82)]' \ -'*-N+[Filter out messages of a particular line count (ex: -N 20 -N 31,30)]' \ -'*--filter-lines=[Filter out messages of a particular line count (ex: -N 20 -N 31,30)]' \ -'*-C+[Filter out status codes (deny list) (ex: -C 200 -C 401)]' \ -'*--filter-status=[Filter out status codes (deny list) (ex: -C 200 -C 401)]' \ -'*--filter-similar-to=[Filter out pages that are similar to the given page (ex. --filter-similar-to http://site.xyz/soft404)]' \ -'-L+[Limit total number of concurrent scans (default: 0, i.e. no limit)]' \ -'--scan-limit=[Limit total number of concurrent scans (default: 0, i.e. no limit)]' \ -'--parallel=[Run parallel feroxbuster instances (one child process per url passed via stdin)]' \ -'(--auto-tune)--rate-limit=[Limit number of requests per second (per directory) (default: 0, i.e. no limit)]' \ -'--time-limit=[Limit total run time of all scans (ex: --time-limit 10m)]' \ -'(--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 + turn off logging (good for piping a list of urls to other commands)]' \ -'-q[Hide progress bars and banner (good for tmux windows w/ notifications)]' \ -'--quiet[Hide progress bars and banner (good for tmux windows w/ notifications)]' \ -'(--auto-bail)--auto-tune[Automatically lower scan rate when an excessive amount of errors are encountered]' \ -'--auto-bail[Automatically stop scanning when an excessive amount of errors are encountered]' \ -'--json[Emit JSON logs to --output and --debug-log instead of normal text]' \ -'-D[Don'\''t auto-filter wildcard responses]' \ -'--dont-filter[Don'\''t auto-filter wildcard responses]' \ +'*-u+[The target URL(s) (required, unless --stdin used)]:URL:_urls' \ +'*--url=[The target URL(s) (required, unless --stdin 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' \ +'-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.5.0)]:USER_AGENT: ' \ +'--user-agent=[Sets the User-Agent (default: feroxbuster/2.5.0)]:USER_AGENT: ' \ +'*-x+[File extension(s) to search for (ex: -x php -x pdf js)]:FILE_EXTENSION: ' \ +'*--extensions=[File extension(s) to search for (ex: -x php -x pdf js)]:FILE_EXTENSION: ' \ +'*-m+[Which HTTP request method(s) should be sent (default: GET)]:HTTP_METHODS: ' \ +'*--methods=[Which HTTP request method(s) should be sent (default: GET)]:HTTP_METHODS: ' \ +'--data=[Request'\''s Body; can read data from a file if input starts with an @ (ex: @post.bin)]:DATA: ' \ +'*-H+[Specify HTTP headers to be used in each request (ex: -H Header:val -H '\''stuff: things'\'')]:HEADER: ' \ +'*--headers=[Specify HTTP headers to be used in each request (ex: -H Header:val -H '\''stuff: things'\'')]:HEADER: ' \ +'*-b+[Specify HTTP cookies to be used in each request (ex: -b stuff=things)]:COOKIE: ' \ +'*--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: ' \ +'*--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: ' \ +'*-X+[Filter out messages via regular expression matching on the response'\''s body (ex: -X '\''^ignore me$'\'')]:REGEX: ' \ +'*--filter-regex=[Filter out messages via regular expression matching on the response'\''s body (ex: -X '\''^ignore me$'\'')]:REGEX: ' \ +'*-W+[Filter out messages of a particular word count (ex: -W 312 -W 91,82)]:WORDS: ' \ +'*--filter-words=[Filter out messages of a particular word count (ex: -W 312 -W 91,82)]:WORDS: ' \ +'*-N+[Filter out messages of a particular line count (ex: -N 20 -N 31,30)]:LINES: ' \ +'*--filter-lines=[Filter out messages of a particular line count (ex: -N 20 -N 31,30)]:LINES: ' \ +'*-C+[Filter out status codes (deny list) (ex: -C 200 -C 401)]:STATUS_CODE: ' \ +'*--filter-status=[Filter out status codes (deny list) (ex: -C 200 -C 401)]:STATUS_CODE: ' \ +'*--filter-similar-to=[Filter out pages that are similar to the given page (ex. --filter-similar-to http://site.xyz/soft404)]:UNWANTED_PAGE:_urls' \ +'*-s+[Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405)]:STATUS_CODE: ' \ +'*--status-codes=[Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405)]:STATUS_CODE: ' \ +'-T+[Number of seconds before a client'\''s request times out (default: 7)]:SECONDS: ' \ +'--timeout=[Number of seconds before a client'\''s request times out (default: 7)]:SECONDS: ' \ +'-t+[Number of concurrent threads (default: 50)]:THREADS: ' \ +'--threads=[Number of concurrent threads (default: 50)]:THREADS: ' \ +'-d+[Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)]:RECURSION_DEPTH: ' \ +'--depth=[Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)]:RECURSION_DEPTH: ' \ +'-L+[Limit total number of concurrent scans (default: 0, i.e. no limit)]:SCAN_LIMIT: ' \ +'--scan-limit=[Limit total number of concurrent scans (default: 0, i.e. no limit)]:SCAN_LIMIT: ' \ +'--parallel=[Run parallel feroxbuster instances (one child process per url passed via stdin)]:PARALLEL_SCANS: ' \ +'(--auto-tune)--rate-limit=[Limit number of requests per second (per directory) (default: 0, i.e. no limit)]:RATE_LIMIT: ' \ +'--time-limit=[Limit total run time of all scans (ex: --time-limit 10m)]:TIME_SPEC: ' \ +'-w+[Path to the wordlist]:FILE:_files' \ +'--wordlist=[Path to the wordlist]:FILE:_files' \ +'-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' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +'-V[Print version information]' \ +'--version[Print version information]' \ +'(-u --url)--stdin[Read url(s) from STDIN]' \ '-A[Use a random User-Agent]' \ '--random-agent[Use a random User-Agent]' \ -'-r[Follow redirects]' \ -'--redirects[Follow redirects]' \ -'-k[Disables TLS certificate validation]' \ -'--insecure[Disables TLS certificate validation]' \ +'-f[Append / to each request'\''s URL]' \ +'--add-slash[Append / to each request'\''s URL]' \ +'-r[Allow client to follow redirects]' \ +'--redirects[Allow client to follow redirects]' \ +'-k[Disables TLS certificate validation in the client]' \ +'--insecure[Disables TLS certificate validation in the client]' \ '-n[Do not scan recursively]' \ '--no-recursion[Do not scan recursively]' \ -'-f[Append / to each request]' \ -'--add-slash[Append / to each request]' \ -'(-u --url)--stdin[Read url(s) from STDIN]' \ '-e[Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)]' \ '--extract-links[Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)]' \ -'-h[Prints help information]' \ -'--help[Prints help information]' \ -'-V[Prints version information]' \ -'--version[Prints version information]' \ +'(--auto-bail)--auto-tune[Automatically lower scan rate when an excessive amount of errors are encountered]' \ +'--auto-bail[Automatically stop scanning when an excessive amount of errors are encountered]' \ +'-D[Don'\''t auto-filter wildcard responses]' \ +'--dont-filter[Don'\''t auto-filter wildcard responses]' \ +'(--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 + turn off logging (good for piping a list of urls to other commands)]' \ +'-q[Hide progress bars and banner (good for tmux windows w/ notifications)]' \ +'--quiet[Hide progress bars and banner (good for tmux windows w/ notifications)]' \ +'--json[Emit JSON logs to --output and --debug-log instead of normal text]' \ && ret=0 - } (( $+functions[_feroxbuster_commands] )) || _feroxbuster_commands() { - local commands; commands=( - - ) + local commands; commands=() _describe -t commands 'feroxbuster commands' commands "$@" } -_feroxbuster "$@" \ No newline at end of file +_feroxbuster "$@" diff --git a/shell_completions/_feroxbuster.ps1 b/shell_completions/_feroxbuster.ps1 index 1bb04662..344a693c 100644 --- a/shell_completions/_feroxbuster.ps1 +++ b/shell_completions/_feroxbuster.ps1 @@ -20,42 +20,29 @@ Register-ArgumentCompleter -Native -CommandName 'feroxbuster' -ScriptBlock { $completions = @(switch ($command) { 'feroxbuster' { - [CompletionResult]::new('-w', 'w', [CompletionResultType]::ParameterName, 'Path to the wordlist') - [CompletionResult]::new('--wordlist', 'wordlist', [CompletionResultType]::ParameterName, 'Path to the wordlist') [CompletionResult]::new('-u', 'u', [CompletionResultType]::ParameterName, 'The target URL(s) (required, unless --stdin used)') [CompletionResult]::new('--url', 'url', [CompletionResultType]::ParameterName, 'The target URL(s) (required, unless --stdin used)') - [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('-T', 'T', [CompletionResultType]::ParameterName, 'Number of seconds before a request times out (default: 7)') - [CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Number of seconds before a request times out (default: 7)') + [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('-s', 's', [CompletionResultType]::ParameterName, 'Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405)') - [CompletionResult]::new('--status-codes', 'status-codes', [CompletionResultType]::ParameterName, 'Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405)') - [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('--resume-from', 'resume-from', [CompletionResultType]::ParameterName, 'State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state)') - [CompletionResult]::new('--debug-log', 'debug-log', [CompletionResultType]::ParameterName, 'Output file to write log entries (use w/ --json for JSON entries)') - [CompletionResult]::new('-a', 'a', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/VERSION)') - [CompletionResult]::new('--user-agent', 'user-agent', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/VERSION)') + [CompletionResult]::new('-a', 'a', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/2.5.0)') + [CompletionResult]::new('--user-agent', 'user-agent', [CompletionResultType]::ParameterName, 'Sets the User-Agent (default: feroxbuster/2.5.0)') [CompletionResult]::new('-x', 'x', [CompletionResultType]::ParameterName, 'File extension(s) to search for (ex: -x php -x pdf js)') [CompletionResult]::new('--extensions', 'extensions', [CompletionResultType]::ParameterName, 'File extension(s) to search for (ex: -x php -x pdf js)') - [CompletionResult]::new('-m', 'm', [CompletionResultType]::ParameterName, 'HTTP request method(s) (default: GET)') - [CompletionResult]::new('--methods', 'methods', [CompletionResultType]::ParameterName, 'HTTP request method(s) (default: GET)') - [CompletionResult]::new('--data', 'data', [CompletionResultType]::ParameterName, 'HTTP Body data; can read data from a file if input starts with an @ (ex: @post.bin)') + [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('-H', 'H', [CompletionResultType]::ParameterName, 'Specify HTTP headers (ex: -H Header:val ''stuff: things'')') - [CompletionResult]::new('--headers', 'headers', [CompletionResultType]::ParameterName, 'Specify HTTP headers (ex: -H Header:val ''stuff: things'')') - [CompletionResult]::new('-b', 'b', [CompletionResultType]::ParameterName, 'Specify HTTP cookies (ex: -b stuff=things)') - [CompletionResult]::new('--cookies', 'cookies', [CompletionResultType]::ParameterName, 'Specify HTTP cookies (ex: -b stuff=things)') - [CompletionResult]::new('-Q', 'Q', [CompletionResultType]::ParameterName, 'Specify URL query parameters (ex: -Q token=stuff -Q secret=key)') - [CompletionResult]::new('--query', 'query', [CompletionResultType]::ParameterName, 'Specify URL query parameters (ex: -Q token=stuff -Q secret=key)') [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 (ex: -X ''^ignore me$'')') @@ -67,38 +54,51 @@ Register-ArgumentCompleter -Native -CommandName 'feroxbuster' -ScriptBlock { [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: 200 204 301 302 307 308 401 403 405)') + [CompletionResult]::new('--status-codes', 'status-codes', [CompletionResultType]::ParameterName, 'Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405)') + [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('-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('-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 + 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('--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('--json', 'json', [CompletionResultType]::ParameterName, 'Emit JSON logs to --output and --debug-log instead of normal text') - [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('-w', 'w', [CompletionResultType]::ParameterName, 'Path to the wordlist') + [CompletionResult]::new('--wordlist', 'wordlist', [CompletionResultType]::ParameterName, 'Path to the wordlist') + [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('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information') + [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information') + [CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information') + [CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information') + [CompletionResult]::new('--stdin', 'stdin', [CompletionResultType]::ParameterName, 'Read url(s) from STDIN') [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('-r', 'r', [CompletionResultType]::ParameterName, 'Follow redirects') - [CompletionResult]::new('--redirects', 'redirects', [CompletionResultType]::ParameterName, 'Follow redirects') - [CompletionResult]::new('-k', 'k', [CompletionResultType]::ParameterName, 'Disables TLS certificate validation') - [CompletionResult]::new('--insecure', 'insecure', [CompletionResultType]::ParameterName, 'Disables TLS certificate validation') + [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('-f', 'f', [CompletionResultType]::ParameterName, 'Append / to each request') - [CompletionResult]::new('--add-slash', 'add-slash', [CompletionResultType]::ParameterName, 'Append / to each request') - [CompletionResult]::new('--stdin', 'stdin', [CompletionResultType]::ParameterName, 'Read url(s) from STDIN') [CompletionResult]::new('-e', 'e', [CompletionResultType]::ParameterName, 'Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)') [CompletionResult]::new('--extract-links', 'extract-links', [CompletionResultType]::ParameterName, 'Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)') - [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information') - [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information') - [CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information') - [CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Prints version information') + [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('-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 + 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') break } }) diff --git a/shell_completions/feroxbuster.bash b/shell_completions/feroxbuster.bash index 34f5a4d0..fdb9a6dc 100644 --- a/shell_completions/feroxbuster.bash +++ b/shell_completions/feroxbuster.bash @@ -9,10 +9,9 @@ _feroxbuster() { for i in ${COMP_WORDS[@]} do case "${i}" in - feroxbuster) + "$1") cmd="feroxbuster" ;; - *) ;; esac @@ -20,218 +19,217 @@ _feroxbuster() { case "${cmd}" in feroxbuster) - opts=" -v -q -D -A -r -k -n -f -e -h -V -w -u -t -d -T -p -P -R -s -o -a -x -m -H -b -Q -S -X -W -N -C -L --verbosity --silent --quiet --auto-tune --auto-bail --json --dont-filter --random-agent --redirects --insecure --no-recursion --add-slash --stdin --extract-links --help --version --wordlist --url --threads --depth --timeout --proxy --replay-proxy --replay-codes --status-codes --output --resume-from --debug-log --user-agent --extensions --methods --data --dont-scan --headers --cookies --query --filter-size --filter-regex --filter-words --filter-lines --filter-status --filter-similar-to --scan-limit --parallel --rate-limit --time-limit " + opts="-h -V -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 -v -q -o --help --version --url --stdin --resume-from --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 --threads --no-recursion --depth --extract-links --scan-limit --parallel --rate-limit --time-limit --wordlist --auto-tune --auto-bail --dont-filter --verbosity --silent --quiet --json --output --debug-log" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 fi case "${prev}" in - - --wordlist) + --url) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -w) + -u) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --url) + --resume-from) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -u) + --proxy) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --threads) + -p) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -t) + --replay-proxy) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --depth) + -P) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -d) + --replay-codes) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --timeout) + -R) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -T) + --user-agent) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --proxy) + -a) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -p) + --extensions) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --replay-proxy) + -x) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -P) + --methods) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --replay-codes) + -m) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -R) + --data) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --status-codes) + --headers) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -s) + -H) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --output) + --cookies) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -o) + -b) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --resume-from) + --query) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --debug-log) + -Q) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --user-agent) + --dont-scan) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -a) + --filter-size) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --extensions) + -S) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -x) + --filter-regex) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --methods) + -X) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -m) + --filter-words) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --data) + -W) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --dont-scan) + --filter-lines) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --headers) + -N) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -H) + --filter-status) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --cookies) + -C) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -b) + --filter-similar-to) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --query) + --status-codes) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -Q) + -s) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --filter-size) + --timeout) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -S) + -T) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --filter-regex) + --threads) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -X) + -t) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --filter-words) + --depth) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -W) + -d) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --filter-lines) + --scan-limit) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -N) + -L) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --filter-status) + --parallel) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -C) + --rate-limit) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --filter-similar-to) + --time-limit) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --scan-limit) + --wordlist) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - -L) + -w) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --parallel) + --output) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --rate-limit) + -o) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; - --time-limit) + --debug-log) COMPREPLY=($(compgen -f "${cur}")) return 0 ;; @@ -242,7 +240,6 @@ _feroxbuster() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - esac } diff --git a/shell_completions/feroxbuster.elv b/shell_completions/feroxbuster.elv new file mode 100644 index 00000000..9e1a68b2 --- /dev/null +++ b/shell_completions/feroxbuster.elv @@ -0,0 +1,103 @@ + +use builtin; +use str; + +set edit:completion:arg-completer[feroxbuster] = {|@words| + fn spaces {|n| + builtin:repeat $n ' ' | str:join '' + } + fn cand {|text desc| + edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc + } + var command = 'feroxbuster' + for word $words[1..-1] { + if (str:has-prefix $word '-') { + break + } + set command = $command';'$word + } + var completions = [ + &'feroxbuster'= { + cand -u 'The target URL(s) (required, unless --stdin used)' + cand --url 'The target URL(s) (required, unless --stdin used)' + cand --resume-from 'State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state)' + 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.5.0)' + cand --user-agent 'Sets the User-Agent (default: feroxbuster/2.5.0)' + cand -x 'File extension(s) to search for (ex: -x php -x pdf js)' + cand --extensions 'File extension(s) to search for (ex: -x php -x pdf js)' + cand -m 'Which HTTP request method(s) should be sent (default: GET)' + cand --methods 'Which HTTP request method(s) should be sent (default: GET)' + cand --data 'Request''s Body; can read data from a file if input starts with an @ (ex: @post.bin)' + cand -H 'Specify HTTP headers to be used in each request (ex: -H Header:val -H ''stuff: things'')' + cand --headers 'Specify HTTP headers to be used in each request (ex: -H Header:val -H ''stuff: things'')' + cand -b 'Specify HTTP cookies to be used in each request (ex: -b stuff=things)' + 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 --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)' + cand -X 'Filter out messages via regular expression matching on the response''s body (ex: -X ''^ignore me$'')' + cand --filter-regex 'Filter out messages via regular expression matching on the response''s body (ex: -X ''^ignore me$'')' + cand -W 'Filter out messages of a particular word count (ex: -W 312 -W 91,82)' + cand --filter-words 'Filter out messages of a particular word count (ex: -W 312 -W 91,82)' + cand -N 'Filter out messages of a particular line count (ex: -N 20 -N 31,30)' + cand --filter-lines 'Filter out messages of a particular line count (ex: -N 20 -N 31,30)' + cand -C 'Filter out status codes (deny list) (ex: -C 200 -C 401)' + cand --filter-status 'Filter out status codes (deny list) (ex: -C 200 -C 401)' + cand --filter-similar-to 'Filter out pages that are similar to the given page (ex. --filter-similar-to http://site.xyz/soft404)' + cand -s 'Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405)' + cand --status-codes 'Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405)' + cand -T 'Number of seconds before a client''s request times out (default: 7)' + cand --timeout 'Number of seconds before a client''s request times out (default: 7)' + cand -t 'Number of concurrent threads (default: 50)' + cand --threads 'Number of concurrent threads (default: 50)' + cand -d 'Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)' + cand --depth 'Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)' + cand -L 'Limit total number of concurrent scans (default: 0, i.e. no limit)' + cand --scan-limit 'Limit total number of concurrent scans (default: 0, i.e. no limit)' + cand --parallel 'Run parallel feroxbuster instances (one child process per url passed via stdin)' + cand --rate-limit 'Limit number of requests per second (per directory) (default: 0, i.e. no limit)' + cand --time-limit 'Limit total run time of all scans (ex: --time-limit 10m)' + cand -w 'Path to the wordlist' + cand --wordlist 'Path to the wordlist' + 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 -h 'Print help information' + cand --help 'Print help information' + cand -V 'Print version information' + cand --version 'Print version information' + cand --stdin 'Read url(s) from STDIN' + cand -A 'Use a random User-Agent' + cand --random-agent 'Use a random User-Agent' + cand -f 'Append / to each request''s URL' + cand --add-slash 'Append / to each request''s URL' + cand -r 'Allow client to follow redirects' + cand --redirects 'Allow client to follow redirects' + cand -k 'Disables TLS certificate validation in the client' + cand --insecure 'Disables TLS certificate validation in the client' + cand -n 'Do not scan recursively' + cand --no-recursion 'Do not scan recursively' + cand -e 'Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)' + cand --extract-links 'Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)' + cand --auto-tune 'Automatically lower scan rate when an excessive amount of errors are encountered' + cand --auto-bail 'Automatically stop scanning when an excessive amount of errors are encountered' + cand -D 'Don''t auto-filter wildcard responses' + cand --dont-filter 'Don''t auto-filter wildcard responses' + 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 + turn off logging (good for piping a list of urls to other commands)' + cand -q 'Hide progress bars and banner (good for tmux windows w/ notifications)' + cand --quiet 'Hide progress bars and banner (good for tmux windows w/ notifications)' + cand --json 'Emit JSON logs to --output and --debug-log instead of normal text' + } + ] + $completions[$command] +} diff --git a/src/banner/container.rs b/src/banner/container.rs index 60159f89..04e54e7a 100644 --- a/src/banner/container.rs +++ b/src/banner/container.rs @@ -315,14 +315,15 @@ impl Banner { ); let offset = std::cmp::min(config.data.len(), 30); - let data = String::from_utf8(config.data[..offset].to_vec()).unwrap_or_else(|_err| { - format!( - "{:x?} ...", - &config.data[..std::cmp::min(config.data.len(), 13)] - ) - }) - .replace("\n", " ") - .replace("\r", ""); + let data = String::from_utf8(config.data[..offset].to_vec()) + .unwrap_or_else(|_err| { + format!( + "{:x?} ...", + &config.data[..std::cmp::min(config.data.len(), 13)] + ) + }) + .replace("\n", " ") + .replace("\r", ""); let data = BannerEntry::new("💣", "HTTP Body", &data); let insecure = BannerEntry::new("🔓", "Insecure", &config.insecure.to_string()); let redirects = BannerEntry::new("📍", "Follow Redirects", &config.redirects.to_string()); diff --git a/src/config/container.rs b/src/config/container.rs index 723c7e09..4c250579 100644 --- a/src/config/container.rs +++ b/src/config/container.rs @@ -9,7 +9,7 @@ use crate::{ DEFAULT_CONFIG_NAME, }; use anyhow::{anyhow, Context, Result}; -use clap::{value_t, ArgMatches}; +use clap::ArgMatches; use regex::Regex; use reqwest::{Client, Method, StatusCode, Url}; use serde::{Deserialize, Serialize}; @@ -22,17 +22,15 @@ use std::{ /// macro helper to abstract away repetitive configuration updates macro_rules! update_config_if_present { - ($c:expr, $m:ident, $v:expr, $t:ty) => { - match value_t!($m, $v, $t) { - Ok(value) => *$c = value, // Update value - Err(clap::Error { - kind: clap::ErrorKind::ArgumentNotFound, - message: _, - info: _, - }) => { - // Do nothing if argument not found + ($conf_val:expr, $matches:ident, $arg_name:expr) => { + match $matches.value_of_t($arg_name) { + Ok(value) => *$conf_val = value, // Update value + Err(err) => { + if !matches!(err.kind, clap::ErrorKind::ArgumentNotFound) { + // Do nothing if argument not found + err.exit() // Exit with error on any other parse error + } } - Err(e) => e.exit(), // Exit with error on parse error } }; } @@ -519,16 +517,16 @@ impl Configuration { fn parse_cli_args(args: &ArgMatches) -> Self { let mut config = Configuration::default(); - update_config_if_present!(&mut config.threads, args, "threads", usize); - update_config_if_present!(&mut config.depth, args, "depth", usize); - update_config_if_present!(&mut config.scan_limit, args, "scan_limit", usize); - update_config_if_present!(&mut config.parallel, args, "parallel", usize); - update_config_if_present!(&mut config.rate_limit, args, "rate_limit", 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.time_limit, args, "time_limit", String); - update_config_if_present!(&mut config.resume_from, args, "resume_from", String); + update_config_if_present!(&mut config.threads, args, "threads"); + update_config_if_present!(&mut config.depth, args, "depth"); + update_config_if_present!(&mut config.scan_limit, args, "scan_limit"); + update_config_if_present!(&mut config.parallel, args, "parallel"); + update_config_if_present!(&mut config.rate_limit, args, "rate_limit"); + update_config_if_present!(&mut config.wordlist, args, "wordlist"); + update_config_if_present!(&mut config.output, args, "output"); + update_config_if_present!(&mut config.debug_log, args, "debug_log"); + update_config_if_present!(&mut config.time_limit, args, "time_limit"); + update_config_if_present!(&mut config.resume_from, args, "resume_from"); if let Some(arg) = args.values_of("status_codes") { config.status_codes = arg @@ -602,7 +600,7 @@ impl Configuration { // url to be scanned. With the addition of regex support, I want to move parsing // out of should_deny_url and into here, so it's performed once instead of thousands // of times - for denier in arg.into_iter() { + for denier in arg { // could be an absolute url or a regex, need to determine which and populate the // appropriate vector match Url::parse(denier.trim_end_matches('/')) { @@ -727,10 +725,10 @@ impl Configuration { //// // organizational breakpoint; all options below alter the Client configuration //// - update_config_if_present!(&mut config.proxy, args, "proxy", String); - update_config_if_present!(&mut config.replay_proxy, args, "replay_proxy", String); - update_config_if_present!(&mut config.user_agent, args, "user_agent", String); - update_config_if_present!(&mut config.timeout, args, "timeout", u64); + update_config_if_present!(&mut config.proxy, args, "proxy"); + update_config_if_present!(&mut config.replay_proxy, args, "replay_proxy"); + update_config_if_present!(&mut config.user_agent, args, "user_agent"); + update_config_if_present!(&mut config.timeout, args, "timeout"); if args.is_present("random_agent") { config.random_agent = true; diff --git a/src/main.rs b/src/main.rs index d2feb299..cf13f9ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -244,6 +244,7 @@ async fn wrapped_main(config: Arc) -> Result<()> { } // get targets from command line or stdin + // todo handle multiple -u let targets = match get_targets(handles.clone()).await { Ok(t) => t, Err(e) => { diff --git a/src/parser.rs b/src/parser.rs index 8290fa79..fb691c6a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,4 +1,6 @@ -use clap::{App, Arg, ArgGroup}; +use clap::{ + crate_authors, crate_description, crate_name, crate_version, App, Arg, ArgGroup, ValueHint, +}; use lazy_static::lazy_static; use regex::Regex; use std::env; @@ -14,423 +16,562 @@ lazy_static! { /// - 1d pub static ref TIMESPEC_REGEX: Regex = Regex::new(r"^(?i)(?P\d+)(?P[smdh])$").expect("Could not compile regex"); + + /// help string for user agent, your guess is as good as mine as to why this is required... + static ref DEFAULT_USER_AGENT: String = format!( + "Sets the User-Agent (default: feroxbuster/{})", + crate_version!() + ); } /// Create and return an instance of [clap::App](https://docs.rs/clap/latest/clap/struct.App.html), i.e. the Command Line Interface's configuration -pub fn initialize() -> App<'static, 'static> { - let mut app = App::new("feroxbuster") - .version(env!("CARGO_PKG_VERSION")) - .author("Ben 'epi' Risher (@epi052)") - .about("A fast, simple, recursive content discovery tool written in Rust") - .arg( - Arg::with_name("wordlist") - .short("w") - .long("wordlist") - .value_name("FILE") - .help("Path to the wordlist") - .takes_value(true), - ) +pub fn initialize() -> App<'static> { + let app = App::new(crate_name!()) + .version(crate_version!()) + .author(crate_authors!()) + .about(crate_description!()); + + ///////////////////////////////////////////////////////////////////// + // group - target selection + ///////////////////////////////////////////////////////////////////// + let app = app .arg( - Arg::with_name("url") - .short("u") + Arg::new("url") + .short('u') .long("url") - .required_unless_one(&["stdin", "resume_from"]) + .required_unless_present_any(&["stdin", "resume_from"]) + .help_heading("Target selection") .value_name("URL") - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) .use_delimiter(true) + .value_hint(ValueHint::Url) .help("The target URL(s) (required, unless --stdin used)"), ) .arg( - Arg::with_name("threads") - .short("t") - .long("threads") - .value_name("THREADS") - .takes_value(true) - .help("Number of concurrent threads (default: 50)"), - ) - .arg( - Arg::with_name("depth") - .short("d") - .long("depth") - .value_name("RECURSION_DEPTH") - .takes_value(true) - .help("Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)"), - ) - .arg( - Arg::with_name("timeout") - .short("T") - .long("timeout") - .value_name("SECONDS") - .takes_value(true) - .help("Number of seconds before a request times out (default: 7)"), - ) - .arg( - Arg::with_name("verbosity") - .short("v") - .long("verbosity") + Arg::new("stdin") + .long("stdin") + .help_heading("Target selection") .takes_value(false) - .multiple(true) - .conflicts_with("silent") - .help("Increase verbosity level (use -vv or more for greater effect. [CAUTION] 4 -v's is probably too much)"), + .help("Read url(s) from STDIN") + .conflicts_with("url") ) .arg( - Arg::with_name("proxy") - .short("p") + Arg::new("resume_from") + .long("resume-from") + .value_hint(ValueHint::FilePath) + .value_name("STATE_FILE") + .help_heading("Target selection") + .help("State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state)") + .conflicts_with("url") + .takes_value(true), + ); + + ///////////////////////////////////////////////////////////////////// + // group - proxy settings + ///////////////////////////////////////////////////////////////////// + let app = app + .arg( + Arg::new("proxy") + .short('p') .long("proxy") .takes_value(true) .value_name("PROXY") + .value_hint(ValueHint::Url) + .help_heading("Proxy settings") .help( "Proxy to use for requests (ex: http(s)://host:port, socks5(h)://host:port)", ), ) .arg( - Arg::with_name("replay_proxy") - .short("P") + Arg::new("replay_proxy") + .short('P') .long("replay-proxy") .takes_value(true) + .value_hint(ValueHint::Url) .value_name("REPLAY_PROXY") + .help_heading("Proxy settings") .help( "Send only unfiltered requests through a Replay Proxy, instead of all requests", ), ) .arg( - Arg::with_name("replay_codes") - .short("R") + Arg::new("replay_codes") + .short('R') .long("replay-codes") .value_name("REPLAY_CODE") .takes_value(true) - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) .use_delimiter(true) .requires("replay_proxy") + .help_heading("Proxy settings") .help( "Status Codes to send through a Replay Proxy when found (default: --status-codes value)", ), - ) - .arg( - Arg::with_name("status_codes") - .short("s") - .long("status-codes") - .value_name("STATUS_CODE") - .takes_value(true) - .multiple(true) - .use_delimiter(true) - .help( - "Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405)", - ), - ) - .arg( - Arg::with_name("silent") - .long("silent") - .takes_value(false) - .conflicts_with("quiet") - .help("Only print URLs + turn off logging (good for piping a list of urls to other commands)") - ) - .arg( - Arg::with_name("quiet") - .short("q") - .long("quiet") - .takes_value(false) - .help("Hide progress bars and banner (good for tmux windows w/ notifications)") - ) - .arg( - Arg::with_name("auto_tune") - .long("auto-tune") - .takes_value(false) - .conflicts_with("auto_bail") - .help("Automatically lower scan rate when an excessive amount of errors are encountered") - ) - .arg( - Arg::with_name("auto_bail") - .long("auto-bail") - .takes_value(false) - .help("Automatically stop scanning when an excessive amount of errors are encountered") - ) - .arg( - Arg::with_name("json") - .long("json") - .takes_value(false) - .requires("output_files") - .help("Emit JSON logs to --output and --debug-log instead of normal text") - ) - .arg( - Arg::with_name("dont_filter") - .short("D") - .long("dont-filter") - .takes_value(false) - .help("Don't auto-filter wildcard responses") - ) - .arg( - Arg::with_name("output") - .short("o") - .long("output") - .value_name("FILE") - .help("Output file to write results to (use w/ --json for JSON entries)") - .takes_value(true), - ) - .arg( - Arg::with_name("resume_from") - .long("resume-from") - .value_name("STATE_FILE") - .help("State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state)") - .conflicts_with("url") - .takes_value(true), - ) - .arg( - Arg::with_name("debug_log") - .long("debug-log") - .value_name("FILE") - .help("Output file to write log entries (use w/ --json for JSON entries)") - .takes_value(true), - ) + ); + + ///////////////////////////////////////////////////////////////////// + // group - request settings + ///////////////////////////////////////////////////////////////////// + + let app = app .arg( - Arg::with_name("user_agent") - .short("a") + Arg::new("user_agent") + .short('a') .long("user-agent") .value_name("USER_AGENT") .takes_value(true) - .help( - "Sets the User-Agent (default: feroxbuster/VERSION)" - ), + .help_heading("Request settings") + .help(&**DEFAULT_USER_AGENT), ) .arg( - Arg::with_name("random_agent") - .short("A") + Arg::new("random_agent") + .short('A') .long("random-agent") .takes_value(false) - .help( - "Use a random User-Agent" - ), + .help_heading("Request settings") + .help("Use a random User-Agent"), ) .arg( - Arg::with_name("redirects") - .short("r") - .long("redirects") - .takes_value(false) - .help("Follow redirects") - ) - .arg( - Arg::with_name("insecure") - .short("k") - .long("insecure") - .takes_value(false) - .help("Disables TLS certificate validation") - ) - .arg( - Arg::with_name("extensions") - .short("x") + Arg::new("extensions") + .short('x') .long("extensions") .value_name("FILE_EXTENSION") .takes_value(true) - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) .use_delimiter(true) + .help_heading("Request settings") .help( "File extension(s) to search for (ex: -x php -x pdf js)", ), ) .arg( - Arg::with_name("methods") - .short("m") + Arg::new("methods") + .short('m') .long("methods") .value_name("HTTP_METHODS") .takes_value(true) - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) .use_delimiter(true) + .help_heading("Request settings") .help( - "HTTP request method(s) (default: GET)", + "Which HTTP request method(s) should be sent (default: GET)", ), ) .arg( - Arg::with_name("data") + Arg::new("data") .long("data") .value_name("DATA") .takes_value(true) + .help_heading("Request settings") .help( - "HTTP Body data; can read data from a file if input starts with an @ (ex: @post.bin)", - ), - ) - .arg( - Arg::with_name("url_denylist") - .long("dont-scan") - .value_name("URL") - .takes_value(true) - .multiple(true) - .use_delimiter(true) - .help( - "URL(s) or Regex Pattern(s) to exclude from recursion/scans", + "Request's Body; can read data from a file if input starts with an @ (ex: @post.bin)", ), ) + .arg( - Arg::with_name("headers") - .short("H") + Arg::new("headers") + .short('H') .long("headers") .value_name("HEADER") .takes_value(true) - .multiple(true) + .help_heading("Request settings") + .multiple_values(true) + .multiple_occurrences(true) .use_delimiter(true) .help( - "Specify HTTP headers (ex: -H Header:val 'stuff: things')", + "Specify HTTP headers to be used in each request (ex: -H Header:val -H 'stuff: things')", ), ) .arg( - Arg::with_name("cookies") - .short("b") + Arg::new("cookies") + .short('b') .long("cookies") .value_name("COOKIE") .takes_value(true) - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) .use_delimiter(true) + .help_heading("Request settings") .help( - "Specify HTTP cookies (ex: -b stuff=things)", + "Specify HTTP cookies to be used in each request (ex: -b stuff=things)", ), ) .arg( - Arg::with_name("queries") - .short("Q") + Arg::new("queries") + .short('Q') .long("query") .value_name("QUERY") .takes_value(true) - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) .use_delimiter(true) + .help_heading("Request settings") .help( - "Specify URL query parameters (ex: -Q token=stuff -Q secret=key)", + "Request's URL query parameters (ex: -Q token=stuff -Q secret=key)", ), ) .arg( - Arg::with_name("no_recursion") - .short("n") - .long("no-recursion") - .takes_value(false) - .help("Do not scan recursively") - ) - .arg( - Arg::with_name("add_slash") - .short("f") + Arg::new("add_slash") + .short('f') .long("add-slash") + .help_heading("Request settings") .takes_value(false) - .help("Append / to each request") - ) - .arg( - Arg::with_name("stdin") - .long("stdin") - .takes_value(false) - .help("Read url(s) from STDIN") - .conflicts_with("url") - ) - .arg( - Arg::with_name("filter_size") - .short("S") + .help("Append / to each request's URL") + ); + ///////////////////////////////////////////////////////////////////// + // group - request filters + ///////////////////////////////////////////////////////////////////// + let app = app.arg( + Arg::new("url_denylist") + .long("dont-scan") + .value_name("URL") + .takes_value(true) + .multiple_values(true) + .multiple_occurrences(true) + .use_delimiter(true) + .help_heading("Request filters") + .help("URL(s) or Regex Pattern(s) to exclude from recursion/scans"), + ); + ///////////////////////////////////////////////////////////////////// + // group - response filters + ///////////////////////////////////////////////////////////////////// + let app = app + .arg( + Arg::new("filter_size") + .short('S') .long("filter-size") .value_name("SIZE") .takes_value(true) - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) .use_delimiter(true) + .help_heading("Response filters") .help( "Filter out messages of a particular size (ex: -S 5120 -S 4927,1970)", ), ) .arg( - Arg::with_name("filter_regex") - .short("X") + Arg::new("filter_regex") + .short('X') .long("filter-regex") .value_name("REGEX") .takes_value(true) - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) .use_delimiter(true) + .help_heading("Response filters") .help( "Filter out messages via regular expression matching on the response's body (ex: -X '^ignore me$')", ), ) .arg( - Arg::with_name("filter_words") - .short("W") + Arg::new("filter_words") + .short('W') .long("filter-words") .value_name("WORDS") .takes_value(true) - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) .use_delimiter(true) + .help_heading("Response filters") .help( "Filter out messages of a particular word count (ex: -W 312 -W 91,82)", ), ) .arg( - Arg::with_name("filter_lines") - .short("N") + Arg::new("filter_lines") + .short('N') .long("filter-lines") .value_name("LINES") .takes_value(true) - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) .use_delimiter(true) + .help_heading("Response filters") .help( "Filter out messages of a particular line count (ex: -N 20 -N 31,30)", ), ) .arg( - Arg::with_name("filter_status") - .short("C") + Arg::new("filter_status") + .short('C') .long("filter-status") .value_name("STATUS_CODE") .takes_value(true) - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) .use_delimiter(true) + .help_heading("Response filters") .help( "Filter out status codes (deny list) (ex: -C 200 -C 401)", ), ) .arg( - Arg::with_name("filter_similar") + Arg::new("filter_similar") .long("filter-similar-to") .value_name("UNWANTED_PAGE") .takes_value(true) - .multiple(true) + .multiple_values(true) + .multiple_occurrences(true) + .value_hint(ValueHint::Url) .use_delimiter(true) + .help_heading("Response filters") .help( "Filter out pages that are similar to the given page (ex. --filter-similar-to http://site.xyz/soft404)", ), ) .arg( - Arg::with_name("extract_links") - .short("e") + Arg::new("status_codes") + .short('s') + .long("status-codes") + .value_name("STATUS_CODE") + .takes_value(true) + .multiple_values(true) + .multiple_occurrences(true) + .use_delimiter(true) + .help_heading("Response filters") + .help( + "Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405)", + ), + ); + ///////////////////////////////////////////////////////////////////// + // group - client settings + ///////////////////////////////////////////////////////////////////// + let app = app + .arg( + Arg::new("timeout") + .short('T') + .long("timeout") + .value_name("SECONDS") + .takes_value(true) + .help_heading("Client settings") + .help("Number of seconds before a client's request times out (default: 7)"), + ) + .arg( + Arg::new("redirects") + .short('r') + .long("redirects") + .takes_value(false) + .help_heading("Client settings") + .help("Allow client to follow redirects"), + ) + .arg( + Arg::new("insecure") + .short('k') + .long("insecure") + .takes_value(false) + .help_heading("Client settings") + .help("Disables TLS certificate validation in the client"), + ); + ///////////////////////////////////////////////////////////////////// + // group - scan settings + ///////////////////////////////////////////////////////////////////// + let app = app + .arg( + Arg::new("threads") + .short('t') + .long("threads") + .value_name("THREADS") + .takes_value(true) + .help_heading("Scan settings") + .help("Number of concurrent threads (default: 50)"), + ) + .arg( + Arg::new("no_recursion") + .short('n') + .long("no-recursion") + .takes_value(false) + .help_heading("Scan settings") + .help("Do not scan recursively"), + ) + .arg( + Arg::new("depth") + .short('d') + .long("depth") + .value_name("RECURSION_DEPTH") + .takes_value(true) + .help_heading("Scan settings") + .help("Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)"), + ).arg( + Arg::new("extract_links") + .short('e') .long("extract-links") .takes_value(false) + .help_heading("Scan settings") .help("Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)") ) .arg( - Arg::with_name("scan_limit") - .short("L") + Arg::new("scan_limit") + .short('L') .long("scan-limit") .value_name("SCAN_LIMIT") .takes_value(true) + .help_heading("Scan settings") .help("Limit total number of concurrent scans (default: 0, i.e. no limit)") ) .arg( - Arg::with_name("parallel") + Arg::new("parallel") .long("parallel") .value_name("PARALLEL_SCANS") .takes_value(true) .requires("stdin") + .help_heading("Scan settings") .help("Run parallel feroxbuster instances (one child process per url passed via stdin)") ) .arg( - Arg::with_name("rate_limit") + Arg::new("rate_limit") .long("rate-limit") .value_name("RATE_LIMIT") .takes_value(true) .conflicts_with("auto_tune") + .help_heading("Scan settings") .help("Limit number of requests per second (per directory) (default: 0, i.e. no limit)") ) .arg( - Arg::with_name("time_limit") + Arg::new("time_limit") .long("time-limit") .value_name("TIME_SPEC") .takes_value(true) .validator(valid_time_spec) + .help_heading("Scan settings") .help("Limit total run time of all scans (ex: --time-limit 10m)") ) - .group(ArgGroup::with_name("output_files") - .args(&["debug_log", "output"]) - .multiple(true) + .arg( + Arg::new("wordlist") + .short('w') + .long("wordlist") + .value_hint(ValueHint::FilePath) + .value_name("FILE") + .help("Path to the wordlist") + .help_heading("Scan settings") + .takes_value(true), + ).arg( + Arg::new("auto_tune") + .long("auto-tune") + .takes_value(false) + .conflicts_with("auto_bail") + .help_heading("Scan settings") + .help("Automatically lower scan rate when an excessive amount of errors are encountered") ) - .after_help(r#"NOTE: + .arg( + Arg::new("auto_bail") + .long("auto-bail") + .takes_value(false) + .help_heading("Scan settings") + .help("Automatically stop scanning when an excessive amount of errors are encountered") + ).arg( + Arg::new("dont_filter") + .short('D') + .long("dont-filter") + .takes_value(false) + .help_heading("Scan settings") + .help("Don't auto-filter wildcard responses") + ); + ///////////////////////////////////////////////////////////////////// + // group - output settings + ///////////////////////////////////////////////////////////////////// + let app = app + .arg( + Arg::new("verbosity") + .short('v') + .long("verbosity") + .takes_value(false) + .multiple_occurrences(true) + .conflicts_with("silent") + .help_heading("Output settings") + .help("Increase verbosity level (use -vv or more for greater effect. [CAUTION] 4 -v's is probably too much)"), + ).arg( + Arg::new("silent") + .long("silent") + .takes_value(false) + .conflicts_with("quiet") + .help_heading("Output settings") + .help("Only print URLs + turn off logging (good for piping a list of urls to other commands)") + ) + .arg( + Arg::new("quiet") + .short('q') + .long("quiet") + .takes_value(false) + .help_heading("Output settings") + .help("Hide progress bars and banner (good for tmux windows w/ notifications)") + ) + + .arg( + Arg::new("json") + .long("json") + .takes_value(false) + .requires("output_files") + .help_heading("Output settings") + .help("Emit JSON logs to --output and --debug-log instead of normal text") + ).arg( + Arg::new("output") + .short('o') + .long("output") + .value_hint(ValueHint::FilePath) + .value_name("FILE") + .help_heading("Output settings") + .help("Output file to write results to (use w/ --json for JSON entries)") + .takes_value(true), + ) + .arg( + Arg::new("debug_log") + .long("debug-log") + .value_name("FILE") + .value_hint(ValueHint::FilePath) + .help_heading("Output settings") + .help("Output file to write log entries (use w/ --json for JSON entries)") + .takes_value(true), + ); + ///////////////////////////////////////////////////////////////////// + // group - miscellaneous + ///////////////////////////////////////////////////////////////////// + let mut app = app + .group( + ArgGroup::new("output_files") + .args(&["debug_log", "output"]) + .multiple(true), + ) + .after_long_help(EPILOGUE); + + for arg in env::args() { + // secure-77 noticed that when an incorrect flag/option is used, the short help message is printed + // which is fine, but if you add -h|--help, it still errors out on the bad flag/option, + // never showing the full help message. This code addresses that behavior + if arg == "--help" { + app.print_long_help().unwrap(); + println!(); // just a newline to mirror original --help output + process::exit(0); + } else if arg == "-h" { + // same for -h, just shorter + app.print_help().unwrap(); + println!(); + process::exit(0); + } + } + + app +} + +/// Validate that a string is formatted as a number followed by s, m, h, or d (10d, 30s, etc...) +fn valid_time_spec(time_spec: &str) -> Result<(), String> { + match TIMESPEC_REGEX.is_match(time_spec) { + true => Ok(()), + false => { + let msg = format!( + "Expected a non-negative, whole number followed by s, m, h, or d (case insensitive); received {}", + time_spec + ); + Err(msg) + } + } +} + +const EPILOGUE: &str = r#"NOTE: Options that take multiple values are very flexible. Consider the following ways of specifying extensions: ./feroxbuster -u http://127.1 -x pdf -x js,html -x php txt json,docx @@ -463,36 +604,21 @@ EXAMPLES: ./feroxbuster -u http://127.1 --extract-links Ludicrous speed... go! - ./feroxbuster -u http://127.1 -t 200 - "#); - - for arg in env::args() { - // secure-77 noticed that when an incorrect flag/option is used, the short help message is printed - // which is fine, but if you add -h|--help, it still errors out on the bad flag/option, - // never showing the full help message. This code addresses that behavior - if arg == "--help" || arg == "-h" { - app.print_long_help().unwrap(); - println!(); // just a newline to mirror original --help output - process::exit(0); - } - } - - app -} - -/// Validate that a string is formatted as a number followed by s, m, h, or d (10d, 30s, etc...) -fn valid_time_spec(time_spec: String) -> Result<(), String> { - match TIMESPEC_REGEX.is_match(&time_spec) { - true => Ok(()), - false => { - let msg = format!( - "Expected a non-negative, whole number followed by s, m, h, or d (case insensitive); received {}", - time_spec - ); - Err(msg) - } - } -} + ./feroxbuster -u http://127.1 -threads 200 + + Limit to a total of 60 active requests at any given time (threads * scan limit) + ./feroxbuster -u http://127.1 --threads 30 --scan-limit 2 + + Send all 200/302 responses to a proxy (only proxy requests/responses you care about) + ./feroxbuster -u http://127.1 --replay-proxy http://localhost:8080 --replay-codes 200 302 --insecure + + Abort or reduce scan speed to individual directory scans when too many errors have occurred + ./feroxbuster -u http://127.1 --auto-bail + ./feroxbuster -u http://127.1 --auto-tune + + Examples and demonstrations of all features + https://epi052.github.io/feroxbuster-docs/docs/examples/ + "#; #[cfg(test)] mod tests { @@ -512,29 +638,29 @@ mod tests { /// that i didn't hose up the regex. Going to consolidate them into a single test fn validate_valid_time_spec_validation() { let float_rejected = "1.4m"; - assert!(valid_time_spec(float_rejected.into()).is_err()); + assert!(valid_time_spec(float_rejected).is_err()); let negative_rejected = "-1m"; - assert!(valid_time_spec(negative_rejected.into()).is_err()); + assert!(valid_time_spec(negative_rejected).is_err()); let only_number_rejected = "1"; - assert!(valid_time_spec(only_number_rejected.into()).is_err()); + assert!(valid_time_spec(only_number_rejected).is_err()); let only_measurement_rejected = "m"; - assert!(valid_time_spec(only_measurement_rejected.into()).is_err()); + assert!(valid_time_spec(only_measurement_rejected).is_err()); for accepted_measurement in &["s", "m", "h", "d", "S", "M", "H", "D"] { // all upper/lowercase should be good - assert!(valid_time_spec(format!("1{}", *accepted_measurement)).is_ok()); + assert!(valid_time_spec(&format!("1{}", *accepted_measurement)).is_ok()); } let leading_space_rejected = " 14m"; - assert!(valid_time_spec(leading_space_rejected.into()).is_err()); + assert!(valid_time_spec(leading_space_rejected).is_err()); let trailing_space_rejected = "14m "; - assert!(valid_time_spec(trailing_space_rejected.into()).is_err()); + assert!(valid_time_spec(trailing_space_rejected).is_err()); let space_between_rejected = "1 4m"; - assert!(valid_time_spec(space_between_rejected.into()).is_err()); + assert!(valid_time_spec(space_between_rejected).is_err()); } } diff --git a/tests/test_scanner.rs b/tests/test_scanner.rs index f86e9c08..b6fab74e 100644 --- a/tests/test_scanner.rs +++ b/tests/test_scanner.rs @@ -363,7 +363,7 @@ fn scanner_single_request_replayed_to_proxy() -> Result<(), Box Date: Sat, 15 Jan 2022 18:46:41 -0600 Subject: [PATCH 2/3] fixed multiple -u issue --- shell_completions/_feroxbuster | 4 ++-- shell_completions/_feroxbuster.ps1 | 4 ++-- shell_completions/feroxbuster.elv | 4 ++-- src/main.rs | 1 - src/parser.rs | 4 +--- tests/test_parser.rs | 8 +++++--- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/shell_completions/_feroxbuster b/shell_completions/_feroxbuster index cbc2fb65..dfb3b384 100644 --- a/shell_completions/_feroxbuster +++ b/shell_completions/_feroxbuster @@ -15,8 +15,8 @@ _feroxbuster() { local context curcontext="$curcontext" state line _arguments "${_arguments_options[@]}" \ -'*-u+[The target URL(s) (required, unless --stdin used)]:URL:_urls' \ -'*--url=[The target URL(s) (required, unless --stdin used)]:URL:_urls' \ +'-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 --url)--resume-from=[State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state)]:STATE_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' \ diff --git a/shell_completions/_feroxbuster.ps1 b/shell_completions/_feroxbuster.ps1 index 344a693c..aba6c9be 100644 --- a/shell_completions/_feroxbuster.ps1 +++ b/shell_completions/_feroxbuster.ps1 @@ -20,8 +20,8 @@ Register-ArgumentCompleter -Native -CommandName 'feroxbuster' -ScriptBlock { $completions = @(switch ($command) { 'feroxbuster' { - [CompletionResult]::new('-u', 'u', [CompletionResultType]::ParameterName, 'The target URL(s) (required, unless --stdin used)') - [CompletionResult]::new('--url', 'url', [CompletionResultType]::ParameterName, 'The target URL(s) (required, unless --stdin used)') + [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)') diff --git a/shell_completions/feroxbuster.elv b/shell_completions/feroxbuster.elv index 9e1a68b2..3accfb18 100644 --- a/shell_completions/feroxbuster.elv +++ b/shell_completions/feroxbuster.elv @@ -18,8 +18,8 @@ set edit:completion:arg-completer[feroxbuster] = {|@words| } var completions = [ &'feroxbuster'= { - cand -u 'The target URL(s) (required, unless --stdin used)' - cand --url 'The target URL(s) (required, unless --stdin used)' + cand -u 'The target URL (required, unless [--stdin || --resume-from] used)' + cand --url 'The target URL (required, unless [--stdin || --resume-from] used)' cand --resume-from 'State file from which to resume a partially complete scan (ex. --resume-from ferox-1606586780.state)' 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)' diff --git a/src/main.rs b/src/main.rs index cf13f9ba..d2feb299 100644 --- a/src/main.rs +++ b/src/main.rs @@ -244,7 +244,6 @@ async fn wrapped_main(config: Arc) -> Result<()> { } // get targets from command line or stdin - // todo handle multiple -u let targets = match get_targets(handles.clone()).await { Ok(t) => t, Err(e) => { diff --git a/src/parser.rs b/src/parser.rs index fb691c6a..e5d11c1e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -42,11 +42,9 @@ pub fn initialize() -> App<'static> { .required_unless_present_any(&["stdin", "resume_from"]) .help_heading("Target selection") .value_name("URL") - .multiple_values(true) - .multiple_occurrences(true) .use_delimiter(true) .value_hint(ValueHint::Url) - .help("The target URL(s) (required, unless --stdin used)"), + .help("The target URL (required, unless [--stdin || --resume-from] used)"), ) .arg( Arg::new("stdin") diff --git a/tests/test_parser.rs b/tests/test_parser.rs index 77ea6a83..bf3d53ee 100644 --- a/tests/test_parser.rs +++ b/tests/test_parser.rs @@ -33,8 +33,8 @@ fn parser_incorrect_param_with_tack_tack_help() { /// /// For more information try --help /// -/// the new behavior we expect to see is to print the long form help message, of which -/// Ludicrous speed... go! is near the bottom of that output, so we can test for that +/// the new behavior we expect to see is to print the short form help message, of which +/// "[CAUTION] 4 -v's is probably too much" is near the bottom of that output, so we can test for that fn parser_incorrect_param_with_tack_h() { Command::cargo_bin("feroxbuster") .unwrap() @@ -42,5 +42,7 @@ fn parser_incorrect_param_with_tack_h() { .arg("-h") .assert() .success() - .stdout(predicate::str::contains("Ludicrous speed... go!")); + .stdout(predicate::str::contains( + "[CAUTION] 4 -v's is probably too much", + )); } From 1500e651fa9b59ea7e8b455909054b8340ae7c43 Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 15 Jan 2022 19:04:53 -0600 Subject: [PATCH 3/3] clippy / nitpickery --- shell_completions/_feroxbuster | 4 ++-- shell_completions/_feroxbuster.ps1 | 4 ++-- shell_completions/feroxbuster.elv | 4 ++-- src/parser.rs | 15 +++++++++++---- src/scanner/requester.rs | 1 + 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/shell_completions/_feroxbuster b/shell_completions/_feroxbuster index dfb3b384..f7fb7b9f 100644 --- a/shell_completions/_feroxbuster +++ b/shell_completions/_feroxbuster @@ -82,8 +82,8 @@ _feroxbuster() { '--insecure[Disables TLS certificate validation in the client]' \ '-n[Do not scan recursively]' \ '--no-recursion[Do not scan recursively]' \ -'-e[Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)]' \ -'--extract-links[Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)]' \ +'-e[Extract links from response body (html, javascript, etc...); make new requests based on findings]' \ +'--extract-links[Extract links from response body (html, javascript, etc...); make new requests based on findings]' \ '(--auto-bail)--auto-tune[Automatically lower scan rate when an excessive amount of errors are encountered]' \ '--auto-bail[Automatically stop scanning when an excessive amount of errors are encountered]' \ '-D[Don'\''t auto-filter wildcard responses]' \ diff --git a/shell_completions/_feroxbuster.ps1 b/shell_completions/_feroxbuster.ps1 index aba6c9be..a38c7d8d 100644 --- a/shell_completions/_feroxbuster.ps1 +++ b/shell_completions/_feroxbuster.ps1 @@ -87,8 +87,8 @@ Register-ArgumentCompleter -Native -CommandName 'feroxbuster' -ScriptBlock { [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('-e', 'e', [CompletionResultType]::ParameterName, 'Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)') - [CompletionResult]::new('--extract-links', 'extract-links', [CompletionResultType]::ParameterName, 'Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)') + [CompletionResult]::new('-e', 'e', [CompletionResultType]::ParameterName, 'Extract links from response body (html, javascript, etc...); make new requests based on findings') + [CompletionResult]::new('--extract-links', 'extract-links', [CompletionResultType]::ParameterName, 'Extract links from response body (html, javascript, etc...); make new requests based on findings') [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') diff --git a/shell_completions/feroxbuster.elv b/shell_completions/feroxbuster.elv index 3accfb18..7234c7bb 100644 --- a/shell_completions/feroxbuster.elv +++ b/shell_completions/feroxbuster.elv @@ -85,8 +85,8 @@ set edit:completion:arg-completer[feroxbuster] = {|@words| cand --insecure 'Disables TLS certificate validation in the client' cand -n 'Do not scan recursively' cand --no-recursion 'Do not scan recursively' - cand -e 'Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)' - cand --extract-links 'Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)' + cand -e 'Extract links from response body (html, javascript, etc...); make new requests based on findings' + cand --extract-links 'Extract links from response body (html, javascript, etc...); make new requests based on findings' cand --auto-tune 'Automatically lower scan rate when an excessive amount of errors are encountered' cand --auto-bail 'Automatically stop scanning when an excessive amount of errors are encountered' cand -D 'Don''t auto-filter wildcard responses' diff --git a/src/parser.rs b/src/parser.rs index e5d11c1e..3bab47b0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -112,7 +112,6 @@ pub fn initialize() -> App<'static> { ///////////////////////////////////////////////////////////////////// // group - request settings ///////////////////////////////////////////////////////////////////// - let app = app .arg( Arg::new("user_agent") @@ -169,7 +168,6 @@ pub fn initialize() -> App<'static> { "Request's Body; can read data from a file if input starts with an @ (ex: @post.bin)", ), ) - .arg( Arg::new("headers") .short('H') @@ -220,6 +218,7 @@ pub fn initialize() -> App<'static> { .takes_value(false) .help("Append / to each request's URL") ); + ///////////////////////////////////////////////////////////////////// // group - request filters ///////////////////////////////////////////////////////////////////// @@ -234,11 +233,12 @@ pub fn initialize() -> App<'static> { .help_heading("Request filters") .help("URL(s) or Regex Pattern(s) to exclude from recursion/scans"), ); + ///////////////////////////////////////////////////////////////////// // group - response filters ///////////////////////////////////////////////////////////////////// let app = app - .arg( + .arg( Arg::new("filter_size") .short('S') .long("filter-size") @@ -336,6 +336,7 @@ pub fn initialize() -> App<'static> { "Status Codes to include (allow list) (default: 200 204 301 302 307 308 401 403 405)", ), ); + ///////////////////////////////////////////////////////////////////// // group - client settings ///////////////////////////////////////////////////////////////////// @@ -365,6 +366,7 @@ pub fn initialize() -> App<'static> { .help_heading("Client settings") .help("Disables TLS certificate validation in the client"), ); + ///////////////////////////////////////////////////////////////////// // group - scan settings ///////////////////////////////////////////////////////////////////// @@ -400,7 +402,7 @@ pub fn initialize() -> App<'static> { .long("extract-links") .takes_value(false) .help_heading("Scan settings") - .help("Extract links from response body (html, javascript, etc...); make new requests based on findings (default: false)") + .help("Extract links from response body (html, javascript, etc...); make new requests based on findings") ) .arg( Arg::new("scan_limit") @@ -469,6 +471,7 @@ pub fn initialize() -> App<'static> { .help_heading("Scan settings") .help("Don't auto-filter wildcard responses") ); + ///////////////////////////////////////////////////////////////////// // group - output settings ///////////////////////////////////////////////////////////////////// @@ -525,6 +528,7 @@ pub fn initialize() -> App<'static> { .help("Output file to write log entries (use w/ --json for JSON entries)") .takes_value(true), ); + ///////////////////////////////////////////////////////////////////// // group - miscellaneous ///////////////////////////////////////////////////////////////////// @@ -536,6 +540,9 @@ pub fn initialize() -> App<'static> { ) .after_long_help(EPILOGUE); + ///////////////////////////////////////////////////////////////////// + // end parser + ///////////////////////////////////////////////////////////////////// for arg in env::args() { // secure-77 noticed that when an incorrect flag/option is used, the short help message is printed // which is fine, but if you add -h|--help, it still errors out on the bad flag/option, diff --git a/src/scanner/requester.rs b/src/scanner/requester.rs index 6ce7326d..89b0422d 100644 --- a/src/scanner/requester.rs +++ b/src/scanner/requester.rs @@ -57,6 +57,7 @@ pub(super) struct Requester { /// need a usize to determine the number of consecutive non-error calls that a requester has /// seen; this will satisfy the non-mut self constraint (due to us being behind an Arc, and /// the need for a counter) + #[allow(clippy::mutex_atomic)] tuning_lock: Mutex, }