use crate::{ParsedTarget, AttackReport};
use crate::attacks::{
execute_cve_2026_25253, execute_cve_2026_22708, execute_cve_2026_25157,
execute_prompt_injection, execute_rag_poisoning, execute_supply_chain,
execute_mcp_poisoning, execute_elevated_bypass, execute_zero_click_rce,
};
use anyhow::Result;
use std::sync::Arc;
use tokio::sync::Semaphore;
pub struct ScanConfig {
pub concurrency: usize,
}
impl Default for ScanConfig {
fn default() -> Self {
Self { concurrency: 50 }
}
}
pub async fn scan_target(target: &ParsedTarget) -> Result<Vec<AttackReport>> {
use crate::attacks::check_write_capability;
use tokio::task;
let target_clone1 = target.clone();
let target_clone2 = target.clone();
let target_clone3 = target.clone();
let group1 = task::spawn(async move {
execute_cve_2026_25253(&target_clone1).await
});
let group2 = task::spawn(async move {
let capability = check_write_capability(&target_clone2).await?;
let t2 = target_clone2.clone();
let t3 = target_clone2.clone();
let t4 = target_clone2.clone();
let t5 = target_clone2.clone();
let t6 = target_clone2.clone();
let t7 = target_clone2.clone();
let t8 = target_clone2.clone();
let cap2 = capability.clone();
let cap3 = capability.clone();
let cap4 = capability.clone();
let cap5 = capability.clone();
let cap6 = capability.clone();
let cap7 = capability.clone();
let cap8 = capability.clone();
let m2 = task::spawn(async move { execute_cve_2026_22708(&t2, Some(&cap2)).await });
let m3 = task::spawn(async move { execute_cve_2026_25157(&t3, Some(&cap3)).await });
let m4 = task::spawn(async move { execute_prompt_injection(&t4, Some(&cap4)).await });
let m5 = task::spawn(async move { execute_rag_poisoning(&t5, Some(&cap5)).await });
let m6 = task::spawn(async move { execute_supply_chain(&t6, Some(&cap6)).await });
let m7 = task::spawn(async move { execute_mcp_poisoning(&t7, Some(&cap7)).await });
let m8 = task::spawn(async move { execute_elevated_bypass(&t8, Some(&cap8)).await });
let r2 = m2.await??;
let r3 = m3.await??;
let r4 = m4.await??;
let r5 = m5.await??;
let r6 = m6.await??;
let r7 = m7.await??;
let r8 = m8.await??;
Ok::<Vec<AttackReport>, anyhow::Error>(vec![r2, r3, r4, r5, r6, r7, r8])
});
let group3 = task::spawn(async move {
execute_zero_click_rce(&target_clone3).await
});
let report1 = group1.await??;
let reports2_8 = group2.await??;
let report9 = group3.await??;
let mut reports = Vec::new();
reports.push(report1);
reports.extend(reports2_8);
reports.push(report9);
Ok(reports)
}
pub async fn scan_targets(targets: Vec<ParsedTarget>, config: ScanConfig) -> Result<Vec<(ParsedTarget, Vec<AttackReport>)>> {
use tokio::task;
let semaphore = Arc::new(Semaphore::new(config.concurrency));
let mut tasks = Vec::new();
for target in targets {
let sem = semaphore.clone();
let task = task::spawn(async move {
let _permit = sem.acquire().await.unwrap();
let reports = scan_target(&target).await?;
Ok::<_, anyhow::Error>((target, reports))
});
tasks.push(task);
}
let mut results = Vec::new();
for task in tasks {
let result = task.await??;
results.push(result);
}
Ok(results)
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_scan_target_executes_all_modules() {
let target = ParsedTarget {
url: "ws://test.example.com:18789".to_string(),
host: "test.example.com".to_string(),
port: 18789,
};
let reports = scan_target(&target).await.unwrap();
assert_eq!(reports.len(), 9);
}
#[tokio::test]
async fn test_scan_targets_concurrent_execution() {
let targets = vec![
ParsedTarget {
url: "ws://target1.com:18789".to_string(),
host: "target1.com".to_string(),
port: 18789,
},
ParsedTarget {
url: "ws://target2.com:18789".to_string(),
host: "target2.com".to_string(),
port: 18789,
},
];
let config = ScanConfig { concurrency: 50 };
let results = scan_targets(targets.clone(), config).await.unwrap();
assert_eq!(results.len(), 2);
for (target, reports) in results {
assert!(targets.iter().any(|t| t.url == target.url));
assert_eq!(reports.len(), 9);
}
}
#[tokio::test]
async fn test_scan_config_default() {
let config = ScanConfig::default();
assert_eq!(config.concurrency, 50);
}
#[tokio::test]
async fn test_scan_target_modules_run_concurrently() {
use std::time::Instant;
let target = ParsedTarget {
url: "ws://test.example.com:18789".to_string(),
host: "test.example.com".to_string(),
port: 18789,
};
let start = Instant::now();
let reports = scan_target(&target).await.unwrap();
let duration = start.elapsed();
assert_eq!(reports.len(), 9);
assert!(duration.as_secs() < 120, "Scan took too long: {:?}", duration);
}
}