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,77 @@
use std::sync::Arc;
use compliance_core::models::Finding;
use compliance_core::traits::issue_tracker::ReviewComment;
use crate::error::AgentError;
use crate::llm::LlmClient;
const PR_REVIEW_SYSTEM_PROMPT: &str = r#"You are a security-focused code reviewer. Given a list of security findings in a PR diff, generate concise review comments. Each comment should:
1. Briefly explain the issue
2. Suggest a specific fix
3. Reference the relevant security standard (CWE, OWASP) if applicable
Be constructive and professional. Return JSON array:
[{"path": "file.rs", "line": 42, "body": "..."}]"#;
pub async fn generate_pr_review(
llm: &Arc<LlmClient>,
findings: &[Finding],
) -> Result<(String, Vec<ReviewComment>), AgentError> {
if findings.is_empty() {
return Ok(("No security issues found in this PR.".to_string(), Vec::new()));
}
let findings_text: Vec<String> = findings
.iter()
.map(|f| {
format!(
"- [{severity}] {title} in {file}:{line}\n Code: {code}\n Rule: {rule}",
severity = f.severity,
title = f.title,
file = f.file_path.as_deref().unwrap_or("unknown"),
line = f.line_number.map(|n| n.to_string()).unwrap_or_else(|| "?".to_string()),
code = f.code_snippet.as_deref().unwrap_or("N/A"),
rule = f.rule_id.as_deref().unwrap_or("N/A"),
)
})
.collect();
let user_prompt = format!(
"Generate review comments for these {} findings:\n{}",
findings.len(),
findings_text.join("\n"),
);
let response = llm.chat(PR_REVIEW_SYSTEM_PROMPT, &user_prompt, Some(0.3)).await?;
// Parse comments from LLM response
let comments: Vec<ReviewComment> = serde_json::from_str::<Vec<PrComment>>(&response)
.unwrap_or_default()
.into_iter()
.map(|c| ReviewComment {
path: c.path,
line: c.line,
body: c.body,
})
.collect();
let summary = format!(
"## Security Review\n\nFound **{}** potential security issue(s) in this PR.\n\n{}",
findings.len(),
findings
.iter()
.map(|f| format!("- **[{}]** {} in `{}`", f.severity, f.title, f.file_path.as_deref().unwrap_or("unknown")))
.collect::<Vec<_>>()
.join("\n"),
);
Ok((summary, comments))
}
#[derive(serde::Deserialize)]
struct PrComment {
path: String,
line: u32,
body: String,
}