feroxbuster/tests/test_main.rs

266 lines
8.2 KiB
Rust
Raw Normal View History

2020-10-04 12:04:46 +00:00
mod utils;
2020-10-04 02:09:37 +00:00
use assert_cmd::Command;
2020-10-04 11:14:25 +00:00
use httpmock::Method::GET;
2021-02-18 17:01:11 +00:00
use httpmock::{MockServer, Regex};
2020-10-04 02:09:37 +00:00
use predicates::prelude::*;
2021-08-01 19:32:14 +00:00
use std::fs::{read_dir, read_to_string};
2020-10-04 12:04:46 +00:00
use utils::{setup_tmp_directory, teardown_tmp_directory};
2020-10-04 02:09:37 +00:00
#[test]
2020-10-04 11:14:25 +00:00
/// send the function a file to which we dont have permission in order to execute error branch
fn main_use_root_owned_file_as_wordlist() {
2020-10-04 02:09:37 +00:00
let srv = MockServer::start();
let mock = srv.mock(|when, then| {
when.method(GET).path("/");
then.status(200).body("this is a test");
});
2020-10-04 02:09:37 +00:00
Command::cargo_bin("feroxbuster")
.unwrap()
.arg("--url")
.arg(srv.url("/"))
.arg("--wordlist")
.arg("/etc/shadow")
.arg("-vvvv")
.assert()
.success()
2022-02-14 12:29:25 +00:00
.stderr(predicate::str::contains("Could not open /etc/shadow"));
2020-10-04 02:09:37 +00:00
2022-02-14 12:29:25 +00:00
assert_eq!(mock.hits(), 0);
2020-10-04 11:14:25 +00:00
}
2020-10-04 12:04:46 +00:00
#[test]
/// send the function an empty file
fn main_use_empty_wordlist() -> Result<(), Box<dyn std::error::Error>> {
let srv = MockServer::start();
let (tmp_dir, file) = setup_tmp_directory(&[], "wordlist")?;
2020-10-04 12:04:46 +00:00
let mock = srv.mock(|when, then| {
when.method(GET).path("/");
then.status(200).body("this is a test");
});
2020-10-04 12:04:46 +00:00
Command::cargo_bin("feroxbuster")
.unwrap()
.arg("--url")
.arg(srv.url("/"))
.arg("--wordlist")
.arg(file.as_os_str())
.arg("-vvvv")
.assert()
.success()
2022-02-14 12:29:25 +00:00
.stderr(predicate::str::contains("Did not find any words in"));
2020-10-04 12:04:46 +00:00
2022-02-14 12:29:25 +00:00
assert_eq!(mock.hits(), 0);
2020-10-04 12:04:46 +00:00
teardown_tmp_directory(tmp_dir);
Ok(())
}
#[test]
/// send nothing over stdin, expect heuristics to be upset during connectivity test
fn main_use_empty_stdin_targets() -> Result<(), Box<dyn std::error::Error>> {
let (tmp_dir, file) = setup_tmp_directory(&[], "wordlist")?;
2020-10-04 12:04:46 +00:00
// get_targets is called before scan, so the empty wordlist shouldn't trigger
// the 'Did not find any words' error
Command::cargo_bin("feroxbuster")
.unwrap()
.arg("--stdin")
.arg("--wordlist")
.arg(file.as_os_str())
.arg("-vvv")
.pipe_stdin(file)
.unwrap()
.assert()
.success()
2020-10-04 12:04:46 +00:00
.stderr(
predicate::str::contains("Could not connect to any target provided")
.and(predicate::str::contains("Target Url"))
.not(), // no target url found
);
teardown_tmp_directory(tmp_dir);
Ok(())
}
2021-02-08 12:44:00 +00:00
#[test]
2021-02-18 17:01:11 +00:00
/// send three targets over stdin, expect parallel to spawn children and each child config to show
/// up in the output file
2021-02-08 12:44:00 +00:00
fn main_parallel_spawns_children() -> Result<(), Box<dyn std::error::Error>> {
let t1 = MockServer::start();
let t2 = MockServer::start();
let t3 = MockServer::start();
let words = [
String::from("LICENSE"),
String::from("stuff"),
String::from("things"),
String::from("mostuff"),
String::from("mothings"),
];
let (word_tmp_dir, wordlist) = setup_tmp_directory(&words, "wordlist")?;
2021-02-18 17:01:11 +00:00
let (output_dir, outfile) = setup_tmp_directory(&[], "output-file")?;
2021-02-08 12:44:00 +00:00
let (tgt_tmp_dir, targets) =
setup_tmp_directory(&[t1.url("/"), t2.url("/"), t3.url("/")], "targets")?;
Command::cargo_bin("feroxbuster")
.unwrap()
.env("RUST_LOG", "trace")
2021-02-08 12:44:00 +00:00
.arg("--stdin")
.arg("--parallel")
.arg("2")
.arg("--quiet")
2021-02-18 17:01:11 +00:00
.arg("--debug-log")
.arg(outfile.as_os_str())
2021-02-08 12:44:00 +00:00
.arg("--wordlist")
.arg(wordlist.as_os_str())
.pipe_stdin(targets)
.unwrap()
.assert()
.success()
.stderr(
predicate::str::contains("Could not connect to any target provided")
.and(predicate::str::contains("Target Url"))
.not(), // no target url found
);
2021-02-18 17:01:11 +00:00
let contents = read_to_string(outfile).unwrap();
2023-02-16 01:39:27 +00:00
println!("contents: {contents}");
2021-02-18 17:01:11 +00:00
assert!(contents.contains("parallel branch && wrapped main")); // exits parallel branch
// DBG 0.007 feroxbuster parallel exec: target/debug/feroxbuster
// --debug-log /tmp/.tmpAjRts6/output-file --wordlist /tmp/.tmpS4CKKq/wordlist
// --silent -u http://127.0.0.1:41979/
let r1 = Regex::new(&format!("parallel exec:.*-u {}", t1.url("/"))).unwrap();
let r2 = Regex::new(&format!("parallel exec:.*-u {}", t2.url("/"))).unwrap();
let r3 = Regex::new(&format!("parallel exec:.*-u {}", t3.url("/"))).unwrap();
assert!(r1.is_match(&contents)); // all 3 were spawned
assert!(r2.is_match(&contents));
assert!(r3.is_match(&contents));
2021-02-08 12:44:00 +00:00
teardown_tmp_directory(word_tmp_dir);
teardown_tmp_directory(tgt_tmp_dir);
2021-02-18 17:01:11 +00:00
teardown_tmp_directory(output_dir);
2021-02-08 12:44:00 +00:00
Ok(())
}
2021-08-01 19:32:14 +00:00
#[test]
/// send three targets over stdin with --output enabled, expect parallel to create a new directory
/// and the log files therein
fn main_parallel_creates_output_directory() -> Result<(), Box<dyn std::error::Error>> {
let t1 = MockServer::start();
let t2 = MockServer::start();
let t3 = MockServer::start();
let words = [
String::from("LICENSE"),
String::from("stuff"),
String::from("things"),
String::from("mostuff"),
String::from("mothings"),
];
let (word_tmp_dir, wordlist) = setup_tmp_directory(&words, "wordlist")?;
let (output_dir, outfile) = setup_tmp_directory(&[], "output-file")?;
let (tgt_tmp_dir, targets) =
setup_tmp_directory(&[t1.url("/"), t2.url("/"), t3.url("/")], "targets")?;
Command::cargo_bin("feroxbuster")
.unwrap()
.arg("--stdin")
.arg("--quiet")
2021-08-01 19:32:14 +00:00
.arg("--parallel")
.arg("2")
.arg("--output")
.arg(outfile.as_os_str())
.arg("--wordlist")
.arg(wordlist.as_os_str())
.pipe_stdin(targets)
.unwrap()
.assert()
.success()
.stderr(
predicate::str::contains("Could not connect to any target provided")
.and(predicate::str::contains("Target Url"))
.not(), // no target url found
);
// output_dir should return something similar to output-file-1627845244.logs with the
// line below. if it ever fails, can use the regex below to filter out the right directory
let sub_dir = read_dir(&output_dir)?.next().unwrap()?.file_name();
let mut num_logs = 0;
let file_regex = Regex::new("ferox-[a-zA-Z_:0-9]+-[0-9]+.log").unwrap();
let dir_regex = Regex::new("output-file-[0-9]+.logs").unwrap();
2022-12-29 21:32:22 +00:00
let sub_dir = output_dir.as_ref().join(sub_dir);
2021-08-01 19:32:14 +00:00
// created directory like output-file-1627845741.logs/
2022-09-18 10:51:13 +00:00
assert!(dir_regex.is_match(&sub_dir.to_string_lossy()));
2021-08-01 19:32:14 +00:00
for entry in sub_dir.read_dir()? {
let entry = entry?;
// created each file like ferox-https_localhost-1627845741.log
println!("name: {:?}", entry.file_name().to_string_lossy());
assert!(file_regex.is_match(&entry.file_name().to_string_lossy()));
num_logs += 1;
}
// should be 3 log files total
assert_eq!(num_logs, 3);
teardown_tmp_directory(word_tmp_dir);
teardown_tmp_directory(tgt_tmp_dir);
teardown_tmp_directory(output_dir);
Ok(())
}
2023-03-18 16:44:45 +00:00
#[test]
/// download a wordlist from a url
fn main_download_wordlist_from_url() -> Result<(), Box<dyn std::error::Error>> {
let srv = MockServer::start();
let (tmp_dir, _) = setup_tmp_directory(&["a".to_string()], "wordlist")?;
let mock1 = srv.mock(|when, then| {
when.method(GET).path("/derp");
then.status(200).body("stuff\nthings");
});
// serve endpoints stuff and things
let mock2 = srv.mock(|when, then| {
when.method(GET).path("/stuff");
then.status(200);
});
let mock3 = srv.mock(|when, then| {
when.method(GET).path("/things");
then.status(200);
});
Command::cargo_bin("feroxbuster")
.unwrap()
.current_dir(&tmp_dir)
.arg("--url")
.arg(srv.url("/"))
.arg("--wordlist")
.arg(srv.url("/derp"))
.assert()
.success()
.stderr(predicate::str::contains(srv.url("/derp")));
teardown_tmp_directory(tmp_dir);
assert_eq!(mock1.hits(), 1); // downloaded wordlist
assert_eq!(mock2.hits(), 1); // found stuff from wordlist
assert_eq!(mock3.hits(), 1); // found things from wordlist
Ok(())
}