use std::path::Path; use compliance_core::CoreError; pub(super) struct AuditVuln { pub package: String, pub id: String, pub url: String, } #[tracing::instrument(skip_all)] pub(super) async fn run_cargo_audit( repo_path: &Path, _repo_id: &str, ) -> Result, CoreError> { let cargo_lock = repo_path.join("Cargo.lock"); if !cargo_lock.exists() { return Ok(Vec::new()); } let output = tokio::process::Command::new("cargo") .args(["audit", "--json"]) .current_dir(repo_path) .env("RUSTC_WRAPPER", "") .output() .await .map_err(|e| CoreError::Scanner { scanner: "cargo-audit".to_string(), source: Box::new(e), })?; let result: CargoAuditOutput = serde_json::from_slice(&output.stdout).unwrap_or_else(|_| CargoAuditOutput { vulnerabilities: CargoAuditVulns { list: Vec::new() }, }); let vulns = result .vulnerabilities .list .into_iter() .map(|v| AuditVuln { package: v.advisory.package, id: v.advisory.id, url: v.advisory.url, }) .collect(); Ok(vulns) } // Cargo audit types #[derive(serde::Deserialize)] struct CargoAuditOutput { vulnerabilities: CargoAuditVulns, } #[derive(serde::Deserialize)] struct CargoAuditVulns { list: Vec, } #[derive(serde::Deserialize)] struct CargoAuditEntry { advisory: CargoAuditAdvisory, } #[derive(serde::Deserialize)] struct CargoAuditAdvisory { id: String, package: String, url: String, }