From 339189ff13af65e28cf784aab3bea0c43109bd67 Mon Sep 17 00:00:00 2001 From: epi Date: Mon, 27 Feb 2023 07:26:59 -0600 Subject: [PATCH] resume scan starts from offset in wordlist --- src/event_handlers/scans.rs | 17 +++++++++++++---- src/scan_manager/scan.rs | 9 +++++++++ src/scan_manager/tests.rs | 26 ++++++++++++++++++++------ tests/test_scan_manager.rs | 7 +++++-- 4 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/event_handlers/scans.rs b/src/event_handlers/scans.rs index 2659a55..8329c20 100644 --- a/src/event_handlers/scans.rs +++ b/src/event_handlers/scans.rs @@ -218,7 +218,7 @@ impl ScanHandler { // current number of requests expected per scan // ExpectedPerScan and TotalExpected are a += action, so we need the wordlist length to // update them while the other updates use expected_num_requests_per_dir - let num_words = self.get_wordlist()?.len(); + let num_words = self.get_wordlist(0)?.len(); let current_expectation = self.handles.expected_num_requests_per_dir() as u64; // used in the calculation of bar width down below, see explanation there @@ -290,10 +290,19 @@ impl ScanHandler { } /// Helper to easily get the (locked) underlying wordlist - pub fn get_wordlist(&self) -> Result>> { + pub fn get_wordlist(&self, offset: usize) -> Result>> { if let Ok(guard) = self.wordlist.lock().as_ref() { if let Some(list) = guard.as_ref() { - return Ok(list.clone()); + return if offset > 0 { + // the offset could be off a bit, so we'll adjust it backwards by 10% + // of the overall wordlist size to ensure we don't miss any words + // (hopefully) + let adjusted_offset = offset - ((offset as f64 * 0.10) as usize); + + Ok(Arc::new(list[adjusted_offset..].to_vec())) + } else { + Ok(list.clone()) + }; } } @@ -328,7 +337,7 @@ impl ScanHandler { continue; } - let list = self.get_wordlist()?; + let list = self.get_wordlist(scan.requests() as usize)?; log::info!("scan handler received {} - beginning scan", target); diff --git a/src/scan_manager/scan.rs b/src/scan_manager/scan.rs index 4dd74a2..766a8ca 100644 --- a/src/scan_manager/scan.rs +++ b/src/scan_manager/scan.rs @@ -354,6 +354,7 @@ impl Serialize for FeroxScan { state.serialize_field("scan_type", &self.scan_type)?; state.serialize_field("status", &self.status)?; state.serialize_field("num_requests", &self.num_requests)?; + state.serialize_field("requests_made_so_far", &self.requests())?; state.end() } @@ -367,6 +368,7 @@ impl<'de> Deserialize<'de> for FeroxScan { D: Deserializer<'de>, { let mut scan = Self::default(); + let mut progress_bar_position = 0; let map: HashMap = HashMap::deserialize(deserializer)?; @@ -412,10 +414,17 @@ impl<'de> Deserialize<'de> for FeroxScan { scan.num_requests = num_requests; } } + "requests_made_so_far" => { + if let Some(requests_made_so_far) = value.as_u64() { + progress_bar_position = requests_made_so_far; + } + } _ => {} } } + scan.progress_bar().set_position(progress_bar_position); + Ok(scan) } } diff --git a/src/scan_manager/tests.rs b/src/scan_manager/tests.rs index 52e9b32..3343342 100644 --- a/src/scan_manager/tests.rs +++ b/src/scan_manager/tests.rs @@ -224,7 +224,7 @@ fn ferox_scan_get_progress_bar_when_none_is_set() { /// given a JSON entry representing a FeroxScan, test that it deserializes into the proper type /// with the right attributes fn ferox_scan_deserialize() { - let fs_json = r#"{"id":"057016a14769414aac9a7a62707598cb","url":"https://spiritanimal.com","scan_type":"Directory","status":"Complete"}"#; + let fs_json = r#"{"id":"057016a14769414aac9a7a62707598cb","url":"https://spiritanimal.com","scan_type":"Directory","status":"Complete","requests_made_so_far":500}"#; let fs_json_two = r#"{"id":"057016a14769414aac9a7a62707598cb","url":"https://spiritanimal.com","scan_type":"Not Correct","status":"Cancelled"}"#; let fs_json_three = r#"{"id":"057016a14769414aac9a7a62707598cb","url":"https://spiritanimal.com","scan_type":"Not Correct","status":"","num_requests":42}"#; @@ -246,9 +246,23 @@ fn ferox_scan_deserialize() { ScanType::File => {} } - match *fs.progress_bar.lock().unwrap() { - None => {} - Some(_) => { + match fs.progress_bar.lock() { + Ok(guard) => { + match guard.as_ref() { + Some(pb) => { + // position based on the requests made so far + // + // note: when this goes through the actual deserialize function, the + // progress bar will be set to the total requests made so far, -10% + // (i.e. 450 in this case) + assert_eq!(pb.position(), 500); + } + None => { + panic!(); + } + } + } + Err(_) => { panic!(); } } @@ -277,7 +291,7 @@ fn ferox_scan_serialize() { None, ); let fs_json = format!( - r#"{{"id":"{}","url":"https://spiritanimal.com","normalized_url":"https://spiritanimal.com/","scan_type":"Directory","status":"NotStarted","num_requests":0}}"#, + r#"{{"id":"{}","url":"https://spiritanimal.com","normalized_url":"https://spiritanimal.com/","scan_type":"Directory","status":"NotStarted","num_requests":0,"requests_made_so_far":0}}"#, fs.id ); assert_eq!(fs_json, serde_json::to_string(&*fs).unwrap()); @@ -296,7 +310,7 @@ fn ferox_scans_serialize() { ); let ferox_scans = FeroxScans::default(); let ferox_scans_json = format!( - r#"[{{"id":"{}","url":"https://spiritanimal.com","normalized_url":"https://spiritanimal.com/","scan_type":"Directory","status":"NotStarted","num_requests":0}}]"#, + r#"[{{"id":"{}","url":"https://spiritanimal.com","normalized_url":"https://spiritanimal.com/","scan_type":"Directory","status":"NotStarted","num_requests":0,"requests_made_so_far":0}}]"#, ferox_scan.id ); ferox_scans.scans.write().unwrap().push(ferox_scan); diff --git a/tests/test_scan_manager.rs b/tests/test_scan_manager.rs index b59c7e7..418a64c 100644 --- a/tests/test_scan_manager.rs +++ b/tests/test_scan_manager.rs @@ -20,12 +20,12 @@ fn resume_scan_works() { // localhost:PORT/ <- complete // localhost:PORT/js <- will get scanned with /css and /stuff let complete_scan = format!( - r#"{{"id":"057016a14769414aac9a7a62707598cb","url":"{}","normalized_url":"{}","scan_type":"Directory","status":"Complete"}}"#, + r#"{{"id":"057016a14769414aac9a7a62707598cb","url":"{}","normalized_url":"{}","scan_type":"Directory","status":"Complete","num_requests":4174,"requests_made_so_far":0}}"#, srv.url("/"), srv.url("/"), ); let incomplete_scan = format!( - r#"{{"id":"400b2323a16f43468a04ffcbbeba34c6","url":"{}","normalized_url":"{}/","scan_type":"Directory","status":"NotStarted"}}"#, + r#"{{"id":"400b2323a16f43468a04ffcbbeba34c6","url":"{}","normalized_url":"{}/","scan_type":"Directory","status":"NotStarted","num_requests":4174,"requests_made_so_far":0}}"#, srv.url("/js"), srv.url("/js") ); @@ -64,10 +64,13 @@ fn resume_scan_works() { }); let state_file_contents = format!("{{{scans},{config},{responses}}}"); + let (tmp_dir2, state_file) = setup_tmp_directory(&[state_file_contents], "state-file").unwrap(); Command::cargo_bin("feroxbuster") .unwrap() + .arg("-vvv") + .arg("--burp") .arg("--resume-from") .arg(state_file.as_os_str()) .assert()