Initial commit: Compliance Scanner Agent

Autonomous security and compliance scanning agent for git repositories.
Features: SAST (Semgrep), SBOM (Syft), CVE monitoring (OSV.dev/NVD),
GDPR/OAuth pattern detection, LLM triage, issue creation (GitHub/GitLab/Jira),
PR reviews, and Dioxus fullstack dashboard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-03-02 13:30:17 +01:00
commit 0867e401bc
97 changed files with 11750 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
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
}