use std::time::Instant;
use chrono::Utc;
use crate::catalog::security_audit_report_uri;
use crate::error::Result;
use crate::evidence::evidence_status;
use crate::models::{
DependencyAuditSummary, DependencyInventorySummary, EvidenceStatus, SecurityAuditCheck,
SecurityAuditReport,
};
use crate::release_gate::resolve_workspace_dir;
use crate::security_audit::{
build_security_audit_checks, dependency_audit_summary, dependency_inventory_summary,
resolve_security_audit_mode,
};
use super::AxiomSync;
struct SecurityAuditRuntimeState {
workspace_dir: String,
inventory: DependencyInventorySummary,
dependency_audit: DependencyAuditSummary,
checks: Vec<SecurityAuditCheck>,
passed: bool,
status: EvidenceStatus,
}
impl AxiomSync {
pub fn run_security_audit(&self, workspace_dir: Option<&str>) -> Result<SecurityAuditReport> {
self.run_security_audit_with_mode(workspace_dir, None)
}
pub fn run_security_audit_with_mode(
&self,
workspace_dir: Option<&str>,
mode: Option<&str>,
) -> Result<SecurityAuditReport> {
let request_id = uuid::Uuid::new_v4().to_string();
let started = Instant::now();
let workspace_input = workspace_dir.unwrap_or(".").to_string();
let mode_input = mode.unwrap_or("offline").to_string();
let output = (|| -> Result<SecurityAuditReport> {
let runtime =
Self::collect_security_audit_runtime_state(&workspace_input, &mode_input)?;
let report_id = uuid::Uuid::new_v4().to_string();
let report_uri = security_audit_report_uri(&report_id)?;
let report = SecurityAuditReport {
report_id,
created_at: Utc::now().to_rfc3339(),
workspace_dir: runtime.workspace_dir,
passed: runtime.passed,
status: runtime.status,
inventory: runtime.inventory,
dependency_audit: runtime.dependency_audit,
checks: runtime.checks,
report_uri: report_uri.to_string(),
};
self.fs
.write(&report_uri, &serde_json::to_string_pretty(&report)?, true)?;
Ok(report)
})();
match output {
Ok(report) => {
self.log_request_status(
request_id,
"security.audit",
report.status.as_str(),
started,
None,
Some(serde_json::json!({
"workspace_dir": report.workspace_dir,
"passed": report.passed,
"report_uri": report.report_uri,
"advisories_found": report.dependency_audit.advisories_found,
"audit_status": report.dependency_audit.status,
"audit_mode": report.dependency_audit.mode,
})),
);
Ok(report)
}
Err(err) => {
self.log_request_error(
request_id,
"security.audit",
started,
None,
&err,
Some(serde_json::json!({
"workspace_dir": workspace_input,
"audit_mode": mode_input,
})),
);
Err(err)
}
}
}
fn collect_security_audit_runtime_state(
workspace_input: &str,
mode_input: &str,
) -> Result<SecurityAuditRuntimeState> {
let workspace_path = resolve_workspace_dir(Some(workspace_input))?;
let workspace_dir = workspace_path.display().to_string();
let mode = resolve_security_audit_mode(Some(mode_input))?;
let inventory = dependency_inventory_summary(&workspace_path);
let dependency_audit = dependency_audit_summary(&workspace_path, mode);
let checks = build_security_audit_checks(&inventory, &dependency_audit);
let passed = checks.iter().all(|check| check.passed);
let status = evidence_status(passed);
Ok(SecurityAuditRuntimeState {
workspace_dir,
inventory,
dependency_audit,
checks,
passed,
status,
})
}
}