refactor: modularize codebase and add 404 unit tests (#13)
All checks were successful
CI / Format (push) Successful in 4s
CI / Clippy (push) Successful in 4m19s
CI / Security Audit (push) Successful in 1m44s
CI / Detect Changes (push) Successful in 5s
CI / Tests (push) Successful in 5m15s
CI / Deploy Agent (push) Successful in 2s
CI / Deploy Dashboard (push) Successful in 2s
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Successful in 2s
All checks were successful
CI / Format (push) Successful in 4s
CI / Clippy (push) Successful in 4m19s
CI / Security Audit (push) Successful in 1m44s
CI / Detect Changes (push) Successful in 5s
CI / Tests (push) Successful in 5m15s
CI / Deploy Agent (push) Successful in 2s
CI / Deploy Dashboard (push) Successful in 2s
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Successful in 2s
This commit was merged in pull request #13.
This commit is contained in:
@@ -54,72 +54,75 @@ impl PentestTool for ReconTool {
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
input: serde_json::Value,
|
||||
context: &'a PentestToolContext,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<PentestToolResult, CoreError>> + Send + 'a>> {
|
||||
_context: &'a PentestToolContext,
|
||||
) -> std::pin::Pin<
|
||||
Box<dyn std::future::Future<Output = Result<PentestToolResult, CoreError>> + Send + 'a>,
|
||||
> {
|
||||
Box::pin(async move {
|
||||
let url = input
|
||||
.get("url")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| CoreError::Dast("Missing required 'url' parameter".to_string()))?;
|
||||
let url = input
|
||||
.get("url")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| CoreError::Dast("Missing required 'url' parameter".to_string()))?;
|
||||
|
||||
let additional_paths: Vec<String> = input
|
||||
.get("additional_paths")
|
||||
.and_then(|v| v.as_array())
|
||||
.map(|arr| {
|
||||
arr.iter()
|
||||
.filter_map(|v| v.as_str().map(String::from))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let additional_paths: Vec<String> = input
|
||||
.get("additional_paths")
|
||||
.and_then(|v| v.as_array())
|
||||
.map(|arr| {
|
||||
arr.iter()
|
||||
.filter_map(|v| v.as_str().map(String::from))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let result = self.agent.scan(url).await?;
|
||||
let result = self.agent.scan(url).await?;
|
||||
|
||||
// Scan additional paths for more technology signals
|
||||
let mut extra_technologies: Vec<String> = Vec::new();
|
||||
let mut extra_headers: HashMap<String, String> = HashMap::new();
|
||||
// Scan additional paths for more technology signals
|
||||
let mut extra_technologies: Vec<String> = Vec::new();
|
||||
let mut extra_headers: HashMap<String, String> = HashMap::new();
|
||||
|
||||
let base_url = url.trim_end_matches('/');
|
||||
for path in &additional_paths {
|
||||
let probe_url = format!("{base_url}/{}", path.trim_start_matches('/'));
|
||||
if let Ok(resp) = self.http.get(&probe_url).send().await {
|
||||
for (key, value) in resp.headers() {
|
||||
let k = key.to_string().to_lowercase();
|
||||
let v = value.to_str().unwrap_or("").to_string();
|
||||
let base_url = url.trim_end_matches('/');
|
||||
for path in &additional_paths {
|
||||
let probe_url = format!("{base_url}/{}", path.trim_start_matches('/'));
|
||||
if let Ok(resp) = self.http.get(&probe_url).send().await {
|
||||
for (key, value) in resp.headers() {
|
||||
let k = key.to_string().to_lowercase();
|
||||
let v = value.to_str().unwrap_or("").to_string();
|
||||
|
||||
// Look for technology indicators
|
||||
if k == "x-powered-by" || k == "server" || k == "x-generator" {
|
||||
if !result.technologies.contains(&v) && !extra_technologies.contains(&v) {
|
||||
// Look for technology indicators
|
||||
if (k == "x-powered-by" || k == "server" || k == "x-generator")
|
||||
&& !result.technologies.contains(&v)
|
||||
&& !extra_technologies.contains(&v)
|
||||
{
|
||||
extra_technologies.push(v.clone());
|
||||
}
|
||||
extra_headers.insert(format!("{probe_url} -> {k}"), v);
|
||||
}
|
||||
extra_headers.insert(format!("{probe_url} -> {k}"), v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut all_technologies = result.technologies.clone();
|
||||
all_technologies.extend(extra_technologies);
|
||||
all_technologies.dedup();
|
||||
let mut all_technologies = result.technologies.clone();
|
||||
all_technologies.extend(extra_technologies);
|
||||
all_technologies.dedup();
|
||||
|
||||
let tech_count = all_technologies.len();
|
||||
info!(url, technologies = tech_count, "Recon complete");
|
||||
let tech_count = all_technologies.len();
|
||||
info!(url, technologies = tech_count, "Recon complete");
|
||||
|
||||
Ok(PentestToolResult {
|
||||
summary: format!(
|
||||
"Recon complete for {url}. Detected {} technologies. Server: {}.",
|
||||
tech_count,
|
||||
result.server.as_deref().unwrap_or("unknown")
|
||||
),
|
||||
findings: Vec::new(), // Recon produces data, not findings
|
||||
data: json!({
|
||||
"base_url": url,
|
||||
"server": result.server,
|
||||
"technologies": all_technologies,
|
||||
"interesting_headers": result.interesting_headers,
|
||||
"extra_headers": extra_headers,
|
||||
"open_ports": result.open_ports,
|
||||
}),
|
||||
})
|
||||
Ok(PentestToolResult {
|
||||
summary: format!(
|
||||
"Recon complete for {url}. Detected {} technologies. Server: {}.",
|
||||
tech_count,
|
||||
result.server.as_deref().unwrap_or("unknown")
|
||||
),
|
||||
findings: Vec::new(), // Recon produces data, not findings
|
||||
data: json!({
|
||||
"base_url": url,
|
||||
"server": result.server,
|
||||
"technologies": all_technologies,
|
||||
"interesting_headers": result.interesting_headers,
|
||||
"extra_headers": extra_headers,
|
||||
"open_ports": result.open_ports,
|
||||
}),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user