All checks were successful
CI / Format (push) Successful in 4s
CI / Clippy (push) Successful in 4m19s
CI / Security Audit (push) Successful in 1m44s
CI / Tests (push) Successful in 5m15s
CI / Detect Changes (push) Successful in 5s
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
151 lines
4.8 KiB
Rust
151 lines
4.8 KiB
Rust
use futures_util::StreamExt;
|
|
use mongodb::bson::doc;
|
|
|
|
use compliance_core::models::dast::DastTarget;
|
|
use compliance_core::models::finding::Finding;
|
|
use compliance_core::models::pentest::CodeContextHint;
|
|
use compliance_core::models::sbom::SbomEntry;
|
|
|
|
use super::orchestrator::PentestOrchestrator;
|
|
|
|
impl PentestOrchestrator {
|
|
/// Fetch SAST findings, SBOM entries (with CVEs), and code graph entry points
|
|
/// for the repo linked to this DAST target.
|
|
pub(crate) async fn gather_repo_context(
|
|
&self,
|
|
target: &DastTarget,
|
|
) -> (Vec<Finding>, Vec<SbomEntry>, Vec<CodeContextHint>) {
|
|
let Some(repo_id) = &target.repo_id else {
|
|
return (Vec::new(), Vec::new(), Vec::new());
|
|
};
|
|
|
|
let sast_findings = self.fetch_sast_findings(repo_id).await;
|
|
let sbom_entries = self.fetch_vulnerable_sbom(repo_id).await;
|
|
let code_context = self.fetch_code_context(repo_id, &sast_findings).await;
|
|
|
|
tracing::info!(
|
|
repo_id,
|
|
sast_findings = sast_findings.len(),
|
|
vulnerable_deps = sbom_entries.len(),
|
|
code_hints = code_context.len(),
|
|
"Gathered code-awareness context for pentest"
|
|
);
|
|
|
|
(sast_findings, sbom_entries, code_context)
|
|
}
|
|
|
|
/// Fetch open/triaged SAST findings for the repo (not false positives or resolved)
|
|
async fn fetch_sast_findings(&self, repo_id: &str) -> Vec<Finding> {
|
|
let cursor = self
|
|
.db
|
|
.findings()
|
|
.find(doc! {
|
|
"repo_id": repo_id,
|
|
"status": { "$in": ["open", "triaged"] },
|
|
})
|
|
.sort(doc! { "severity": -1 })
|
|
.limit(100)
|
|
.await;
|
|
|
|
match cursor {
|
|
Ok(mut c) => {
|
|
let mut results = Vec::new();
|
|
while let Some(Ok(f)) = c.next().await {
|
|
results.push(f);
|
|
}
|
|
results
|
|
}
|
|
Err(e) => {
|
|
tracing::warn!("Failed to fetch SAST findings for pentest: {e}");
|
|
Vec::new()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Fetch SBOM entries that have known vulnerabilities
|
|
async fn fetch_vulnerable_sbom(&self, repo_id: &str) -> Vec<SbomEntry> {
|
|
let cursor = self
|
|
.db
|
|
.sbom_entries()
|
|
.find(doc! {
|
|
"repo_id": repo_id,
|
|
"known_vulnerabilities": { "$exists": true, "$ne": [] },
|
|
})
|
|
.limit(50)
|
|
.await;
|
|
|
|
match cursor {
|
|
Ok(mut c) => {
|
|
let mut results = Vec::new();
|
|
while let Some(Ok(e)) = c.next().await {
|
|
results.push(e);
|
|
}
|
|
results
|
|
}
|
|
Err(e) => {
|
|
tracing::warn!("Failed to fetch vulnerable SBOM entries: {e}");
|
|
Vec::new()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Build CodeContextHint objects from the code knowledge graph.
|
|
/// Maps entry points to their source files and links SAST findings.
|
|
async fn fetch_code_context(
|
|
&self,
|
|
repo_id: &str,
|
|
sast_findings: &[Finding],
|
|
) -> Vec<CodeContextHint> {
|
|
// Get entry point nodes from the code graph
|
|
let cursor = self
|
|
.db
|
|
.graph_nodes()
|
|
.find(doc! {
|
|
"repo_id": repo_id,
|
|
"is_entry_point": true,
|
|
})
|
|
.limit(50)
|
|
.await;
|
|
|
|
let nodes = match cursor {
|
|
Ok(mut c) => {
|
|
let mut results = Vec::new();
|
|
while let Some(Ok(n)) = c.next().await {
|
|
results.push(n);
|
|
}
|
|
results
|
|
}
|
|
Err(_) => return Vec::new(),
|
|
};
|
|
|
|
// Build hints by matching graph nodes to SAST findings by file path
|
|
nodes
|
|
.into_iter()
|
|
.map(|node| {
|
|
// Find SAST findings in the same file
|
|
let linked_vulns: Vec<String> = sast_findings
|
|
.iter()
|
|
.filter(|f| f.file_path.as_deref() == Some(&node.file_path))
|
|
.map(|f| {
|
|
format!(
|
|
"[{}] {}: {} (line {})",
|
|
f.severity,
|
|
f.scanner,
|
|
f.title,
|
|
f.line_number.unwrap_or(0)
|
|
)
|
|
})
|
|
.collect();
|
|
|
|
CodeContextHint {
|
|
endpoint_pattern: node.qualified_name.clone(),
|
|
handler_function: node.name.clone(),
|
|
file_path: node.file_path.clone(),
|
|
code_snippet: String::new(), // Could fetch from embeddings
|
|
known_vulnerabilities: linked_vulns,
|
|
}
|
|
})
|
|
.collect()
|
|
}
|
|
}
|