use std::time::Duration;
#[derive(Debug, Clone)]
pub struct ScanPreset {
pub name: &'static str,
pub description: &'static str,
pub modules: Vec<Module>,
pub rate_limit: RateLimit,
pub parallelism: Parallelism,
pub save_incremental: bool,
pub output_format: OutputFormat,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Module {
DnsPassive, WhoisLookup, CertTransparency, SearchEngines, ArchiveOrg,
TlsCert, HttpHeaders, DnsEnumeration, PortScanCommon,
PortScanFull, DirFuzzing, VulnScanning, WebCrawling, }
#[derive(Debug, Clone)]
pub struct RateLimit {
pub requests_per_second: u32,
pub delay_between_requests: Duration,
pub max_concurrent: usize,
pub jitter: bool, }
#[derive(Debug, Clone)]
pub struct Parallelism {
pub threads: usize,
pub batch_size: usize,
pub queue_size: usize,
}
#[derive(Debug, Clone)]
pub enum OutputFormat {
Text,
Json,
Both,
}
impl ScanPreset {
pub fn passive() -> Self {
Self {
name: "passive",
description: "100% passive reconnaissance using OSINT sources only",
modules: vec![
Module::DnsPassive,
Module::WhoisLookup,
Module::CertTransparency,
Module::SearchEngines,
Module::ArchiveOrg,
],
rate_limit: RateLimit {
requests_per_second: 5,
delay_between_requests: Duration::from_millis(200),
max_concurrent: 3,
jitter: true,
},
parallelism: Parallelism {
threads: 5,
batch_size: 10,
queue_size: 100,
},
save_incremental: true,
output_format: OutputFormat::Json,
}
}
pub fn stealth() -> Self {
Self {
name: "stealth",
description: "Minimal contact, rate-limited, incremental scanning",
modules: vec![
Module::DnsPassive,
Module::WhoisLookup,
Module::CertTransparency,
Module::TlsCert,
Module::HttpHeaders,
Module::DnsEnumeration,
Module::PortScanCommon,
],
rate_limit: RateLimit {
requests_per_second: 10,
delay_between_requests: Duration::from_millis(100),
max_concurrent: 5,
jitter: true, },
parallelism: Parallelism {
threads: 10,
batch_size: 20,
queue_size: 200,
},
save_incremental: true, output_format: OutputFormat::Both,
}
}
pub fn aggressive() -> Self {
Self {
name: "aggressive",
description: "Full-speed scanning with all modules enabled",
modules: vec![
Module::DnsPassive,
Module::WhoisLookup,
Module::CertTransparency,
Module::SearchEngines,
Module::ArchiveOrg,
Module::TlsCert,
Module::HttpHeaders,
Module::DnsEnumeration,
Module::PortScanFull,
Module::DirFuzzing,
Module::VulnScanning,
Module::WebCrawling,
],
rate_limit: RateLimit {
requests_per_second: 100,
delay_between_requests: Duration::from_millis(10),
max_concurrent: 50,
jitter: false,
},
parallelism: Parallelism {
threads: 100,
batch_size: 100,
queue_size: 1000,
},
save_incremental: true,
output_format: OutputFormat::Text,
}
}
pub fn from_name(name: &str) -> Option<Self> {
match name.to_lowercase().as_str() {
"passive" => Some(Self::passive()),
"stealth" => Some(Self::stealth()),
"aggressive" | "aggresive" => Some(Self::aggressive()),
_ => None,
}
}
pub fn has_module(&self, module: &Module) -> bool {
self.modules.contains(module)
}
}
impl Default for ScanPreset {
fn default() -> Self {
Self::stealth() }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_presets() {
let passive = ScanPreset::passive();
assert_eq!(passive.name, "passive");
assert_eq!(passive.modules.len(), 5);
assert!(passive.has_module(&Module::DnsPassive));
assert!(!passive.has_module(&Module::PortScanFull));
let stealth = ScanPreset::stealth();
assert_eq!(stealth.name, "stealth");
assert!(stealth.save_incremental);
let aggressive = ScanPreset::aggressive();
assert_eq!(aggressive.name, "aggressive");
assert!(aggressive.modules.len() > stealth.modules.len());
}
#[test]
fn test_from_name() {
assert!(ScanPreset::from_name("passive").is_some());
assert!(ScanPreset::from_name("stealth").is_some());
assert!(ScanPreset::from_name("aggressive").is_some());
assert!(ScanPreset::from_name("invalid").is_none());
}
}