mirror of
https://github.com/epi052/feroxbuster.git
synced 2026-06-06 08:51:12 -03:00
fixed up implementation/removed todo items
This commit is contained in:
@@ -5,6 +5,7 @@ use anyhow::{Context, Result};
|
||||
use futures::future::{BoxFuture, FutureExt};
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
||||
use crate::statistics::StatField::TotalExpected;
|
||||
use crate::{
|
||||
config::Configuration,
|
||||
progress::PROGRESS_PRINTER,
|
||||
@@ -19,6 +20,16 @@ use crate::{
|
||||
use std::sync::Arc;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug, Copy)]
|
||||
/// Simple enum for semantic clarity around calling expectations for `process_response`
|
||||
enum ProcessResponseCall {
|
||||
/// call should allow recursion
|
||||
Recursive,
|
||||
|
||||
/// call should not allow recursion
|
||||
NotRecursive,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Container for terminal output transmitter
|
||||
pub struct TermOutHandle {
|
||||
@@ -189,8 +200,8 @@ impl TermOutHandler {
|
||||
while let Some(command) = self.receiver.recv().await {
|
||||
match command {
|
||||
Command::Report(resp) => {
|
||||
// todo add enum to replace bool
|
||||
self.process_response(tx_stats.clone(), resp, false).await?;
|
||||
self.process_response(tx_stats.clone(), resp, ProcessResponseCall::Recursive)
|
||||
.await?;
|
||||
}
|
||||
Command::Sync(sender) => {
|
||||
sender.send(true).unwrap_or_default();
|
||||
@@ -208,13 +219,16 @@ impl TermOutHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// todo
|
||||
/// upon receiving a `FeroxResponse` from the mpsc, handle printing, sending to the replay
|
||||
/// proxy, checking for backups of the `FeroxResponse`'s url, and tracking the response.
|
||||
fn process_response(
|
||||
&self,
|
||||
tx_stats: CommandSender,
|
||||
mut resp: Box<FeroxResponse>,
|
||||
recursive_call: bool,
|
||||
call_type: ProcessResponseCall,
|
||||
) -> BoxFuture<'_, Result<()>> {
|
||||
log::trace!("enter: generate_backup_urls({:?})", response);
|
||||
|
||||
async move {
|
||||
let contains_sentry = self.config.status_codes.contains(&resp.status().as_u16());
|
||||
let unknown_sentry = !RESPONSES.contains(&resp); // !contains == unknown
|
||||
@@ -254,8 +268,12 @@ impl TermOutHandler {
|
||||
}
|
||||
|
||||
// todo update if statement to include --collect-backups
|
||||
if should_process_response && !recursive_call {
|
||||
if should_process_response && matches!(call_type, ProcessResponseCall::Recursive) {
|
||||
let backup_urls = self.generate_backup_urls(&resp).await;
|
||||
|
||||
// need to manually adjust stats
|
||||
send_command!(tx_stats, AddToUsizeField(TotalExpected, backup_urls.len()));
|
||||
|
||||
for backup_url in &backup_urls {
|
||||
let backup_response = make_request(
|
||||
&self.config.client,
|
||||
@@ -270,15 +288,21 @@ impl TermOutHandler {
|
||||
.with_context(|| {
|
||||
format!("Could not request backup of {}", resp.url().as_str())
|
||||
})?;
|
||||
let mut ferox_response = FeroxResponse::from(
|
||||
|
||||
let ferox_response = FeroxResponse::from(
|
||||
backup_response,
|
||||
resp.url().as_str(),
|
||||
resp.method().as_str(),
|
||||
resp.output_level,
|
||||
)
|
||||
.await;
|
||||
self.process_response(tx_stats.clone(), Box::new(ferox_response), true)
|
||||
.await?;
|
||||
|
||||
self.process_response(
|
||||
tx_stats.clone(),
|
||||
Box::new(ferox_response),
|
||||
ProcessResponseCall::NotRecursive,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,9 +330,22 @@ impl TermOutHandler {
|
||||
urls.push(new_url);
|
||||
}
|
||||
|
||||
/// todo
|
||||
/// given a `FeroxResponse`, generate either 6 or 7 urls that are likely backups of the
|
||||
/// original.
|
||||
///
|
||||
/// example:
|
||||
/// original: LICENSE.txt
|
||||
/// backups:
|
||||
/// - LICENSE.txt~
|
||||
/// - LICENSE.txt.bak
|
||||
/// - LICENSE.txt.bak2
|
||||
/// - LICENSE.txt.old
|
||||
/// - LICENSE.txt.1
|
||||
/// - LICENSE.bak
|
||||
/// - .LICENSE.txt.swp
|
||||
async fn generate_backup_urls(&self, response: &FeroxResponse) -> Vec<Url> {
|
||||
// todo
|
||||
log::trace!("enter: generate_backup_urls({:?})", response);
|
||||
|
||||
let mut urls = vec![];
|
||||
let url = response.url();
|
||||
|
||||
@@ -337,7 +374,7 @@ impl TermOutHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// todo
|
||||
log::trace!("exit: generate_backup_urls -> {:?}", urls);
|
||||
urls
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,51 +687,45 @@ fn collect_backups_makes_appropriate_requests() {
|
||||
let srv = MockServer::start();
|
||||
let (tmp_dir, file) = setup_tmp_directory(&["LICENSE.txt".to_string()], "wordlist").unwrap();
|
||||
|
||||
let mock = srv.mock(|when, then| {
|
||||
when.method(GET).path("/LICENSE.txt");
|
||||
then.status(200).body("this is a test");
|
||||
});
|
||||
let valid_paths = vec![
|
||||
"/LICENSE.txt",
|
||||
"/LICENSE.txt~",
|
||||
"/LICENSE.txt.bak",
|
||||
"/LICENSE.txt.bak2",
|
||||
"/LICENSE.txt.old",
|
||||
"/LICENSE.txt.1",
|
||||
"/LICENSE.bak",
|
||||
"/.LICENSE.txt.swp",
|
||||
];
|
||||
|
||||
let tilde_backup = srv.mock(|when, then| {
|
||||
when.method(GET).path("/LICENSE.txt~");
|
||||
then.status(200);
|
||||
});
|
||||
let valid_mocks: Vec<_> = valid_paths
|
||||
.iter()
|
||||
.map(|&p| {
|
||||
srv.mock(|when, then| {
|
||||
when.method(GET).path(p);
|
||||
then.status(200).body("this is a valid test");
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
let bak_backup = srv.mock(|when, then| {
|
||||
when.method(GET).path("/LICENSE.txt.bak");
|
||||
then.status(200);
|
||||
});
|
||||
let invalid_paths: Vec<_> = vec![
|
||||
"/LICENSE.txt~~",
|
||||
"/LICENSE.txt.bak.bak",
|
||||
"/LICENSE.txt.bak2.bak2",
|
||||
"/LICENSE.txt.old.old",
|
||||
"/LICENSE.txt.1.1",
|
||||
"/..LICENSE.txt.swp.swp",
|
||||
];
|
||||
|
||||
let bak2_backup = srv.mock(|when, then| {
|
||||
when.method(GET).path("/LICENSE.txt.bak2");
|
||||
then.status(200);
|
||||
});
|
||||
|
||||
let old_backup = srv.mock(|when, then| {
|
||||
when.method(GET).path("/LICENSE.txt.old");
|
||||
then.status(200);
|
||||
});
|
||||
|
||||
let dot1_backup = srv.mock(|when, then| {
|
||||
when.method(GET).path("/LICENSE.txt.1");
|
||||
then.status(200);
|
||||
});
|
||||
|
||||
let replaced_bak_backup = srv.mock(|when, then| {
|
||||
when.method(GET).path("/LICENSE.bak");
|
||||
then.status(200);
|
||||
});
|
||||
|
||||
let vim_swap_backup = srv.mock(|when, then| {
|
||||
when.method(GET).path("/.LICENSE.txt.swp");
|
||||
then.status(200);
|
||||
});
|
||||
|
||||
// todo add double backup style tests for all variants
|
||||
let tilde_double_backup = srv.mock(|when, then| {
|
||||
when.method(GET).path("/LICENSE.txt~~");
|
||||
then.status(404);
|
||||
});
|
||||
let invalid_mocks: Vec<_> = invalid_paths
|
||||
.iter()
|
||||
.map(|&p| {
|
||||
srv.mock(|when, then| {
|
||||
when.method(GET).path(p);
|
||||
then.status(200).body("this is an invalid test");
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
// todo add --collect-backups flag when available
|
||||
let cmd = Command::cargo_bin("feroxbuster")
|
||||
@@ -742,28 +736,24 @@ fn collect_backups_makes_appropriate_requests() {
|
||||
.arg(file.as_os_str())
|
||||
.unwrap();
|
||||
|
||||
// todo maybe add in some stdout checks
|
||||
cmd.assert().success().stdout(
|
||||
predicate::str::contains("/LICENSE.txt").and(predicate::str::contains("/LICENSE.txt~")),
|
||||
predicate::str::contains("/LICENSE.txt")
|
||||
.and(predicate::str::contains("/LICENSE.txt~"))
|
||||
.and(predicate::str::contains("/LICENSE.txt.bak"))
|
||||
.and(predicate::str::contains("/LICENSE.txt.bak2"))
|
||||
.and(predicate::str::contains("/LICENSE.txt.old"))
|
||||
.and(predicate::str::contains("/LICENSE.txt.1"))
|
||||
.and(predicate::str::contains("/LICENSE.bak"))
|
||||
.and(predicate::str::contains("/.LICENSE.txt.swp")),
|
||||
);
|
||||
// .and(predicate::str::contains("403"))
|
||||
// .and(predicate::str::contains("53c"))
|
||||
// .and(predicate::str::contains("14c"))
|
||||
// .and(predicate::str::contains("0c"))
|
||||
// .and(predicate::str::contains("ignored").count(2))
|
||||
// .and(predicate::str::contains("/ignored/LICENSE")),
|
||||
// );
|
||||
|
||||
assert_eq!(mock.hits(), 1);
|
||||
assert_eq!(tilde_backup.hits(), 1);
|
||||
assert_eq!(tilde_double_backup.hits(), 0); // shouldn't request backups of backups
|
||||
for valid_mock in valid_mocks {
|
||||
assert_eq!(valid_mock.hits(), 1);
|
||||
}
|
||||
|
||||
assert_eq!(bak_backup.hits(), 1);
|
||||
assert_eq!(bak2_backup.hits(), 1);
|
||||
assert_eq!(old_backup.hits(), 1);
|
||||
assert_eq!(dot1_backup.hits(), 1);
|
||||
assert_eq!(replaced_bak_backup.hits(), 1);
|
||||
assert_eq!(vim_swap_backup.hits(), 1);
|
||||
for invalid_mock in invalid_mocks {
|
||||
assert_eq!(invalid_mock.hits(), 0);
|
||||
}
|
||||
|
||||
teardown_tmp_directory(tmp_dir);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user