fixed up implementation/removed todo items

This commit is contained in:
epi
2022-02-16 20:44:07 -06:00
parent d13bce2261
commit 368035833c
2 changed files with 98 additions and 71 deletions

View File

@@ -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
}
}

View File

@@ -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);
}