added more tests

This commit is contained in:
epi 2022-02-15 07:14:23 -06:00
parent 9a84c5234f
commit 88a595fd82
5 changed files with 290 additions and 5 deletions

135
Cargo.lock generated
View File

@ -11,6 +11,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.53"
@ -26,6 +35,29 @@ dependencies = [
"term",
]
[[package]]
name = "assay"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "238d82aacd5cfde8ccae5c981912be68ec3cfa2d92ff4ce34090be40584c96a6"
dependencies = [
"assay-proc-macro",
"pretty_assertions",
"rusty-fork",
"tempdir",
"tokio",
]
[[package]]
name = "assay-proc-macro"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b5fd637c6a75fe224b372556511913f12d6ad481fbfef2fb7ecea2f7cb4965d"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "assert-json-diff"
version = "2.0.1"
@ -674,6 +706,7 @@ name = "feroxbuster"
version = "2.6.0"
dependencies = [
"anyhow",
"assay",
"assert_cmd",
"clap",
"clap_complete",
@ -752,6 +785,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "futf"
version = "0.1.5"
@ -1506,6 +1545,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "output_vt100"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
dependencies = [
"winapi",
]
[[package]]
name = "parking"
version = "2.0.0"
@ -1581,7 +1629,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
dependencies = [
"phf_shared 0.8.0",
"rand",
"rand 0.7.3",
]
[[package]]
@ -1715,6 +1763,18 @@ dependencies = [
"termtree",
]
[[package]]
name = "pretty_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76d5b548b725018ab5496482b45cb8bef21e9fed1858a6d674e3a8a0f0bb5d50"
dependencies = [
"ansi_term",
"ctor",
"diff",
"output_vt100",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
@ -1730,6 +1790,12 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.15"
@ -1739,6 +1805,19 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
]
[[package]]
name = "rand"
version = "0.7.3"
@ -1748,7 +1827,7 @@ dependencies = [
"getrandom 0.1.16",
"libc",
"rand_chacha",
"rand_core",
"rand_core 0.5.1",
"rand_hc",
"rand_pcg",
]
@ -1760,9 +1839,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.5.1",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.5.1"
@ -1778,7 +1872,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
"rand_core 0.5.1",
]
[[package]]
@ -1787,7 +1881,16 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
dependencies = [
"rand_core",
"rand_core 0.5.1",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
@ -1902,6 +2005,18 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
[[package]]
name = "rusty-fork"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
dependencies = [
"fnv",
"quick-error",
"tempfile",
"wait-timeout",
]
[[package]]
name = "ryu"
version = "1.0.9"
@ -2176,6 +2291,16 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "tempdir"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
dependencies = [
"rand 0.4.6",
"remove_dir_all",
]
[[package]]
name = "tempfile"
version = "3.3.0"

View File

@ -55,6 +55,7 @@ tempfile = "3.3"
httpmock = "0.6"
assert_cmd = "2.0"
predicates = "2.1"
assay = "0.1.0"
[profile.release]
lto = true

View File

@ -407,6 +407,7 @@ impl HeuristicTests {
#[cfg(test)]
mod tests {
use super::*;
use assay::assay;
#[test]
/// request a unique string of 32bytes * a value returns correct result
@ -417,4 +418,51 @@ mod tests {
assert_eq!(tester.unique_string(i).len(), i * 32);
}
}
#[assay]
/// `detect_directory_listing` correctly identifies tomcat/python instances
fn detect_directory_listing_finds_tomcat_python() {
let html = "<title>directory listing for /</title>";
let parsed = Html::parse_document(html);
let handles = Handles::for_testing(None, None);
let heuristics = HeuristicTests::new(Arc::new(handles.0));
let dirlist_type = heuristics.detect_directory_listing(&parsed);
assert!(matches!(
dirlist_type.unwrap(),
DirListingType::TomCatOrPython
));
}
#[assay]
/// `detect_directory_listing` correctly identifies apache instances
fn detect_directory_listing_finds_apache() {
let html = "<title>index of /</title>";
let parsed = Html::parse_document(html);
let handles = Handles::for_testing(None, None);
let heuristics = HeuristicTests::new(Arc::new(handles.0));
let dirlist_type = heuristics.detect_directory_listing(&parsed);
assert!(matches!(dirlist_type.unwrap(), DirListingType::Apache));
}
#[assay]
/// `detect_directory_listing` correctly identifies ASP.NET instances
fn detect_directory_listing_finds_asp_dot_net() {
let html = "<title>directory listing -- /</title>";
let parsed = Html::parse_document(html);
let handles = Handles::for_testing(None, None);
let heuristics = HeuristicTests::new(Arc::new(handles.0));
let dirlist_type = heuristics.detect_directory_listing(&parsed);
assert!(matches!(dirlist_type.unwrap(), DirListingType::AspDotNet));
}
#[assay]
/// `detect_directory_listing` returns None when heuristic doesn't match
fn detect_directory_listing_returns_none_as_default() {
let html = "<title>derp listing -- /</title>";
let parsed = Html::parse_document(html);
let handles = Handles::for_testing(None, None);
let heuristics = HeuristicTests::new(Arc::new(handles.0));
let dirlist_type = heuristics.detect_directory_listing(&parsed);
assert!(dirlist_type.is_none());
}
}

View File

@ -294,6 +294,11 @@ impl FeroxResponse {
// only add extensions to those responses that pass our checks; filtered out
// status codes are handled by should_filter, but we need to still check against
// the allow list for what we want to keep
#[cfg(test)]
handles
.send_scan_command(Command::AddDiscoveredExtension(extension.to_owned()))
.unwrap_or_default();
#[cfg(not(test))]
handles.send_scan_command(Command::AddDiscoveredExtension(extension.to_owned()))?;
}
}
@ -652,6 +657,7 @@ impl<'de> Deserialize<'de> for FeroxResponse {
#[cfg(test)]
mod tests {
use super::*;
use crate::config::Configuration;
use std::default::Default;
#[test]
@ -723,4 +729,65 @@ mod tests {
let result = response.reached_max_depth(0, 2, handles);
assert!(result);
}
#[test]
/// simple case of a single extension gets parsed correctly and stored on the `FeroxResponse`
fn parse_extension_finds_simple_extension() {
let config = Configuration {
collect_extensions: true,
..Default::default()
};
let (handles, _) = Handles::for_testing(None, Some(Arc::new(config)));
let url = Url::parse("http://localhost/derp.js").unwrap();
let mut response = FeroxResponse {
url,
..Default::default()
};
response.parse_extension(Arc::new(handles)).unwrap();
assert_eq!(response.extension, Some(String::from("js")));
}
#[test]
/// hidden files shouldn't be parsed as extensions, i.e. `/.bash_history`
fn parse_extension_ignores_hidden_files() {
let config = Configuration {
collect_extensions: true,
..Default::default()
};
let (handles, _) = Handles::for_testing(None, Some(Arc::new(config)));
let url = Url::parse("http://localhost/.bash_history").unwrap();
let mut response = FeroxResponse {
url,
..Default::default()
};
response.parse_extension(Arc::new(handles)).unwrap();
assert_eq!(response.extension, None);
}
#[test]
/// `parse_extension` should return immediately if `--collect-extensions` isn't used
fn parse_extension_early_returns_based_on_config() {
let (handles, _) = Handles::for_testing(None, None);
let url = Url::parse("http://localhost/derp.js").unwrap();
let mut response = FeroxResponse {
url,
..Default::default()
};
response.parse_extension(Arc::new(handles)).unwrap();
assert_eq!(response.extension, None);
}
}

View File

@ -1,8 +1,12 @@
mod utils;
use assay::assay;
use assert_cmd::prelude::*;
use httpmock::Method::GET;
use httpmock::MockServer;
use predicates::prelude::*;
use std::env::temp_dir;
use std::thread::sleep;
use std::time::Duration;
use std::{process::Command, time};
use utils::{setup_tmp_directory, teardown_tmp_directory};
@ -638,3 +642,43 @@ fn rate_limit_enforced_when_specified() {
teardown_tmp_directory(tmp_dir);
}
#[assay]
/// ensure that auto-discovered extensions are tracked in statistics and bar lengths are updated
fn add_discovered_extension_updates_bars_and_stats() {
let srv = MockServer::start();
let (tmp_dir, file) = setup_tmp_directory(
&["LICENSE".to_string(), "stuff.php".to_string()],
"wordlist",
)
.unwrap();
let mock = srv.mock(|when, then| {
when.method(GET).path("/stuff.php");
then.status(200).body("cool... coolcoolcool");
});
let file_path = tmp_dir.path().join("debug-file.txt");
assert!(!file_path.exists());
Command::cargo_bin("feroxbuster")?
.arg("--url")
.arg(srv.url("/"))
.arg("--wordlist")
.arg(file.as_os_str())
.arg("--extract-links")
.arg("--collect-extensions")
.arg("-vvvv")
.arg("--debug-log")
.arg(file_path.as_os_str())
.unwrap()
.assert()
.success();
let contents = std::fs::read_to_string(file_path).unwrap();
println!("{}", contents);
assert!(contents.contains("discovered new extension: php"));
assert!(contents.contains("extensions_collected: 1"));
assert!(contents.contains("expected_per_scan: 6"));
}