1use std::time::Duration;
5
6#[derive(Debug, Clone)]
8pub struct ScanPreset {
9 pub name: &'static str,
10 pub description: &'static str,
11 pub modules: Vec<Module>,
12 pub rate_limit: RateLimit,
13 pub parallelism: Parallelism,
14 pub save_incremental: bool,
15 pub output_format: OutputFormat,
16}
17
18#[derive(Debug, Clone, PartialEq)]
20pub enum Module {
21 DnsPassive, WhoisLookup, CertTransparency, SearchEngines, ArchiveOrg, TlsCert, HttpHeaders, DnsEnumeration, PortScanCommon, PortScanFull, DirFuzzing, VulnScanning, WebCrawling, }
40
41#[derive(Debug, Clone)]
43pub struct RateLimit {
44 pub requests_per_second: u32,
45 pub delay_between_requests: Duration,
46 pub max_concurrent: usize,
47 pub jitter: bool, }
49
50#[derive(Debug, Clone)]
52pub struct Parallelism {
53 pub threads: usize,
54 pub batch_size: usize,
55 pub queue_size: usize,
56}
57
58#[derive(Debug, Clone)]
60pub enum OutputFormat {
61 Text,
62 Json,
63 Both,
64}
65
66impl ScanPreset {
67 pub fn passive() -> Self {
69 Self {
70 name: "passive",
71 description: "100% passive reconnaissance using OSINT sources only",
72 modules: vec![
73 Module::DnsPassive,
74 Module::WhoisLookup,
75 Module::CertTransparency,
76 Module::SearchEngines,
77 Module::ArchiveOrg,
78 ],
79 rate_limit: RateLimit {
80 requests_per_second: 5,
81 delay_between_requests: Duration::from_millis(200),
82 max_concurrent: 3,
83 jitter: true,
84 },
85 parallelism: Parallelism {
86 threads: 5,
87 batch_size: 10,
88 queue_size: 100,
89 },
90 save_incremental: true,
91 output_format: OutputFormat::Json,
92 }
93 }
94
95 pub fn stealth() -> Self {
97 Self {
98 name: "stealth",
99 description: "Minimal contact, rate-limited, incremental scanning",
100 modules: vec![
101 Module::DnsPassive,
103 Module::WhoisLookup,
104 Module::CertTransparency,
105 Module::TlsCert,
107 Module::HttpHeaders,
108 Module::DnsEnumeration,
109 Module::PortScanCommon,
110 ],
111 rate_limit: RateLimit {
112 requests_per_second: 10,
113 delay_between_requests: Duration::from_millis(100),
114 max_concurrent: 5,
115 jitter: true, },
117 parallelism: Parallelism {
118 threads: 10,
119 batch_size: 20,
120 queue_size: 200,
121 },
122 save_incremental: true, output_format: OutputFormat::Both,
124 }
125 }
126
127 pub fn aggressive() -> Self {
129 Self {
130 name: "aggressive",
131 description: "Full-speed scanning with all modules enabled",
132 modules: vec![
133 Module::DnsPassive,
135 Module::WhoisLookup,
136 Module::CertTransparency,
137 Module::SearchEngines,
138 Module::ArchiveOrg,
139 Module::TlsCert,
141 Module::HttpHeaders,
142 Module::DnsEnumeration,
143 Module::PortScanFull,
144 Module::DirFuzzing,
145 Module::VulnScanning,
146 Module::WebCrawling,
147 ],
148 rate_limit: RateLimit {
149 requests_per_second: 100,
150 delay_between_requests: Duration::from_millis(10),
151 max_concurrent: 50,
152 jitter: false,
153 },
154 parallelism: Parallelism {
155 threads: 100,
156 batch_size: 100,
157 queue_size: 1000,
158 },
159 save_incremental: true,
160 output_format: OutputFormat::Text,
161 }
162 }
163
164 pub fn from_name(name: &str) -> Option<Self> {
166 match name.to_lowercase().as_str() {
167 "passive" => Some(Self::passive()),
168 "stealth" => Some(Self::stealth()),
169 "aggressive" | "aggresive" => Some(Self::aggressive()),
170 _ => None,
171 }
172 }
173
174 pub fn has_module(&self, module: &Module) -> bool {
176 self.modules.contains(module)
177 }
178}
179
180impl Default for ScanPreset {
181 fn default() -> Self {
182 Self::stealth() }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn test_presets() {
192 let passive = ScanPreset::passive();
193 assert_eq!(passive.name, "passive");
194 assert_eq!(passive.modules.len(), 5);
195 assert!(passive.has_module(&Module::DnsPassive));
196 assert!(!passive.has_module(&Module::PortScanFull));
197
198 let stealth = ScanPreset::stealth();
199 assert_eq!(stealth.name, "stealth");
200 assert!(stealth.save_incremental);
201
202 let aggressive = ScanPreset::aggressive();
203 assert_eq!(aggressive.name, "aggressive");
204 assert!(aggressive.modules.len() > stealth.modules.len());
205 }
206
207 #[test]
208 fn test_from_name() {
209 assert!(ScanPreset::from_name("passive").is_some());
210 assert!(ScanPreset::from_name("stealth").is_some());
211 assert!(ScanPreset::from_name("aggressive").is_some());
212 assert!(ScanPreset::from_name("invalid").is_none());
213 }
214}