Skip to content

Commit 4354974

Browse files
committed
Fixed clippy warning
1 parent ac3be71 commit 4354974

4 files changed

Lines changed: 105 additions & 84 deletions

File tree

examples/benchmark.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ fn evaluate_certificate(seed: &str, offset: usize, coverage: usize) -> usize {
123123
measure_block!("tls_parse", {
124124
let len = bytes.len().max(1);
125125
bytes.rotate_left(offset % len);
126-
if coverage % 2 == 0 {
126+
if coverage.is_multiple_of(2) {
127127
thread::sleep(Duration::from_micros(60));
128128
}
129129
});

shadowmap-sbom.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"components": [
3+
{
4+
"licenses": [
5+
"Apache-2.0",
6+
"MIT"
7+
],
8+
"name": "shadowmap",
9+
"type": "application",
10+
"version": "0.1.0"
11+
},
12+
{
13+
"licenses": [
14+
"Apache-2.0"
15+
],
16+
"name": "openssl",
17+
"type": "library",
18+
"version": "3.2.1"
19+
},
20+
{
21+
"licenses": [
22+
"MIT"
23+
],
24+
"name": "tokio",
25+
"type": "library",
26+
"version": "1.40.0"
27+
}
28+
],
29+
"format": "cyclonedx",
30+
"generated_at": "2025-12-20T08:28:34.714909+00:00",
31+
"manifest": "Cargo.toml"
32+
}

src/pretty_report.rs

Lines changed: 58 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@ use tabled::{Table, Tabled, settings::Style};
1010
use crate::cloud::CloudAssetFinding;
1111
use crate::social::SocialIntelligenceSummary;
1212

13+
pub struct ReconSummary<'a> {
14+
pub domain: &'a str,
15+
pub subs: &'a HashSet<String>,
16+
pub header_map: &'a HashMap<String, (u16, Option<String>)>,
17+
pub open_ports_map: &'a HashMap<String, Vec<u16>>,
18+
pub cors_map: &'a HashMap<String, Vec<String>>,
19+
pub software_map: &'a HashMap<String, HashMap<String, String>>,
20+
pub takeover_map: &'a HashMap<String, Vec<String>>,
21+
#[allow(dead_code)]
22+
pub cloud_saas_map: &'a HashMap<String, Vec<String>>,
23+
pub cloud_asset_map: &'a HashMap<String, Vec<CloudAssetFinding>>,
24+
pub social_intel: Option<&'a SocialIntelligenceSummary>,
25+
pub output_dir: &'a str,
26+
}
27+
1328
pub struct PrettyReportGenerator;
1429

1530
#[derive(Tabled)]
@@ -30,27 +45,17 @@ struct SubdomainRow {
3045

3146
impl PrettyReportGenerator {
3247
pub fn generate_markdown_report(
33-
domain: &str,
34-
subs: &HashSet<String>,
35-
header_map: &HashMap<String, (u16, Option<String>)>,
36-
open_ports_map: &HashMap<String, Vec<u16>>,
37-
cors_map: &HashMap<String, Vec<String>>,
38-
software_map: &HashMap<String, HashMap<String, String>>,
39-
takeover_map: &HashMap<String, Vec<String>>,
40-
_cloud_saas_map: &HashMap<String, Vec<String>>,
41-
cloud_asset_map: &HashMap<String, Vec<CloudAssetFinding>>,
42-
social_intel: Option<&SocialIntelligenceSummary>,
43-
output_dir: &str,
48+
summary: &ReconSummary,
4449
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
45-
let report_file = format!("{}/{}_REPORT.md", output_dir, domain);
50+
let report_file = format!("{}/{}_REPORT.md", summary.output_dir, summary.domain);
4651
let mut f = File::create(&report_file)?;
4752

4853
let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S %Z");
4954

5055
// Header
5156
writeln!(f, "# ShadowMap Security Report")?;
5257
writeln!(f)?;
53-
writeln!(f, "**Target Domain:** `{}`", domain)?;
58+
writeln!(f, "**Target Domain:** `{}`", summary.domain)?;
5459
writeln!(f, "**Scan Date:** {}", timestamp)?;
5560
writeln!(f, "**Generated by:** ShadowMap v{}", env!("CARGO_PKG_VERSION"))?;
5661
writeln!(f)?;
@@ -61,10 +66,10 @@ impl PrettyReportGenerator {
6166
writeln!(f, "## Executive Summary")?;
6267
writeln!(f)?;
6368

64-
let total_subs = subs.len();
65-
let cors_issues = cors_map.len();
66-
let takeover_risks = takeover_map.len();
67-
let cloud_assets = cloud_asset_map.len();
69+
let total_subs = summary.subs.len();
70+
let cors_issues = summary.cors_map.len();
71+
let takeover_risks = summary.takeover_map.len();
72+
let cloud_assets = summary.cloud_asset_map.len();
6873

6974
let risk_level = if cors_issues > 0 || takeover_risks > 0 {
7075
"**HIGH**"
@@ -96,7 +101,7 @@ impl PrettyReportGenerator {
96101
writeln!(f, "**Impact:** Data theft, unauthorized API access")?;
97102
writeln!(f)?;
98103

99-
for (sub, issues) in cors_map.iter().sorted_by_key(|(k, _)| k.as_str()) {
104+
for (sub, issues) in summary.cors_map.iter().sorted_by_key(|(k, _)| k.as_str()) {
100105
writeln!(f, "**Host:** `{}`", sub)?;
101106
for issue in issues {
102107
writeln!(f, "- {}", issue)?;
@@ -117,7 +122,7 @@ impl PrettyReportGenerator {
117122
if takeover_risks > 0 {
118123
writeln!(f, "### Subdomain Takeover Risks")?;
119124
writeln!(f)?;
120-
for (sub, risks) in takeover_map.iter().sorted_by_key(|(k, _)| k.as_str()) {
125+
for (sub, risks) in summary.takeover_map.iter().sorted_by_key(|(k, _)| k.as_str()) {
121126
writeln!(f, "**Subdomain:** `{}`", sub)?;
122127
for risk in risks {
123128
writeln!(f, "- {}", risk)?;
@@ -133,8 +138,8 @@ impl PrettyReportGenerator {
133138
writeln!(f, "| # | Subdomain | Status | Server | Open Ports | Issues |")?;
134139
writeln!(f, "|---|-----------|--------|--------|------------|--------|")?;
135140

136-
for (idx, sub) in subs.iter().sorted().enumerate() {
137-
let (status, server) = header_map.get(sub).cloned().unwrap_or((0, None));
141+
for (idx, sub) in summary.subs.iter().sorted().enumerate() {
142+
let (status, server) = summary.header_map.get(sub).cloned().unwrap_or((0, None));
138143
let status_icon = match status {
139144
200..=299 => "OK",
140145
300..=399 => "REDIRECT",
@@ -143,12 +148,12 @@ impl PrettyReportGenerator {
143148
_ => "UNKNOWN",
144149
};
145150

146-
let ports = open_ports_map.get(sub)
151+
let ports = summary.open_ports_map.get(sub)
147152
.map(|v| v.iter().map(|p| p.to_string()).join(", "))
148153
.unwrap_or_else(|| "-".to_string());
149154

150-
let issues_count = cors_map.get(sub).map(|v| v.len()).unwrap_or(0)
151-
+ takeover_map.get(sub).map(|v| v.len()).unwrap_or(0);
155+
let issues_count = summary.cors_map.get(sub).map(|v| v.len()).unwrap_or(0)
156+
+ summary.takeover_map.get(sub).map(|v| v.len()).unwrap_or(0);
152157

153158
let issues_display = if issues_count > 0 {
154159
format!("ISSUES: {}", issues_count)
@@ -174,15 +179,15 @@ impl PrettyReportGenerator {
174179
writeln!(f, "## Port Analysis")?;
175180
writeln!(f)?;
176181
let mut all_ports: HashSet<u16> = HashSet::new();
177-
for ports in open_ports_map.values() {
182+
for ports in summary.open_ports_map.values() {
178183
all_ports.extend(ports);
179184
}
180185

181186
writeln!(f, "**Unique Open Ports:** {}", all_ports.iter().sorted().map(|p| p.to_string()).join(", "))?;
182187
writeln!(f)?;
183188

184189
for port in all_ports.iter().sorted() {
185-
let hosts_with_port: Vec<&String> = open_ports_map
190+
let hosts_with_port: Vec<&String> = summary.open_ports_map
186191
.iter()
187192
.filter(|(_, ports)| ports.contains(port))
188193
.map(|(host, _)| host)
@@ -210,11 +215,11 @@ impl PrettyReportGenerator {
210215
}
211216

212217
// Software Fingerprints
213-
if !software_map.is_empty() {
218+
if !summary.software_map.is_empty() {
214219
writeln!(f, "## Software Fingerprints")?;
215220
writeln!(f)?;
216221

217-
for (sub, fingerprints) in software_map.iter().sorted_by_key(|(k, _)| k.as_str()) {
222+
for (sub, fingerprints) in summary.software_map.iter().sorted_by_key(|(k, _)| k.as_str()) {
218223
if !fingerprints.is_empty() {
219224
writeln!(f, "**{}**", sub)?;
220225
for (key, value) in fingerprints {
@@ -226,14 +231,14 @@ impl PrettyReportGenerator {
226231
}
227232

228233
// Cloud Assets
229-
if !cloud_asset_map.is_empty() {
234+
if !summary.cloud_asset_map.is_empty() {
230235
writeln!(f, "## Cloud Infrastructure Analysis")?;
231236
writeln!(f)?;
232-
writeln!(f, "**Total Predicted Assets:** {}", cloud_asset_map.values().map(|v| v.len()).sum::<usize>())?;
237+
writeln!(f, "**Total Predicted Assets:** {}", summary.cloud_asset_map.values().map(|v| v.len()).sum::<usize>())?;
233238
writeln!(f)?;
234239

235240
let mut provider_counts: HashMap<String, usize> = HashMap::new();
236-
for assets in cloud_asset_map.values() {
241+
for assets in summary.cloud_asset_map.values() {
237242
for asset in assets {
238243
*provider_counts.entry(asset.provider.clone()).or_insert(0) += 1;
239244
}
@@ -248,7 +253,7 @@ impl PrettyReportGenerator {
248253
}
249254

250255
// Social Intelligence
251-
if let Some(intel) = social_intel {
256+
if let Some(intel) = summary.social_intel {
252257
writeln!(f, "## AI-Powered Threat Intelligence")?;
253258
writeln!(f)?;
254259
writeln!(f, "**Framework:** {}", intel.framework_name)?;
@@ -305,7 +310,7 @@ impl PrettyReportGenerator {
305310
if takeover_risks > 0 {
306311
writeln!(f, "2. **Investigate subdomain takeover risks** on {} subdomain(s)", takeover_risks)?;
307312
}
308-
if open_ports_map.values().any(|ports| ports.contains(&22)) {
313+
if summary.open_ports_map.values().any(|ports| ports.contains(&22)) {
309314
writeln!(f, "3. **Review SSH exposure** - Port 22 should not be publicly accessible")?;
310315
}
311316
writeln!(f)?;
@@ -331,56 +336,47 @@ impl PrettyReportGenerator {
331336
Ok(())
332337
}
333338

334-
pub fn print_terminal_summary(
335-
domain: &str,
336-
subs: &HashSet<String>,
337-
header_map: &HashMap<String, (u16, Option<String>)>,
338-
open_ports_map: &HashMap<String, Vec<u16>>,
339-
cors_map: &HashMap<String, Vec<String>>,
340-
takeover_map: &HashMap<String, Vec<String>>,
341-
cloud_asset_map: &HashMap<String, Vec<CloudAssetFinding>>,
342-
social_intel: Option<&SocialIntelligenceSummary>,
343-
) {
339+
pub fn print_terminal_summary(summary: &ReconSummary) {
344340
println!();
345341
println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan());
346-
println!("{}", format!(" SCAN RESULTS: {}", domain).bright_white().bold());
342+
println!("{}", format!(" SCAN RESULTS: {}", summary.domain).bright_white().bold());
347343
println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan());
348344
println!();
349345

350346
// Quick Stats
351347
println!("{}", "QUICK STATS".bright_yellow().bold());
352348
println!("{}", "─────────────────────────────────────────────────────────".bright_black());
353-
println!(" {} Subdomains discovered", subs.len().to_string().bright_green().bold());
354-
println!(" {} Live hosts verified", subs.len().to_string().bright_green().bold());
349+
println!(" {} Subdomains discovered", summary.subs.len().to_string().bright_green().bold());
350+
println!(" {} Live hosts verified", summary.subs.len().to_string().bright_green().bold());
355351

356-
if cors_map.is_empty() {
352+
if summary.cors_map.is_empty() {
357353
println!(" {} CORS vulnerabilities", "0".bright_green().bold());
358354
} else {
359-
println!(" {} CORS vulnerabilities [WARNING]", cors_map.len().to_string().bright_red().bold());
355+
println!(" {} CORS vulnerabilities [WARNING]", summary.cors_map.len().to_string().bright_red().bold());
360356
}
361357

362-
if takeover_map.is_empty() {
358+
if summary.takeover_map.is_empty() {
363359
println!(" {} Takeover risks", "0".bright_green().bold());
364360
} else {
365-
println!(" {} Takeover risks [WARNING]", takeover_map.len().to_string().bright_yellow().bold());
361+
println!(" {} Takeover risks [WARNING]", summary.takeover_map.len().to_string().bright_yellow().bold());
366362
}
367363

368-
println!(" {} Cloud assets flagged", cloud_asset_map.len().to_string().bright_cyan().bold());
364+
println!(" {} Cloud assets flagged", summary.cloud_asset_map.len().to_string().bright_cyan().bold());
369365
println!();
370366

371367
// Critical Findings
372-
if !cors_map.is_empty() || !takeover_map.is_empty() {
368+
if !summary.cors_map.is_empty() || !summary.takeover_map.is_empty() {
373369
println!("{}", "CRITICAL FINDINGS".bright_red().bold());
374370
println!("{}", "─────────────────────────────────────────────────────────".bright_black());
375371

376-
for (sub, issues) in cors_map.iter().sorted_by_key(|(k, _)| k.as_str()) {
372+
for (sub, issues) in summary.cors_map.iter().sorted_by_key(|(k, _)| k.as_str()) {
377373
println!(" {} {}", "CORS:".bright_red().bold(), sub.bright_white());
378374
for issue in issues {
379375
println!(" • {}", issue.bright_yellow());
380376
}
381377
}
382378

383-
for (sub, risks) in takeover_map.iter().sorted_by_key(|(k, _)| k.as_str()) {
379+
for (sub, risks) in summary.takeover_map.iter().sorted_by_key(|(k, _)| k.as_str()) {
384380
println!(" {} {}", "TAKEOVER:".bright_yellow().bold(), sub.bright_white());
385381
for risk in risks {
386382
println!(" • {}", risk);
@@ -394,25 +390,25 @@ impl PrettyReportGenerator {
394390
println!("{}", "─────────────────────────────────────────────────────────".bright_black());
395391

396392
let mut rows: Vec<SubdomainRow> = Vec::new();
397-
for sub in subs.iter().sorted().take(10) {
398-
let (status, server) = header_map.get(sub).cloned().unwrap_or((0, None));
393+
for sub in summary.subs.iter().sorted().take(10) {
394+
let (status, server) = summary.header_map.get(sub).cloned().unwrap_or((0, None));
399395
let status_str = if status == 0 {
400396
"N/A".to_string()
401397
} else {
402398
format!("{}", status)
403399
};
404400

405-
let ports = open_ports_map.get(sub)
401+
let ports = summary.open_ports_map.get(sub)
406402
.map(|v| v.iter().take(4).map(|p| p.to_string()).join(","))
407403
.unwrap_or_else(|| "-".to_string());
408404

409-
let cors_str = if cors_map.contains_key(sub) {
405+
let cors_str = if summary.cors_map.contains_key(sub) {
410406
"WARNING".to_string()
411407
} else {
412408
"OK".to_string()
413409
};
414410

415-
let risks = if takeover_map.contains_key(sub) {
411+
let risks = if summary.takeover_map.contains_key(sub) {
416412
"WARNING".to_string()
417413
} else {
418414
"OK".to_string()
@@ -433,14 +429,14 @@ impl PrettyReportGenerator {
433429
println!("{}", table);
434430
}
435431

436-
if subs.len() > 10 {
432+
if summary.subs.len() > 10 {
437433
println!();
438-
println!(" {} more subdomain(s) not shown...", (subs.len() - 10).to_string().bright_black());
434+
println!(" {} more subdomain(s) not shown...", (summary.subs.len() - 10).to_string().bright_black());
439435
}
440436
println!();
441437

442438
// Social Intelligence Summary
443-
if let Some(intel) = social_intel {
439+
if let Some(intel) = summary.social_intel {
444440
if intel.metrics.total_signals > 0 {
445441
println!("{}", "AI THREAT INTELLIGENCE".bright_magenta().bold());
446442
println!("{}", "─────────────────────────────────────────────────────────".bright_black());

src/reporting.rs

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use itertools::Itertools;
77

88
use crate::cloud::CloudAssetFinding;
99
use crate::social::SocialIntelligenceSummary;
10-
use crate::pretty_report::PrettyReportGenerator;
10+
use crate::pretty_report::{PrettyReportGenerator, ReconSummary};
1111

1212
pub struct ReconMaps<'a> {
1313
pub header_map: &'a HashMap<String, (u16, Option<String>)>,
@@ -190,31 +190,24 @@ pub fn write_outputs(
190190
)?;
191191

192192
// Generate beautiful markdown report
193-
PrettyReportGenerator::generate_markdown_report(
193+
let summary = ReconSummary {
194194
domain,
195195
subs,
196-
maps.header_map,
197-
maps.open_ports_map,
198-
maps.cors_map,
199-
maps.software_map,
200-
maps.takeover_map,
201-
maps.cloud_saas_map,
202-
maps.cloud_asset_map,
203-
maps.social_intel,
196+
header_map: maps.header_map,
197+
open_ports_map: maps.open_ports_map,
198+
cors_map: maps.cors_map,
199+
software_map: maps.software_map,
200+
takeover_map: maps.takeover_map,
201+
cloud_saas_map: maps.cloud_saas_map,
202+
cloud_asset_map: maps.cloud_asset_map,
203+
social_intel: maps.social_intel,
204204
output_dir,
205-
)?;
205+
};
206+
207+
PrettyReportGenerator::generate_markdown_report(&summary)?;
206208

207209
// Print terminal summary
208-
PrettyReportGenerator::print_terminal_summary(
209-
domain,
210-
subs,
211-
maps.header_map,
212-
maps.open_ports_map,
213-
maps.cors_map,
214-
maps.takeover_map,
215-
maps.cloud_asset_map,
216-
maps.social_intel,
217-
);
210+
PrettyReportGenerator::print_terminal_summary(&summary);
218211

219212
Ok(())
220213
}

0 commit comments

Comments
 (0)