diff --git a/README.md b/README.md index 7a81b12..c3cb748 100644 --- a/README.md +++ b/README.md @@ -376,6 +376,7 @@ A pre-made configuration file with examples of all available settings can be fou # scan_limit = 6 # rate_limit = 250 # quiet = true +# silent = true # json = true # output = "/targets/ellingson_mineral_company/gibson.txt" # debug_log = "/var/log/find-the-derp.log" @@ -538,7 +539,7 @@ same goes for urls, headers, status codes, queries, and size filters. ### Read urls from STDIN; pipe only resulting urls out to another tool ``` -cat targets | ./feroxbuster --stdin --quiet -s 200 301 302 --redirects -x js | fff -s 200 -o js-files +cat targets | ./feroxbuster --stdin --silent -s 200 301 302 --redirects -x js | fff -s 200 -o js-files ``` ### Proxy traffic through Burp diff --git a/ferox-config.toml.example b/ferox-config.toml.example index 63697c3..ba95630 100644 --- a/ferox-config.toml.example +++ b/ferox-config.toml.example @@ -19,6 +19,7 @@ # scan_limit = 6 # rate_limit = 250 # quiet = true +# silent = true # json = true # output = "/targets/ellingson_mineral_company/gibson.txt" # debug_log = "/var/log/find-the-derp.log" diff --git a/src/config/container.rs b/src/config/container.rs index 1fb9834..ca54fda 100644 --- a/src/config/container.rs +++ b/src/config/container.rs @@ -547,22 +547,22 @@ impl Configuration { if args.is_present("silent") { // the reason this is protected by an if statement: - // consider a user specifying quiet = true in ferox-config.toml + // consider a user specifying silent = true in ferox-config.toml // if the line below is outside of the if, we'd overwrite true with - // false if no -q is used on the command line + // false if no --silent is used on the command line config.silent = true; config.output_level = OutputLevel::Silent; } - if args.is_present("dont_filter") { - config.dont_filter = true; - } - if args.is_present("quiet") { config.quiet = true; config.output_level = OutputLevel::Quiet; } + if args.is_present("dont_filter") { + config.dont_filter = true; + } + if args.occurrences_of("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 diff --git a/src/heuristics.rs b/src/heuristics.rs index 262b997..d84feda 100644 --- a/src/heuristics.rs +++ b/src/heuristics.rs @@ -4,11 +4,12 @@ use anyhow::{bail, Result}; use console::style; use uuid::Uuid; -use crate::response::FeroxResponse; use crate::{ + config::OutputLevel, event_handlers::{Command, Handles}, filters::WildcardFilter, progress::PROGRESS_PRINTER, + response::FeroxResponse, skip_fail, url::FeroxUrl, utils::{ferox_print, fmt_err, make_request, status_colorizer}, @@ -116,14 +117,20 @@ impl HeuristicTests { wildcard.dynamic = wc_length - url_len; - if !self.handles.config.silent { + if matches!( + handles.config.output_level, + OutputLevel::Default | OutputLevel::Quiet + ) { let msg = format_template!("{} {:>9} {:>9} {:>9} Wildcard response is dynamic; {} ({} + url length) responses; toggle this behavior by using {}\n", wildcard.dynamic); ferox_print(&msg, &PROGRESS_PRINTER); } } else if wc_length == wc2_length { wildcard.size = wc_length; - if !self.handles.config.silent { + if matches!( + handles.config.output_level, + OutputLevel::Default | OutputLevel::Quiet + ) { let msg = format_template!("{} {:>9} {:>9} {:>9} Wildcard response is static; {} {} responses; toggle this behavior by using {}\n", wildcard.size); ferox_print(&msg, &PROGRESS_PRINTER); } @@ -179,7 +186,10 @@ impl HeuristicTests { bail!("filtered response") } - if !self.handles.config.silent { + if matches!( + handles.config.output_level, + OutputLevel::Default | OutputLevel::Quiet + ) { let boxed = Box::new(ferox_response.clone()); self.handles.output.send(Command::Report(boxed))?; } @@ -218,7 +228,10 @@ impl HeuristicTests { good_urls.push(target_url.to_owned()); } Err(e) => { - if !self.handles.config.silent { + if matches!( + handles.config.output_level, + OutputLevel::Default | OutputLevel::Quiet + ) { ferox_print( &format!("Could not connect to {}, skipping...", target_url), &PROGRESS_PRINTER, diff --git a/src/main.rs b/src/main.rs index 16dae19..e8ea449 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use tokio_util::codec::{FramedRead, LinesCodec}; use feroxbuster::{ banner::{Banner, UPDATE_URL}, - config::Configuration, + config::{Configuration, OutputLevel}, event_handlers::{ Command::{CreateBar, Exit, JoinTasks, LoadStats, ScanInitialUrls, UpdateWordlist}, FiltersHandler, Handles, ScanHandler, StatsHandler, Tasks, TermInputHandler, @@ -89,8 +89,8 @@ async fn scan(targets: Vec, handles: Arc) -> Result<()> { // - scanner initialized (this sent expected requests per directory to the stats thread, which // having been set, makes it so the progress bar doesn't flash as full before anything has // even happened - if !handles.config.silent { - // only create the bar if -q hasn't been used + if matches!(handles.config.output_level, OutputLevel::Default) { + // only create the bar if no --silent|--quiet handles.stats.send(CreateBar)?; // blocks until the bar is created / avoids race condition in first two bars @@ -228,8 +228,8 @@ async fn wrapped_main(config: Arc) -> Result<()> { } }; - if !config.silent { - // only print banner if -q isn't used + if matches!(config.output_level, OutputLevel::Default) { + // only print banner if output level is default (no banner on --quiet|--silent) let std_stderr = stderr(); // std::io::stderr let mut banner = Banner::new(&targets, &config); diff --git a/src/response.rs b/src/response.rs index 56be472..d8ea544 100644 --- a/src/response.rs +++ b/src/response.rs @@ -16,6 +16,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; use crate::{ + config::OutputLevel, event_handlers::{Command, Handles}, traits::FeroxSerialize, url::FeroxUrl, @@ -50,8 +51,8 @@ pub struct FeroxResponse { /// Wildcard response status wildcard: bool, - /// whether the user passed -q on the command line - pub(crate) quiet: bool, + /// whether the user passed --quiet|--silent on the command line + pub(crate) output_level: OutputLevel, } /// implement Default trait for FeroxResponse @@ -67,7 +68,7 @@ impl Default for FeroxResponse { word_count: 0, headers: Default::default(), wildcard: false, - quiet: false, + output_level: Default::default(), } } } @@ -185,7 +186,7 @@ impl FeroxResponse { } /// Create a new `FeroxResponse` from the given `Response` - pub async fn from(response: Response, read_body: bool, quiet: bool) -> Self { + pub async fn from(response: Response, read_body: bool, output_level: OutputLevel) -> Self { let url = response.url().clone(); let status = response.status(); let headers = response.headers().clone(); @@ -218,7 +219,7 @@ impl FeroxResponse { headers, line_count, word_count, - quiet, + output_level, wildcard: false, } } @@ -327,8 +328,8 @@ impl FeroxSerialize for FeroxResponse { let status = self.status().as_str(); let wild_status = status_colorizer("WLD"); - if self.wildcard && !self.quiet { - // -q was not used and response is a wildcard, special messages abound when + if self.wildcard && matches!(self.output_level, OutputLevel::Default | OutputLevel::Quiet) { + // --silent was not used and response is a wildcard, special messages abound when // this is the case... // create the base message @@ -372,7 +373,7 @@ impl FeroxSerialize for FeroxResponse { &words, &chars, self.url().as_str(), - self.quiet, + self.output_level, ) } } @@ -459,7 +460,7 @@ impl<'de> Deserialize<'de> for FeroxResponse { content_length: 0, headers: HeaderMap::new(), wildcard: false, - quiet: false, + output_level: Default::default(), line_count: 0, word_count: 0, }; @@ -546,7 +547,7 @@ mod tests { word_count: 0, headers: Default::default(), wildcard: false, - quiet: false, + output_level: Default::default(), }; let result = response.reached_max_depth(0, 0, handles); assert!(!result); @@ -567,7 +568,7 @@ mod tests { word_count: 0, headers: Default::default(), wildcard: false, - quiet: false, + output_level: Default::default(), }; let result = response.reached_max_depth(0, 2, handles); @@ -588,7 +589,7 @@ mod tests { word_count: 0, headers: Default::default(), wildcard: false, - quiet: false, + output_level: Default::default(), }; let result = response.reached_max_depth(0, 2, handles); @@ -609,7 +610,7 @@ mod tests { word_count: 0, headers: Default::default(), wildcard: false, - quiet: false, + output_level: Default::default(), }; let result = response.reached_max_depth(2, 2, handles); @@ -630,7 +631,7 @@ mod tests { word_count: 0, headers: Default::default(), wildcard: false, - quiet: false, + output_level: Default::default(), }; let result = response.reached_max_depth(0, 2, handles); diff --git a/src/utils.rs b/src/utils.rs index 3c00130..f8c6f01 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -11,6 +11,7 @@ use std::{ use tokio::sync::mpsc::UnboundedSender; use crate::{ + config::OutputLevel, event_handlers::Command::{self, AddError, AddStatus}, progress::PROGRESS_PRINTER, send_command, @@ -160,10 +161,10 @@ pub fn create_report_string( word_count: &str, content_length: &str, url: &str, - quiet: bool, + output_level: OutputLevel, ) -> String { - if quiet { - // -q used, just need the url + if matches!(output_level, OutputLevel::Silent) { + // --silent used, just need the url format!("{}\n", url) } else { // normal printing with status and sizes diff --git a/tests/test_scan_manager.rs b/tests/test_scan_manager.rs index 85f9955..f6972ba 100644 --- a/tests/test_scan_manager.rs +++ b/tests/test_scan_manager.rs @@ -30,7 +30,7 @@ fn resume_scan_works() { let scans = format!(r#""scans":[{},{}]"#, complete_scan, incomplete_scan); let config = format!( - r#""config": {{"type":"configuration","wordlist":"{}","config":"","proxy":"","replay_proxy":"","target_url":"{}","status_codes":[200,204,301,302,307,308,401,403,405],"replay_codes":[200,204,301,302,307,308,401,403,405],"filter_status":[],"threads":50,"timeout":7,"verbosity":0,"quiet":false,"json":false,"output":"","debug_log":"","user_agent":"feroxbuster/1.9.0","redirects":false,"insecure":false,"extensions":[],"headers":{{}},"queries":[],"no_recursion":false,"extract_links":false,"add_slash":false,"stdin":false,"depth":2,"scan_limit":1,"filter_size":[],"filter_line_count":[],"filter_word_count":[],"filter_regex":[],"dont_filter":false}}"#, + r#""config": {{"type":"configuration","wordlist":"{}","config":"","proxy":"","replay_proxy":"","target_url":"{}","status_codes":[200,204,301,302,307,308,401,403,405],"replay_codes":[200,204,301,302,307,308,401,403,405],"filter_status":[],"threads":50,"timeout":7,"verbosity":0,"silent":false,"quiet":false,"json":false,"output":"","debug_log":"","user_agent":"feroxbuster/1.9.0","redirects":false,"insecure":false,"extensions":[],"headers":{{}},"queries":[],"no_recursion":false,"extract_links":false,"add_slash":false,"stdin":false,"depth":2,"scan_limit":1,"filter_size":[],"filter_line_count":[],"filter_word_count":[],"filter_regex":[],"dont_filter":false}}"#, file.to_string_lossy(), srv.url("/") );