- Run cargo fmt on all crates - Fix regex patterns using unsupported lookahead in patterns.rs - Replace unwrap() calls with compile_regex() helper - Fix never type fallback in GitHub tracker - Fix redundant field name in findings page - Allow enum_variant_names for Dioxus Route enum - Fix &mut Vec -> &mut [T] clippy lint in sbom.rs - Mark unused-but-intended APIs with #[allow(dead_code)] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
106 lines
3.4 KiB
Rust
106 lines
3.4 KiB
Rust
use mongodb::bson::doc;
|
|
use tokio_cron_scheduler::{Job, JobScheduler};
|
|
|
|
use compliance_core::models::ScanTrigger;
|
|
|
|
use crate::agent::ComplianceAgent;
|
|
use crate::error::AgentError;
|
|
|
|
pub async fn start_scheduler(agent: &ComplianceAgent) -> Result<(), AgentError> {
|
|
let sched = JobScheduler::new()
|
|
.await
|
|
.map_err(|e| AgentError::Scheduler(format!("Failed to create scheduler: {e}")))?;
|
|
|
|
// Periodic scan job
|
|
let scan_agent = agent.clone();
|
|
let scan_schedule = agent.config.scan_schedule.clone();
|
|
let scan_job = Job::new_async(scan_schedule.as_str(), move |_uuid, _lock| {
|
|
let agent = scan_agent.clone();
|
|
Box::pin(async move {
|
|
tracing::info!("Scheduled scan triggered");
|
|
scan_all_repos(&agent).await;
|
|
})
|
|
})
|
|
.map_err(|e| AgentError::Scheduler(format!("Failed to create scan job: {e}")))?;
|
|
sched
|
|
.add(scan_job)
|
|
.await
|
|
.map_err(|e| AgentError::Scheduler(format!("Failed to add scan job: {e}")))?;
|
|
|
|
// CVE monitor job (daily)
|
|
let cve_agent = agent.clone();
|
|
let cve_schedule = agent.config.cve_monitor_schedule.clone();
|
|
let cve_job = Job::new_async(cve_schedule.as_str(), move |_uuid, _lock| {
|
|
let agent = cve_agent.clone();
|
|
Box::pin(async move {
|
|
tracing::info!("CVE monitor triggered");
|
|
monitor_cves(&agent).await;
|
|
})
|
|
})
|
|
.map_err(|e| AgentError::Scheduler(format!("Failed to create CVE monitor job: {e}")))?;
|
|
sched
|
|
.add(cve_job)
|
|
.await
|
|
.map_err(|e| AgentError::Scheduler(format!("Failed to add CVE monitor job: {e}")))?;
|
|
|
|
sched
|
|
.start()
|
|
.await
|
|
.map_err(|e| AgentError::Scheduler(format!("Failed to start scheduler: {e}")))?;
|
|
|
|
tracing::info!(
|
|
"Scheduler started: scans='{}', CVE monitor='{}'",
|
|
agent.config.scan_schedule,
|
|
agent.config.cve_monitor_schedule,
|
|
);
|
|
|
|
// Keep scheduler alive
|
|
loop {
|
|
tokio::time::sleep(tokio::time::Duration::from_secs(3600)).await;
|
|
}
|
|
}
|
|
|
|
async fn scan_all_repos(agent: &ComplianceAgent) {
|
|
use futures_util::StreamExt;
|
|
|
|
let cursor = match agent.db.repositories().find(doc! {}).await {
|
|
Ok(c) => c,
|
|
Err(e) => {
|
|
tracing::error!("Failed to list repos for scheduled scan: {e}");
|
|
return;
|
|
}
|
|
};
|
|
|
|
let repos: Vec<_> = cursor.filter_map(|r| async { r.ok() }).collect().await;
|
|
|
|
for repo in repos {
|
|
let repo_id = repo.id.map(|id| id.to_hex()).unwrap_or_default();
|
|
if let Err(e) = agent.run_scan(&repo_id, ScanTrigger::Scheduled).await {
|
|
tracing::error!("Scheduled scan failed for {}: {e}", repo.name);
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn monitor_cves(agent: &ComplianceAgent) {
|
|
use futures_util::StreamExt;
|
|
|
|
// Re-scan all SBOM entries for new CVEs
|
|
let cursor = match agent.db.sbom_entries().find(doc! {}).await {
|
|
Ok(c) => c,
|
|
Err(e) => {
|
|
tracing::error!("Failed to list SBOM entries for CVE monitoring: {e}");
|
|
return;
|
|
}
|
|
};
|
|
|
|
let entries: Vec<_> = cursor.filter_map(|r| async { r.ok() }).collect().await;
|
|
|
|
if entries.is_empty() {
|
|
return;
|
|
}
|
|
|
|
tracing::info!("CVE monitor: checking {} dependencies", entries.len());
|
|
// The actual CVE checking is handled by the CveScanner in the pipeline
|
|
// This is a simplified version that just logs the activity
|
|
}
|