2021-06-18 01:34:21 +00:00
|
|
|
mod utils;
|
|
|
|
use assert_cmd::prelude::*;
|
|
|
|
use assert_cmd::Command;
|
|
|
|
use httpmock::Method::GET;
|
|
|
|
use httpmock::MockServer;
|
|
|
|
use predicates::prelude::*;
|
|
|
|
use utils::{setup_tmp_directory, teardown_tmp_directory};
|
|
|
|
|
|
|
|
#[test]
|
2021-06-18 11:48:41 +00:00
|
|
|
/// test that the deny list prevents a request if the requested url is a match
|
2021-06-18 01:34:21 +00:00
|
|
|
fn deny_list_works_during_with_a_normal_scan() {
|
|
|
|
let srv = MockServer::start();
|
|
|
|
let (tmp_dir, file) = setup_tmp_directory(&["LICENSE".to_string()], "wordlist").unwrap();
|
|
|
|
|
|
|
|
let mock = srv.mock(|when, then| {
|
|
|
|
when.method(GET).path("/LICENSE");
|
|
|
|
then.status(200).body("this is a test");
|
|
|
|
});
|
|
|
|
|
|
|
|
let cmd = Command::cargo_bin("feroxbuster")
|
|
|
|
.unwrap()
|
|
|
|
.arg("--url")
|
|
|
|
.arg(srv.url("/"))
|
|
|
|
.arg("--wordlist")
|
|
|
|
.arg(file.as_os_str())
|
|
|
|
.arg("--dont-scan")
|
|
|
|
.arg(srv.url("/LICENSE"))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
teardown_tmp_directory(tmp_dir);
|
|
|
|
|
|
|
|
cmd.assert()
|
|
|
|
.success()
|
|
|
|
.stdout(predicate::str::contains(srv.url("/LICENSE")).not());
|
|
|
|
|
|
|
|
assert_eq!(mock.hits(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-06-18 11:48:41 +00:00
|
|
|
/// test that the deny list prevents requests of urls found during extraction
|
2021-06-18 01:34:21 +00:00
|
|
|
fn deny_list_works_during_extraction() {
|
|
|
|
let srv = MockServer::start();
|
|
|
|
let (tmp_dir, file) = setup_tmp_directory(&["LICENSE".to_string()], "wordlist").unwrap();
|
|
|
|
|
|
|
|
let mock = srv.mock(|when, then| {
|
|
|
|
when.method(GET).path("/LICENSE");
|
|
|
|
then.status(200)
|
2022-12-29 21:32:22 +00:00
|
|
|
.body(srv.url("'/homepage/assets/img/icons/handshake.svg'"));
|
2021-06-18 01:34:21 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
let mock_two = srv.mock(|when, then| {
|
|
|
|
when.method(GET)
|
|
|
|
.path("/homepage/assets/img/icons/handshake.svg");
|
|
|
|
then.status(200);
|
|
|
|
});
|
|
|
|
|
|
|
|
let cmd = Command::cargo_bin("feroxbuster")
|
|
|
|
.unwrap()
|
|
|
|
.arg("--url")
|
|
|
|
.arg(srv.url("/"))
|
|
|
|
.arg("--wordlist")
|
|
|
|
.arg(file.as_os_str())
|
|
|
|
.arg("--extract-links")
|
|
|
|
.arg("--dont-scan")
|
|
|
|
.arg(srv.url("/homepage/"))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
cmd.assert().success().stdout(
|
|
|
|
predicate::str::contains("/LICENSE")
|
|
|
|
.and(predicate::str::contains("200"))
|
|
|
|
.and(predicate::str::contains("/homepage/assets/img/icons/handshake.svg").not()),
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(mock.hits(), 1);
|
|
|
|
assert_eq!(mock_two.hits(), 0);
|
|
|
|
teardown_tmp_directory(tmp_dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-06-18 11:48:41 +00:00
|
|
|
/// test that the deny list prevents requests of urls found during recursion
|
2021-06-18 01:34:21 +00:00
|
|
|
fn deny_list_works_during_recursion() {
|
|
|
|
let srv = MockServer::start();
|
|
|
|
let urls = [
|
|
|
|
"js".to_string(),
|
|
|
|
"prod".to_string(),
|
|
|
|
"dev".to_string(),
|
|
|
|
"file.js".to_string(),
|
|
|
|
];
|
|
|
|
let (tmp_dir, file) = setup_tmp_directory(&urls, "wordlist").unwrap();
|
|
|
|
|
|
|
|
let js_mock = srv.mock(|when, then| {
|
|
|
|
when.method(GET).path("/js");
|
2022-12-29 21:32:22 +00:00
|
|
|
then.status(301).header("Location", srv.url("/js/"));
|
2021-06-18 01:34:21 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
let js_prod_mock = srv.mock(|when, then| {
|
|
|
|
when.method(GET).path("/js/prod");
|
2022-12-29 21:32:22 +00:00
|
|
|
then.status(301).header("Location", srv.url("/js/prod/"));
|
2021-06-18 01:34:21 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
let js_dev_mock = srv.mock(|when, then| {
|
|
|
|
when.method(GET).path("/js/dev");
|
2022-12-29 21:32:22 +00:00
|
|
|
then.status(301).header("Location", srv.url("/js/dev/"));
|
2021-06-18 01:34:21 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
let js_dev_file_mock = srv.mock(|when, then| {
|
|
|
|
when.method(GET).path("/js/dev/file.js");
|
|
|
|
then.status(200)
|
|
|
|
.body("this is a test and is more bytes than other ones");
|
|
|
|
});
|
|
|
|
|
|
|
|
let cmd = Command::cargo_bin("feroxbuster")
|
|
|
|
.unwrap()
|
|
|
|
.arg("--url")
|
|
|
|
.arg(srv.url("/"))
|
|
|
|
.arg("--wordlist")
|
|
|
|
.arg(file.as_os_str())
|
|
|
|
.arg("-t")
|
|
|
|
.arg("1")
|
|
|
|
.arg("--dont-scan")
|
|
|
|
.arg(srv.url("/js/dev"))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
cmd.assert().success().stdout(
|
|
|
|
predicate::str::is_match("301.*js")
|
|
|
|
.unwrap()
|
|
|
|
.and(predicate::str::is_match("301.*js/prod").unwrap())
|
|
|
|
.and(predicate::str::is_match("301.*js/dev").unwrap())
|
|
|
|
.not()
|
|
|
|
.and(predicate::str::is_match("200.*js/dev/file.js").unwrap())
|
|
|
|
.not(),
|
|
|
|
);
|
|
|
|
|
2022-02-14 12:29:25 +00:00
|
|
|
assert_eq!(js_mock.hits(), 2);
|
|
|
|
assert_eq!(js_prod_mock.hits(), 2);
|
2021-06-18 01:34:21 +00:00
|
|
|
assert_eq!(js_dev_mock.hits(), 0);
|
|
|
|
assert_eq!(js_dev_file_mock.hits(), 0);
|
|
|
|
|
|
|
|
teardown_tmp_directory(tmp_dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-06-18 11:48:41 +00:00
|
|
|
/// test that the deny list prevents requests of urls found during recursion when the denier is a
|
|
|
|
/// parent of a user-specified scan
|
2021-06-18 01:34:21 +00:00
|
|
|
fn deny_list_works_during_recursion_with_inverted_parents() {
|
|
|
|
let srv = MockServer::start();
|
|
|
|
let urls = [
|
|
|
|
"js".to_string(),
|
|
|
|
"prod".to_string(),
|
|
|
|
"dev".to_string(),
|
|
|
|
"api".to_string(),
|
|
|
|
"file.js".to_string(),
|
|
|
|
];
|
|
|
|
let (tmp_dir, file) = setup_tmp_directory(&urls, "wordlist").unwrap();
|
|
|
|
|
|
|
|
let js_mock = srv.mock(|when, then| {
|
|
|
|
when.method(GET).path("/js");
|
2022-12-29 21:32:22 +00:00
|
|
|
then.status(301).header("Location", srv.url("/js/"));
|
2021-06-18 01:34:21 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
let api_mock = srv.mock(|when, then| {
|
|
|
|
when.method(GET).path("/api");
|
|
|
|
then.status(200);
|
|
|
|
});
|
|
|
|
|
|
|
|
let js_prod_mock = srv.mock(|when, then| {
|
|
|
|
when.method(GET).path("/js/prod");
|
2022-12-29 21:32:22 +00:00
|
|
|
then.status(301).header("Location", srv.url("/js/prod/"));
|
2021-06-18 01:34:21 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
let js_dev_mock = srv.mock(|when, then| {
|
|
|
|
when.method(GET).path("/js/dev");
|
2022-12-29 21:32:22 +00:00
|
|
|
then.status(301).header("Location", srv.url("/js/dev/"));
|
2021-06-18 01:34:21 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
let js_dev_file_mock = srv.mock(|when, then| {
|
|
|
|
when.method(GET).path("/js/dev/file.js");
|
|
|
|
then.status(200)
|
|
|
|
.body("this is a test and is more bytes than other ones");
|
|
|
|
});
|
|
|
|
|
|
|
|
let cmd = Command::cargo_bin("feroxbuster")
|
|
|
|
.unwrap()
|
|
|
|
.arg("--url")
|
|
|
|
.arg(srv.url("/js"))
|
|
|
|
.arg("--wordlist")
|
|
|
|
.arg(file.as_os_str())
|
|
|
|
.arg("-t")
|
|
|
|
.arg("1")
|
|
|
|
.arg("-vvvv")
|
|
|
|
.arg("--dont-scan")
|
|
|
|
.arg(srv.url("/"))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
cmd.assert().success().stdout(
|
|
|
|
predicate::str::is_match("301.*js")
|
|
|
|
.unwrap()
|
|
|
|
.and(predicate::str::is_match("301.*js/prod").unwrap())
|
|
|
|
.and(predicate::str::is_match("301.*js/dev").unwrap())
|
|
|
|
.and(predicate::str::is_match("200.*js/dev/file.js").unwrap())
|
|
|
|
.and(predicate::str::is_match("200.*api").unwrap())
|
|
|
|
.not(),
|
|
|
|
);
|
|
|
|
|
2022-02-14 12:29:25 +00:00
|
|
|
assert_eq!(js_mock.hits(), 2);
|
|
|
|
assert_eq!(js_prod_mock.hits(), 2);
|
|
|
|
assert_eq!(js_dev_mock.hits(), 2);
|
2021-06-18 01:34:21 +00:00
|
|
|
assert_eq!(js_dev_file_mock.hits(), 1);
|
|
|
|
assert_eq!(api_mock.hits(), 0);
|
|
|
|
|
|
|
|
teardown_tmp_directory(tmp_dir);
|
|
|
|
}
|
2021-10-03 19:17:28 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
/// test that a regex that prevents the base url from being scanned results in an early exit
|
|
|
|
fn deny_list_prevents_regex_that_denies_base_url() {
|
|
|
|
let srv = MockServer::start();
|
|
|
|
let (tmp_dir, file) = setup_tmp_directory(&["LICENSE".to_string()], "wordlist").unwrap();
|
|
|
|
|
|
|
|
let mock = srv.mock(|when, then| {
|
|
|
|
when.method(GET).path("/LICENSE");
|
|
|
|
then.status(200).body("this is a test");
|
|
|
|
});
|
|
|
|
|
|
|
|
let cmd = Command::cargo_bin("feroxbuster")
|
|
|
|
.unwrap()
|
|
|
|
.arg("--url")
|
|
|
|
.arg(srv.url("/"))
|
|
|
|
.arg("--wordlist")
|
|
|
|
.arg(file.as_os_str())
|
|
|
|
.arg("--dont-scan")
|
|
|
|
.arg("/")
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
teardown_tmp_directory(tmp_dir);
|
|
|
|
|
|
|
|
let err_msg = format!(
|
|
|
|
"Could not determine initial targets: The regex '/' matches {}/; the scan will never start",
|
|
|
|
srv.base_url()
|
|
|
|
);
|
|
|
|
cmd.assert()
|
|
|
|
.success()
|
|
|
|
.stderr(predicate::str::contains(err_msg));
|
|
|
|
|
|
|
|
assert_eq!(mock.hits(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
/// test that a url that prevents the base url from being scanned results in an early exit
|
|
|
|
fn deny_list_prevents_url_that_denies_base_url() {
|
|
|
|
let srv = MockServer::start();
|
|
|
|
let (tmp_dir, file) = setup_tmp_directory(&["LICENSE".to_string()], "wordlist").unwrap();
|
|
|
|
|
|
|
|
let mock = srv.mock(|when, then| {
|
|
|
|
when.method(GET).path("/LICENSE");
|
|
|
|
then.status(200).body("this is a test");
|
|
|
|
});
|
|
|
|
|
|
|
|
let cmd = Command::cargo_bin("feroxbuster")
|
|
|
|
.unwrap()
|
|
|
|
.arg("--url")
|
|
|
|
.arg(srv.url("/"))
|
|
|
|
.arg("--wordlist")
|
|
|
|
.arg(file.as_os_str())
|
|
|
|
.arg("--dont-scan")
|
|
|
|
.arg(srv.base_url())
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
teardown_tmp_directory(tmp_dir);
|
|
|
|
|
|
|
|
let err_msg = format!(
|
|
|
|
"Could not determine initial targets: The url '{}/' matches {}/; the scan will never start",
|
|
|
|
srv.base_url(),
|
|
|
|
srv.base_url()
|
|
|
|
);
|
|
|
|
|
|
|
|
cmd.assert()
|
|
|
|
.success()
|
|
|
|
.stderr(predicate::str::contains(err_msg));
|
|
|
|
|
|
|
|
assert_eq!(mock.hits(), 0);
|
|
|
|
}
|