mirror of
https://github.com/epi052/feroxbuster.git
synced 2026-06-07 01:51:12 -03:00
updated clap from 3.x to 4.x
This commit is contained in:
23
Cargo.lock
generated
23
Cargo.lock
generated
@@ -320,35 +320,33 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.22"
|
||||
version = "4.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750"
|
||||
checksum = "5840cd9093aabeabf7fd932754c435b7674520fc3ddc935c397837050f0f1e4b"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"terminal_size 0.2.1",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "3.2.5"
|
||||
version = "4.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f7a2e0a962c45ce25afce14220bc24f9dade0a1787f185cecf96bfba7847cd8"
|
||||
checksum = "11cba7abac9b56dfe2f035098cdb3a43946f276e6db83b72c4e692343f9aab9a"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.4"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
@@ -2331,15 +2329,6 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b"
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
|
||||
dependencies = [
|
||||
"terminal_size 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thin-slice"
|
||||
version = "0.1.1"
|
||||
|
||||
@@ -22,8 +22,8 @@ build = "build.rs"
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "3.1.18", features = ["wrap_help", "cargo"] }
|
||||
clap_complete = "3.1.4"
|
||||
clap = { version = "4.0.8", features = ["wrap_help", "cargo"] }
|
||||
clap_complete = "4.0.2"
|
||||
regex = "1.5.5"
|
||||
lazy_static = "1.4.0"
|
||||
dirs = "4.0.0"
|
||||
@@ -39,7 +39,7 @@ reqwest = { version = "0.11.10", features = ["socks"] }
|
||||
# uses feature unification to add 'serde' to reqwest::Url
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
serde_regex = "1.1.0"
|
||||
clap = { version = "3.1.18", features = ["wrap_help", "cargo"] }
|
||||
clap = { version = "4.0.8", features = ["wrap_help", "cargo"] }
|
||||
lazy_static = "1.4.0"
|
||||
toml = "0.5.9"
|
||||
serde = { version = "1.0.137", features = ["derive", "rc"] }
|
||||
|
||||
@@ -69,10 +69,6 @@ _feroxbuster() {
|
||||
'-o+[Output file to write results to (use w/ --json for JSON entries)]:FILE:_files' \
|
||||
'--output=[Output file to write results to (use w/ --json for JSON entries)]:FILE:_files' \
|
||||
'--debug-log=[Output file to write log entries (use w/ --json for JSON entries)]:FILE:_files' \
|
||||
'-h[Print help information]' \
|
||||
'--help[Print help information]' \
|
||||
'-V[Print version information]' \
|
||||
'--version[Print version information]' \
|
||||
'(-u --url)--stdin[Read url(s) from STDIN]' \
|
||||
'(-p --proxy -k --insecure --burp-replay)--burp[Set --proxy to http://127.0.0.1:8080 and set --insecure to true]' \
|
||||
'(-P --replay-proxy -k --insecure)--burp-replay[Set --replay-proxy to http://127.0.0.1:8080 and set --insecure to true]' \
|
||||
@@ -108,6 +104,10 @@ _feroxbuster() {
|
||||
'--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]' \
|
||||
'--no-state[Disable state output file (*.state)]' \
|
||||
'-h[Print help information (use `--help` for more detail)]' \
|
||||
'--help[Print help information (use `--help` for more detail)]' \
|
||||
'-V[Print version information]' \
|
||||
'--version[Print version information]' \
|
||||
&& ret=0
|
||||
}
|
||||
|
||||
|
||||
@@ -75,10 +75,6 @@ Register-ArgumentCompleter -Native -CommandName 'feroxbuster' -ScriptBlock {
|
||||
[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('--burp', 'burp', [CompletionResultType]::ParameterName, 'Set --proxy to http://127.0.0.1:8080 and set --insecure to true')
|
||||
[CompletionResult]::new('--burp-replay', 'burp-replay', [CompletionResultType]::ParameterName, 'Set --replay-proxy to http://127.0.0.1:8080 and set --insecure to true')
|
||||
@@ -114,6 +110,10 @@ Register-ArgumentCompleter -Native -CommandName 'feroxbuster' -ScriptBlock {
|
||||
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Hide progress bars and banner (good for tmux windows w/ notifications)')
|
||||
[CompletionResult]::new('--json', 'json', [CompletionResultType]::ParameterName, 'Emit JSON logs to --output and --debug-log instead of normal text')
|
||||
[CompletionResult]::new('--no-state', 'no-state', [CompletionResultType]::ParameterName, 'Disable state output file (*.state)')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information (use `--help` for more detail)')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information (use `--help` for more detail)')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
@@ -8,8 +8,8 @@ _feroxbuster() {
|
||||
|
||||
for i in ${COMP_WORDS[@]}
|
||||
do
|
||||
case "${i}" in
|
||||
"$1")
|
||||
case "${cmd},${i}" in
|
||||
",$1")
|
||||
cmd="feroxbuster"
|
||||
;;
|
||||
*)
|
||||
@@ -19,7 +19,7 @@ _feroxbuster() {
|
||||
|
||||
case "${cmd}" in
|
||||
feroxbuster)
|
||||
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 -E -B -g -I -v -q -o --help --version --url --stdin --resume-from --burp --burp-replay --smart --thorough --proxy --replay-proxy --replay-codes --user-agent --random-agent --extensions --methods --data --headers --cookies --query --add-slash --dont-scan --filter-size --filter-regex --filter-words --filter-lines --filter-status --filter-similar-to --status-codes --timeout --redirects --insecure --threads --no-recursion --depth --force-recursion --extract-links --scan-limit --parallel --rate-limit --time-limit --wordlist --auto-tune --auto-bail --dont-filter --collect-extensions --collect-backups --collect-words --dont-collect --verbosity --silent --quiet --json --output --debug-log --no-state"
|
||||
opts="-u -p -P -R -a -A -x -m -H -b -Q -f -S -X -W -N -C -s -T -r -k -t -n -d -e -L -w -D -E -B -g -I -v -q -o -h -V --url --stdin --resume-from --burp --burp-replay --smart --thorough --proxy --replay-proxy --replay-codes --user-agent --random-agent --extensions --methods --data --headers --cookies --query --add-slash --dont-scan --filter-size --filter-regex --filter-words --filter-lines --filter-status --filter-similar-to --status-codes --timeout --redirects --insecure --threads --no-recursion --depth --force-recursion --extract-links --scan-limit --parallel --rate-limit --time-limit --wordlist --auto-tune --auto-bail --dont-filter --collect-extensions --collect-backups --collect-words --dont-collect --verbosity --silent --quiet --json --output --debug-log --no-state --help --version"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
|
||||
@@ -72,10 +72,6 @@ set edit:completion:arg-completer[feroxbuster] = {|@words|
|
||||
cand -o 'Output file to write results to (use w/ --json for JSON entries)'
|
||||
cand --output 'Output file to write results to (use w/ --json for JSON entries)'
|
||||
cand --debug-log 'Output file to write log entries (use w/ --json for JSON entries)'
|
||||
cand -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 --burp 'Set --proxy to http://127.0.0.1:8080 and set --insecure to true'
|
||||
cand --burp-replay 'Set --replay-proxy to http://127.0.0.1:8080 and set --insecure to true'
|
||||
@@ -111,6 +107,10 @@ set edit:completion:arg-completer[feroxbuster] = {|@words|
|
||||
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'
|
||||
cand --no-state 'Disable state output file (*.state)'
|
||||
cand -h 'Print help information (use `--help` for more detail)'
|
||||
cand --help 'Print help information (use `--help` for more detail)'
|
||||
cand -V 'Print version information'
|
||||
cand --version 'Print version information'
|
||||
}
|
||||
]
|
||||
$completions[$command]
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
DEFAULT_CONFIG_NAME,
|
||||
};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use clap::ArgMatches;
|
||||
use clap::{parser::ValueSource, ArgMatches};
|
||||
use regex::Regex;
|
||||
use reqwest::{Client, Method, StatusCode, Url};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -22,15 +22,10 @@ use std::{
|
||||
|
||||
/// macro helper to abstract away repetitive configuration updates
|
||||
macro_rules! update_config_if_present {
|
||||
($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
|
||||
}
|
||||
}
|
||||
($conf_val:expr, $matches:ident, $arg_name:expr, $arg_type:ty) => {
|
||||
match $matches.get_one::<$arg_type>($arg_name) {
|
||||
Some(value) => *$conf_val = value.to_owned(), // Update value
|
||||
None => {}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -44,6 +39,35 @@ macro_rules! update_if_not_default {
|
||||
};
|
||||
}
|
||||
|
||||
/// macro helper to abstract away repetitive checks to see if the user has specified a value
|
||||
/// for a given argument from the commandline or if we just had a default value in the parser
|
||||
macro_rules! came_from_cli {
|
||||
($matches:ident, $arg_name:expr) => {
|
||||
matches!(
|
||||
$matches.value_source($arg_name),
|
||||
Some(ValueSource::CommandLine)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// macro helper to abstract away repetitive if not default: update checks, specifically for
|
||||
/// values that are number types, i.e. usize, u64, etc
|
||||
macro_rules! update_config_with_num_type_if_present {
|
||||
($conf_val:expr, $matches:ident, $arg_name:expr, $arg_type:ty) => {
|
||||
if let Some(val) = $matches.get_one::<String>($arg_name) {
|
||||
match val.parse::<$arg_type>() {
|
||||
Ok(v) => *$conf_val = v,
|
||||
Err(_) => {
|
||||
report_and_exit(&format!(
|
||||
"Invalid value for --{}, must be a positive integer",
|
||||
$arg_name
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents the final, global configuration of the program.
|
||||
///
|
||||
/// This struct is the combination of the following:
|
||||
@@ -460,7 +484,7 @@ impl Configuration {
|
||||
|
||||
// --resume-from used, need to first read the Configuration from disk, and then
|
||||
// merge the cli_config into the resumed config
|
||||
if let Some(filename) = args.value_of("resume_from") {
|
||||
if let Some(filename) = args.get_one::<String>("resume_from") {
|
||||
// when resuming a scan, instead of normal configuration loading, we just
|
||||
// load the config from disk by calling resume_scan
|
||||
let mut previous_config = resume_scan(filename);
|
||||
@@ -546,18 +570,21 @@ impl Configuration {
|
||||
fn parse_cli_args(args: &ArgMatches) -> Self {
|
||||
let mut config = Configuration::default();
|
||||
|
||||
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");
|
||||
update_config_with_num_type_if_present!(&mut config.threads, args, "threads", usize);
|
||||
update_config_with_num_type_if_present!(&mut config.parallel, args, "parallel", usize);
|
||||
update_config_with_num_type_if_present!(&mut config.depth, args, "depth", usize);
|
||||
update_config_with_num_type_if_present!(&mut config.scan_limit, args, "scan_limit", usize);
|
||||
update_config_with_num_type_if_present!(&mut config.rate_limit, args, "rate_limit", usize);
|
||||
update_config_if_present!(&mut config.wordlist, args, "wordlist", String);
|
||||
update_config_if_present!(&mut config.output, args, "output", String);
|
||||
update_config_if_present!(&mut config.debug_log, args, "debug_log", String);
|
||||
update_config_if_present!(&mut config.resume_from, args, "resume_from", String);
|
||||
|
||||
if let Some(arg) = args.values_of("status_codes") {
|
||||
if let Ok(Some(inner)) = args.try_get_one::<String>("time_limit") {
|
||||
config.time_limit = inner.to_owned();
|
||||
}
|
||||
|
||||
if let Some(arg) = args.get_many::<String>("status_codes") {
|
||||
config.status_codes = arg
|
||||
.map(|code| {
|
||||
StatusCode::from_bytes(code.as_bytes())
|
||||
@@ -567,7 +594,7 @@ impl Configuration {
|
||||
.collect();
|
||||
}
|
||||
|
||||
if let Some(arg) = args.values_of("replay_codes") {
|
||||
if let Some(arg) = args.get_many::<String>("replay_codes") {
|
||||
// replay codes passed in by the user
|
||||
config.replay_codes = arg
|
||||
.map(|code| {
|
||||
@@ -581,7 +608,7 @@ impl Configuration {
|
||||
config.replay_codes = config.status_codes.clone();
|
||||
}
|
||||
|
||||
if let Some(arg) = args.values_of("filter_status") {
|
||||
if let Some(arg) = args.get_many::<String>("filter_status") {
|
||||
config.filter_status = arg
|
||||
.map(|code| {
|
||||
StatusCode::from_bytes(code.as_bytes())
|
||||
@@ -591,17 +618,17 @@ impl Configuration {
|
||||
.collect();
|
||||
}
|
||||
|
||||
if let Some(arg) = args.values_of("extensions") {
|
||||
if let Some(arg) = args.get_many::<String>("extensions") {
|
||||
config.extensions = arg
|
||||
.map(|val| val.trim_start_matches('.').to_string())
|
||||
.collect();
|
||||
}
|
||||
|
||||
if let Some(arg) = args.values_of("dont_collect") {
|
||||
if let Some(arg) = args.get_many::<String>("dont_collect") {
|
||||
config.dont_collect = arg.map(|val| val.to_string()).collect();
|
||||
}
|
||||
|
||||
if let Some(arg) = args.values_of("methods") {
|
||||
if let Some(arg) = args.get_many::<String>("methods") {
|
||||
config.methods = arg
|
||||
.map(|val| {
|
||||
// Check methods if they are correct
|
||||
@@ -613,7 +640,7 @@ impl Configuration {
|
||||
.collect();
|
||||
}
|
||||
|
||||
if let Some(arg) = args.value_of("data") {
|
||||
if let Some(arg) = args.get_one::<String>("data") {
|
||||
if let Some(stripped) = arg.strip_prefix('@') {
|
||||
config.data =
|
||||
std::fs::read(stripped).unwrap_or_else(|e| report_and_exit(&e.to_string()));
|
||||
@@ -622,13 +649,13 @@ impl Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
if args.is_present("stdin") {
|
||||
if came_from_cli!(args, "stdin") {
|
||||
config.stdin = true;
|
||||
} else if let Some(url) = args.value_of("url") {
|
||||
config.target_url = String::from(url);
|
||||
} else if let Some(url) = args.get_one::<String>("url") {
|
||||
config.target_url = url.into();
|
||||
}
|
||||
|
||||
if let Some(arg) = args.values_of("url_denylist") {
|
||||
if let Some(arg) = args.get_many::<String>("url_denylist") {
|
||||
// compile all regular expressions and absolute urls used for --dont-scan
|
||||
//
|
||||
// when --dont-scan is used, the should_deny_url function is called at least once per
|
||||
@@ -672,15 +699,15 @@ impl Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(arg) = args.values_of("filter_regex") {
|
||||
if let Some(arg) = args.get_many::<String>("filter_regex") {
|
||||
config.filter_regex = arg.map(|val| val.to_string()).collect();
|
||||
}
|
||||
|
||||
if let Some(arg) = args.values_of("filter_similar") {
|
||||
if let Some(arg) = args.get_many::<String>("filter_similar") {
|
||||
config.filter_similar = arg.map(|val| val.to_string()).collect();
|
||||
}
|
||||
|
||||
if let Some(arg) = args.values_of("filter_size") {
|
||||
if let Some(arg) = args.get_many::<String>("filter_size") {
|
||||
config.filter_size = arg
|
||||
.map(|size| {
|
||||
size.parse::<u64>()
|
||||
@@ -689,7 +716,7 @@ impl Configuration {
|
||||
.collect();
|
||||
}
|
||||
|
||||
if let Some(arg) = args.values_of("filter_words") {
|
||||
if let Some(arg) = args.get_many::<String>("filter_words") {
|
||||
config.filter_word_count = arg
|
||||
.map(|size| {
|
||||
size.parse::<usize>()
|
||||
@@ -698,7 +725,7 @@ impl Configuration {
|
||||
.collect();
|
||||
}
|
||||
|
||||
if let Some(arg) = args.values_of("filter_lines") {
|
||||
if let Some(arg) = args.get_many::<String>("filter_lines") {
|
||||
config.filter_line_count = arg
|
||||
.map(|size| {
|
||||
size.parse::<usize>()
|
||||
@@ -707,7 +734,7 @@ impl Configuration {
|
||||
.collect();
|
||||
}
|
||||
|
||||
if args.is_present("silent") {
|
||||
if came_from_cli!(args, "silent") {
|
||||
// the reason this is protected by an if statement:
|
||||
// consider a user specifying silent = true in ferox-config.toml
|
||||
// if the line below is outside of the if, we'd overwrite true with
|
||||
@@ -716,106 +743,111 @@ impl Configuration {
|
||||
config.output_level = OutputLevel::Silent;
|
||||
}
|
||||
|
||||
if args.is_present("quiet") {
|
||||
if came_from_cli!(args, "quiet") {
|
||||
config.quiet = true;
|
||||
config.output_level = OutputLevel::Quiet;
|
||||
}
|
||||
|
||||
if args.is_present("auto_tune") || args.is_present("smart") || args.is_present("thorough") {
|
||||
if came_from_cli!(args, "auto_tune")
|
||||
|| came_from_cli!(args, "smart")
|
||||
|| came_from_cli!(args, "thorough")
|
||||
{
|
||||
config.auto_tune = true;
|
||||
config.requester_policy = RequesterPolicy::AutoTune;
|
||||
}
|
||||
|
||||
if args.is_present("auto_bail") {
|
||||
if came_from_cli!(args, "auto_bail") {
|
||||
config.auto_bail = true;
|
||||
config.requester_policy = RequesterPolicy::AutoBail;
|
||||
}
|
||||
|
||||
if args.is_present("no_state") {
|
||||
if came_from_cli!(args, "no_state") {
|
||||
config.save_state = false;
|
||||
}
|
||||
|
||||
if args.is_present("dont_filter") {
|
||||
if came_from_cli!(args, "dont_filter") {
|
||||
config.dont_filter = true;
|
||||
}
|
||||
|
||||
if args.is_present("collect_extensions") || args.is_present("thorough") {
|
||||
if came_from_cli!(args, "collect_extensions") || came_from_cli!(args, "thorough") {
|
||||
config.collect_extensions = true;
|
||||
}
|
||||
|
||||
if args.is_present("collect_backups")
|
||||
|| args.is_present("smart")
|
||||
|| args.is_present("thorough")
|
||||
if came_from_cli!(args, "collect_backups")
|
||||
|| came_from_cli!(args, "smart")
|
||||
|| came_from_cli!(args, "thorough")
|
||||
{
|
||||
config.collect_backups = true;
|
||||
}
|
||||
|
||||
if args.is_present("collect_words")
|
||||
|| args.is_present("smart")
|
||||
|| args.is_present("thorough")
|
||||
if came_from_cli!(args, "collect_words")
|
||||
|| came_from_cli!(args, "smart")
|
||||
|| came_from_cli!(args, "thorough")
|
||||
{
|
||||
config.collect_words = true;
|
||||
}
|
||||
|
||||
if args.occurrences_of("verbosity") > 0 {
|
||||
if args.get_count("verbosity") > 0 {
|
||||
// occurrences_of returns 0 if none are found; this is protected in
|
||||
// an if block for the same reason as the quiet option
|
||||
config.verbosity = args.occurrences_of("verbosity") as u8;
|
||||
config.verbosity = args.get_count("verbosity") as u8;
|
||||
}
|
||||
|
||||
if args.is_present("no_recursion") {
|
||||
if came_from_cli!(args, "no_recursion") {
|
||||
config.no_recursion = true;
|
||||
}
|
||||
|
||||
if args.is_present("add_slash") {
|
||||
if came_from_cli!(args, "add_slash") {
|
||||
config.add_slash = true;
|
||||
}
|
||||
|
||||
if args.is_present("extract_links")
|
||||
|| args.is_present("smart")
|
||||
|| args.is_present("thorough")
|
||||
if came_from_cli!(args, "extract_links")
|
||||
|| came_from_cli!(args, "smart")
|
||||
|| came_from_cli!(args, "thorough")
|
||||
{
|
||||
config.extract_links = true;
|
||||
}
|
||||
|
||||
if args.is_present("json") {
|
||||
if came_from_cli!(args, "json") {
|
||||
config.json = true;
|
||||
}
|
||||
|
||||
if args.is_present("force_recursion") {
|
||||
if came_from_cli!(args, "force_recursion") {
|
||||
config.force_recursion = true;
|
||||
}
|
||||
|
||||
////
|
||||
// organizational breakpoint; all options below alter the Client configuration
|
||||
////
|
||||
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");
|
||||
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_with_num_type_if_present!(&mut config.timeout, args, "timeout", u64);
|
||||
|
||||
if args.is_present("burp") {
|
||||
if came_from_cli!(args, "burp") {
|
||||
config.proxy = String::from("http://127.0.0.1:8080");
|
||||
}
|
||||
|
||||
if args.is_present("burp_replay") {
|
||||
if came_from_cli!(args, "burp_replay") {
|
||||
config.replay_proxy = String::from("http://127.0.0.1:8080");
|
||||
}
|
||||
|
||||
if args.is_present("random_agent") {
|
||||
if came_from_cli!(args, "random_agent") {
|
||||
config.random_agent = true;
|
||||
}
|
||||
|
||||
if args.is_present("redirects") {
|
||||
if came_from_cli!(args, "redirects") {
|
||||
config.redirects = true;
|
||||
}
|
||||
|
||||
if args.is_present("insecure") || args.is_present("burp") || args.is_present("burp_replay")
|
||||
if came_from_cli!(args, "insecure")
|
||||
|| came_from_cli!(args, "burp")
|
||||
|| came_from_cli!(args, "burp_replay")
|
||||
{
|
||||
config.insecure = true;
|
||||
}
|
||||
|
||||
if let Some(headers) = args.values_of("headers") {
|
||||
if let Some(headers) = args.get_many::<String>("headers") {
|
||||
for val in headers {
|
||||
let mut split_val = val.split(':');
|
||||
|
||||
@@ -829,7 +861,7 @@ impl Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(cookies) = args.values_of("cookies") {
|
||||
if let Some(cookies) = args.get_many::<String>("cookies") {
|
||||
config.headers.insert(
|
||||
// we know the header name is always "cookie"
|
||||
"Cookie".to_string(),
|
||||
@@ -845,7 +877,7 @@ impl Configuration {
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(queries) = args.values_of("queries") {
|
||||
if let Some(queries) = args.get_many::<String>("queries") {
|
||||
for val in queries {
|
||||
// same basic logic used as reading in the headers HashMap above
|
||||
let mut split_val = val.split('=');
|
||||
|
||||
157
src/parser.rs
157
src/parser.rs
@@ -1,3 +1,4 @@
|
||||
use clap::ArgAction;
|
||||
use clap::{
|
||||
crate_authors, crate_description, crate_name, crate_version, Arg, ArgGroup, Command, ValueHint,
|
||||
};
|
||||
@@ -25,7 +26,7 @@ lazy_static! {
|
||||
}
|
||||
|
||||
/// 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() -> Command<'static> {
|
||||
pub fn initialize() -> Command {
|
||||
let app = Command::new(crate_name!())
|
||||
.version(crate_version!())
|
||||
.author(crate_authors!())
|
||||
@@ -50,7 +51,7 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("stdin")
|
||||
.long("stdin")
|
||||
.help_heading("Target selection")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.help("Read url(s) from STDIN")
|
||||
.conflicts_with("url")
|
||||
)
|
||||
@@ -62,7 +63,7 @@ pub fn initialize() -> Command<'static> {
|
||||
.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),
|
||||
.num_args(1),
|
||||
);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
@@ -72,6 +73,7 @@ pub fn initialize() -> Command<'static> {
|
||||
.arg(
|
||||
Arg::new("burp")
|
||||
.long("burp")
|
||||
.num_args(0)
|
||||
.help_heading("Composite settings")
|
||||
.conflicts_with_all(&["proxy", "insecure", "burp_replay"])
|
||||
.help("Set --proxy to http://127.0.0.1:8080 and set --insecure to true"),
|
||||
@@ -79,6 +81,7 @@ pub fn initialize() -> Command<'static> {
|
||||
.arg(
|
||||
Arg::new("burp_replay")
|
||||
.long("burp-replay")
|
||||
.num_args(0)
|
||||
.help_heading("Composite settings")
|
||||
.conflicts_with_all(&["replay_proxy", "insecure"])
|
||||
.help("Set --replay-proxy to http://127.0.0.1:8080 and set --insecure to true"),
|
||||
@@ -86,11 +89,13 @@ pub fn initialize() -> Command<'static> {
|
||||
.arg(
|
||||
Arg::new("smart")
|
||||
.long("smart")
|
||||
.num_args(0)
|
||||
.help_heading("Composite settings")
|
||||
.help("Set --extract-links, --auto-tune, --collect-words, and --collect-backups to true"),
|
||||
).arg(
|
||||
Arg::new("thorough")
|
||||
.long("thorough")
|
||||
.num_args(0)
|
||||
.help_heading("Composite settings")
|
||||
.help("Use the same settings as --smart and set --collect-extensions to true"),
|
||||
);
|
||||
@@ -103,7 +108,7 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("proxy")
|
||||
.short('p')
|
||||
.long("proxy")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.value_name("PROXY")
|
||||
.value_hint(ValueHint::Url)
|
||||
.help_heading("Proxy settings")
|
||||
@@ -115,7 +120,7 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("replay_proxy")
|
||||
.short('P')
|
||||
.long("replay-proxy")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.value_hint(ValueHint::Url)
|
||||
.value_name("REPLAY_PROXY")
|
||||
.help_heading("Proxy settings")
|
||||
@@ -128,9 +133,8 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('R')
|
||||
.long("replay-codes")
|
||||
.value_name("REPLAY_CODE")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.use_value_delimiter(true)
|
||||
.requires("replay_proxy")
|
||||
.help_heading("Proxy settings")
|
||||
@@ -148,7 +152,7 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('a')
|
||||
.long("user-agent")
|
||||
.value_name("USER_AGENT")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.help_heading("Request settings")
|
||||
.help(&**DEFAULT_USER_AGENT),
|
||||
)
|
||||
@@ -156,7 +160,7 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("random_agent")
|
||||
.short('A')
|
||||
.long("random-agent")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.help_heading("Request settings")
|
||||
.help("Use a random User-Agent"),
|
||||
)
|
||||
@@ -165,9 +169,8 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('x')
|
||||
.long("extensions")
|
||||
.value_name("FILE_EXTENSION")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.use_value_delimiter(true)
|
||||
.help_heading("Request settings")
|
||||
.help(
|
||||
@@ -179,9 +182,8 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('m')
|
||||
.long("methods")
|
||||
.value_name("HTTP_METHODS")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.use_value_delimiter(true)
|
||||
.help_heading("Request settings")
|
||||
.help(
|
||||
@@ -192,7 +194,7 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("data")
|
||||
.long("data")
|
||||
.value_name("DATA")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.help_heading("Request settings")
|
||||
.help(
|
||||
"Request's Body; can read data from a file if input starts with an @ (ex: @post.bin)",
|
||||
@@ -203,10 +205,9 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('H')
|
||||
.long("headers")
|
||||
.value_name("HEADER")
|
||||
.takes_value(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.help_heading("Request settings")
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.use_value_delimiter(true)
|
||||
.help(
|
||||
"Specify HTTP headers to be used in each request (ex: -H Header:val -H 'stuff: things')",
|
||||
@@ -217,9 +218,8 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('b')
|
||||
.long("cookies")
|
||||
.value_name("COOKIE")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.use_value_delimiter(true)
|
||||
.help_heading("Request settings")
|
||||
.help(
|
||||
@@ -231,9 +231,8 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('Q')
|
||||
.long("query")
|
||||
.value_name("QUERY")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.use_value_delimiter(true)
|
||||
.help_heading("Request settings")
|
||||
.help(
|
||||
@@ -245,7 +244,7 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('f')
|
||||
.long("add-slash")
|
||||
.help_heading("Request settings")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.help("Append / to each request's URL")
|
||||
);
|
||||
|
||||
@@ -256,9 +255,8 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("url_denylist")
|
||||
.long("dont-scan")
|
||||
.value_name("URL")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.use_value_delimiter(true)
|
||||
.help_heading("Request filters")
|
||||
.help("URL(s) or Regex Pattern(s) to exclude from recursion/scans"),
|
||||
@@ -273,9 +271,8 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('S')
|
||||
.long("filter-size")
|
||||
.value_name("SIZE")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.use_value_delimiter(true)
|
||||
.help_heading("Response filters")
|
||||
.help(
|
||||
@@ -287,9 +284,8 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('X')
|
||||
.long("filter-regex")
|
||||
.value_name("REGEX")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.use_value_delimiter(true)
|
||||
.help_heading("Response filters")
|
||||
.help(
|
||||
@@ -301,9 +297,8 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('W')
|
||||
.long("filter-words")
|
||||
.value_name("WORDS")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.use_value_delimiter(true)
|
||||
.help_heading("Response filters")
|
||||
.help(
|
||||
@@ -315,9 +310,8 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('N')
|
||||
.long("filter-lines")
|
||||
.value_name("LINES")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.use_value_delimiter(true)
|
||||
.help_heading("Response filters")
|
||||
.help(
|
||||
@@ -329,9 +323,8 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('C')
|
||||
.long("filter-status")
|
||||
.value_name("STATUS_CODE")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.use_value_delimiter(true)
|
||||
.conflicts_with("status_codes")
|
||||
.help_heading("Response filters")
|
||||
@@ -343,9 +336,8 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("filter_similar")
|
||||
.long("filter-similar-to")
|
||||
.value_name("UNWANTED_PAGE")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.value_hint(ValueHint::Url)
|
||||
.use_value_delimiter(true)
|
||||
.help_heading("Response filters")
|
||||
@@ -358,9 +350,8 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('s')
|
||||
.long("status-codes")
|
||||
.value_name("STATUS_CODE")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.use_value_delimiter(true)
|
||||
.help_heading("Response filters")
|
||||
.help(
|
||||
@@ -377,7 +368,7 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('T')
|
||||
.long("timeout")
|
||||
.value_name("SECONDS")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.help_heading("Client settings")
|
||||
.help("Number of seconds before a client's request times out (default: 7)"),
|
||||
)
|
||||
@@ -385,7 +376,7 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("redirects")
|
||||
.short('r')
|
||||
.long("redirects")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.help_heading("Client settings")
|
||||
.help("Allow client to follow redirects"),
|
||||
)
|
||||
@@ -393,7 +384,7 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("insecure")
|
||||
.short('k')
|
||||
.long("insecure")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.help_heading("Client settings")
|
||||
.help("Disables TLS certificate validation in the client"),
|
||||
);
|
||||
@@ -407,7 +398,7 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('t')
|
||||
.long("threads")
|
||||
.value_name("THREADS")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.help_heading("Scan settings")
|
||||
.help("Number of concurrent threads (default: 50)"),
|
||||
)
|
||||
@@ -415,7 +406,7 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("no_recursion")
|
||||
.short('n')
|
||||
.long("no-recursion")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.help_heading("Scan settings")
|
||||
.help("Do not scan recursively"),
|
||||
)
|
||||
@@ -424,12 +415,13 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('d')
|
||||
.long("depth")
|
||||
.value_name("RECURSION_DEPTH")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.help_heading("Scan settings")
|
||||
.help("Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)"),
|
||||
).arg(
|
||||
Arg::new("force_recursion")
|
||||
.long("force-recursion")
|
||||
.num_args(0)
|
||||
.conflicts_with("no_recursion")
|
||||
.help_heading("Scan settings")
|
||||
.help("Force recursion attempts on all 'found' endpoints (still respects recursion depth)"),
|
||||
@@ -437,7 +429,7 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("extract_links")
|
||||
.short('e')
|
||||
.long("extract-links")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.help_heading("Scan settings")
|
||||
.help("Extract links from response body (html, javascript, etc...); make new requests based on findings")
|
||||
)
|
||||
@@ -446,7 +438,7 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('L')
|
||||
.long("scan-limit")
|
||||
.value_name("SCAN_LIMIT")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.help_heading("Scan settings")
|
||||
.help("Limit total number of concurrent scans (default: 0, i.e. no limit)")
|
||||
)
|
||||
@@ -454,7 +446,7 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("parallel")
|
||||
.long("parallel")
|
||||
.value_name("PARALLEL_SCANS")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.requires("stdin")
|
||||
.help_heading("Scan settings")
|
||||
.help("Run parallel feroxbuster instances (one child process per url passed via stdin)")
|
||||
@@ -463,7 +455,7 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("rate_limit")
|
||||
.long("rate-limit")
|
||||
.value_name("RATE_LIMIT")
|
||||
.takes_value(true)
|
||||
.num_args(1)
|
||||
.conflicts_with("auto_tune")
|
||||
.help_heading("Scan settings")
|
||||
.help("Limit number of requests per second (per directory) (default: 0, i.e. no limit)")
|
||||
@@ -472,8 +464,8 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("time_limit")
|
||||
.long("time-limit")
|
||||
.value_name("TIME_SPEC")
|
||||
.takes_value(true)
|
||||
.validator(valid_time_spec)
|
||||
.num_args(1)
|
||||
.value_parser(valid_time_spec)
|
||||
.help_heading("Scan settings")
|
||||
.help("Limit total run time of all scans (ex: --time-limit 10m)")
|
||||
)
|
||||
@@ -485,11 +477,11 @@ pub fn initialize() -> Command<'static> {
|
||||
.value_name("FILE")
|
||||
.help("Path to the wordlist")
|
||||
.help_heading("Scan settings")
|
||||
.takes_value(true),
|
||||
.num_args(1),
|
||||
).arg(
|
||||
Arg::new("auto_tune")
|
||||
.long("auto-tune")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.conflicts_with("auto_bail")
|
||||
.help_heading("Scan settings")
|
||||
.help("Automatically lower scan rate when an excessive amount of errors are encountered")
|
||||
@@ -497,35 +489,35 @@ pub fn initialize() -> Command<'static> {
|
||||
.arg(
|
||||
Arg::new("auto_bail")
|
||||
.long("auto-bail")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.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)
|
||||
.num_args(0)
|
||||
.help_heading("Scan settings")
|
||||
.help("Don't auto-filter wildcard responses")
|
||||
).arg(
|
||||
Arg::new("collect_extensions")
|
||||
.short('E')
|
||||
.long("collect-extensions")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.help_heading("Dynamic collection settings")
|
||||
.help("Automatically discover extensions and add them to --extensions (unless they're in --dont-collect)")
|
||||
).arg(
|
||||
Arg::new("collect_backups")
|
||||
.short('B')
|
||||
.long("collect-backups")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.help_heading("Dynamic collection settings")
|
||||
.help("Automatically request likely backup extensions for \"found\" urls")
|
||||
).arg(
|
||||
Arg::new("collect_words")
|
||||
.short('g')
|
||||
.long("collect-words")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.help_heading("Dynamic collection settings")
|
||||
.help("Automatically discover important words from within responses and add them to the wordlist")
|
||||
).arg(
|
||||
@@ -533,9 +525,8 @@ pub fn initialize() -> Command<'static> {
|
||||
.short('I')
|
||||
.long("dont-collect")
|
||||
.value_name("FILE_EXTENSION")
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.use_value_delimiter(true)
|
||||
.help_heading("Dynamic collection settings")
|
||||
.help(
|
||||
@@ -551,15 +542,15 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("verbosity")
|
||||
.short('v')
|
||||
.long("verbosity")
|
||||
.takes_value(false)
|
||||
.multiple_occurrences(true)
|
||||
.num_args(0)
|
||||
.action(ArgAction::Count)
|
||||
.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)
|
||||
.num_args(0)
|
||||
.conflicts_with("quiet")
|
||||
.help_heading("Output settings")
|
||||
.help("Only print URLs + turn off logging (good for piping a list of urls to other commands)")
|
||||
@@ -568,7 +559,7 @@ pub fn initialize() -> Command<'static> {
|
||||
Arg::new("quiet")
|
||||
.short('q')
|
||||
.long("quiet")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.help_heading("Output settings")
|
||||
.help("Hide progress bars and banner (good for tmux windows w/ notifications)")
|
||||
)
|
||||
@@ -576,7 +567,7 @@ pub fn initialize() -> Command<'static> {
|
||||
.arg(
|
||||
Arg::new("json")
|
||||
.long("json")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.requires("output_files")
|
||||
.help_heading("Output settings")
|
||||
.help("Emit JSON logs to --output and --debug-log instead of normal text")
|
||||
@@ -588,7 +579,7 @@ pub fn initialize() -> Command<'static> {
|
||||
.value_name("FILE")
|
||||
.help_heading("Output settings")
|
||||
.help("Output file to write results to (use w/ --json for JSON entries)")
|
||||
.takes_value(true),
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("debug_log")
|
||||
@@ -597,12 +588,12 @@ pub fn initialize() -> Command<'static> {
|
||||
.value_hint(ValueHint::FilePath)
|
||||
.help_heading("Output settings")
|
||||
.help("Output file to write log entries (use w/ --json for JSON entries)")
|
||||
.takes_value(true),
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("no_state")
|
||||
.long("no-state")
|
||||
.takes_value(false)
|
||||
.num_args(0)
|
||||
.help_heading("Output settings")
|
||||
.help("Disable state output file (*.state)")
|
||||
);
|
||||
@@ -641,9 +632,9 @@ pub fn initialize() -> Command<'static> {
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
fn valid_time_spec(time_spec: &str) -> Result<String, String> {
|
||||
match TIMESPEC_REGEX.is_match(time_spec) {
|
||||
true => Ok(()),
|
||||
true => Ok(time_spec.to_string()),
|
||||
false => {
|
||||
let msg = format!(
|
||||
"Expected a non-negative, whole number followed by s, m, h, or d (case insensitive); received {}",
|
||||
|
||||
@@ -42,7 +42,13 @@ fn parser_incorrect_param_with_tack_h() {
|
||||
.arg("-h")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains(
|
||||
"[CAUTION] 4 -v's is probably too much",
|
||||
));
|
||||
.stdout(
|
||||
predicate::str::contains("[CAUTION]")
|
||||
.and(predicate::str::contains("4"))
|
||||
.and(predicate::str::contains("-v's"))
|
||||
.and(predicate::str::contains("is"))
|
||||
.and(predicate::str::contains("probably"))
|
||||
.and(predicate::str::contains("too"))
|
||||
.and(predicate::str::contains("much")),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user