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:
18
compliance-core/Cargo.toml
Normal file
18
compliance-core/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "compliance-core"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
sha2 = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
secrecy = { workspace = true }
|
||||
mongodb = { workspace = true }
|
||||
34
compliance-core/src/config.rs
Normal file
34
compliance-core/src/config.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use secrecy::SecretString;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AgentConfig {
|
||||
pub mongodb_uri: String,
|
||||
pub mongodb_database: String,
|
||||
pub litellm_url: String,
|
||||
pub litellm_api_key: SecretString,
|
||||
pub litellm_model: String,
|
||||
pub github_token: Option<SecretString>,
|
||||
pub github_webhook_secret: Option<SecretString>,
|
||||
pub gitlab_url: Option<String>,
|
||||
pub gitlab_token: Option<SecretString>,
|
||||
pub gitlab_webhook_secret: Option<SecretString>,
|
||||
pub jira_url: Option<String>,
|
||||
pub jira_email: Option<String>,
|
||||
pub jira_api_token: Option<SecretString>,
|
||||
pub jira_project_key: Option<String>,
|
||||
pub searxng_url: Option<String>,
|
||||
pub nvd_api_key: Option<SecretString>,
|
||||
pub agent_port: u16,
|
||||
pub scan_schedule: String,
|
||||
pub cve_monitor_schedule: String,
|
||||
pub git_clone_base_path: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DashboardConfig {
|
||||
pub mongodb_uri: String,
|
||||
pub mongodb_database: String,
|
||||
pub agent_api_url: String,
|
||||
pub dashboard_port: u16,
|
||||
}
|
||||
41
compliance-core/src/error.rs
Normal file
41
compliance-core/src/error.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CoreError {
|
||||
#[error("Database error: {0}")]
|
||||
Database(#[from] mongodb::error::Error),
|
||||
|
||||
#[error("Serialization error: {0}")]
|
||||
Serialization(#[from] serde_json::Error),
|
||||
|
||||
#[error("Git error: {0}")]
|
||||
Git(String),
|
||||
|
||||
#[error("Scanner error: {source}")]
|
||||
Scanner {
|
||||
scanner: String,
|
||||
#[source]
|
||||
source: Box<dyn std::error::Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("LLM error: {0}")]
|
||||
Llm(String),
|
||||
|
||||
#[error("Issue tracker error: {0}")]
|
||||
IssueTracker(String),
|
||||
|
||||
#[error("HTTP error: {0}")]
|
||||
Http(String),
|
||||
|
||||
#[error("Configuration error: {0}")]
|
||||
Config(String),
|
||||
|
||||
#[error("IO error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error("Not found: {0}")]
|
||||
NotFound(String),
|
||||
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
}
|
||||
7
compliance-core/src/lib.rs
Normal file
7
compliance-core/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod models;
|
||||
pub mod traits;
|
||||
|
||||
pub use config::{AgentConfig, DashboardConfig};
|
||||
pub use error::CoreError;
|
||||
46
compliance-core/src/models/cve.rs
Normal file
46
compliance-core/src/models/cve.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum CveSource {
|
||||
Osv,
|
||||
Nvd,
|
||||
SearXNG,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CveAlert {
|
||||
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
|
||||
pub id: Option<mongodb::bson::oid::ObjectId>,
|
||||
pub cve_id: String,
|
||||
pub repo_id: String,
|
||||
pub affected_package: String,
|
||||
pub affected_version: String,
|
||||
pub source: CveSource,
|
||||
pub severity: Option<String>,
|
||||
pub cvss_score: Option<f64>,
|
||||
pub summary: Option<String>,
|
||||
pub llm_impact_summary: Option<String>,
|
||||
pub references: Vec<String>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl CveAlert {
|
||||
pub fn new(cve_id: String, repo_id: String, affected_package: String, affected_version: String, source: CveSource) -> Self {
|
||||
Self {
|
||||
id: None,
|
||||
cve_id,
|
||||
repo_id,
|
||||
affected_package,
|
||||
affected_version,
|
||||
source,
|
||||
severity: None,
|
||||
cvss_score: None,
|
||||
summary: None,
|
||||
llm_impact_summary: None,
|
||||
references: Vec::new(),
|
||||
created_at: Utc::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
115
compliance-core/src/models/finding.rs
Normal file
115
compliance-core/src/models/finding.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::scan::ScanType;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Severity {
|
||||
Info,
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Critical,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Severity {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Info => write!(f, "info"),
|
||||
Self::Low => write!(f, "low"),
|
||||
Self::Medium => write!(f, "medium"),
|
||||
Self::High => write!(f, "high"),
|
||||
Self::Critical => write!(f, "critical"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum FindingStatus {
|
||||
Open,
|
||||
Triaged,
|
||||
FalsePositive,
|
||||
Resolved,
|
||||
Ignored,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FindingStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Open => write!(f, "open"),
|
||||
Self::Triaged => write!(f, "triaged"),
|
||||
Self::FalsePositive => write!(f, "false_positive"),
|
||||
Self::Resolved => write!(f, "resolved"),
|
||||
Self::Ignored => write!(f, "ignored"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Finding {
|
||||
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
|
||||
pub id: Option<mongodb::bson::oid::ObjectId>,
|
||||
pub repo_id: String,
|
||||
pub fingerprint: String,
|
||||
pub scanner: String,
|
||||
pub scan_type: ScanType,
|
||||
pub rule_id: Option<String>,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub severity: Severity,
|
||||
pub confidence: Option<f64>,
|
||||
pub cwe: Option<String>,
|
||||
pub cve: Option<String>,
|
||||
pub cvss_score: Option<f64>,
|
||||
pub file_path: Option<String>,
|
||||
pub line_number: Option<u32>,
|
||||
pub code_snippet: Option<String>,
|
||||
pub remediation: Option<String>,
|
||||
pub suggested_fix: Option<String>,
|
||||
pub status: FindingStatus,
|
||||
pub tracker_issue_url: Option<String>,
|
||||
pub scan_run_id: Option<String>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl Finding {
|
||||
pub fn new(
|
||||
repo_id: String,
|
||||
fingerprint: String,
|
||||
scanner: String,
|
||||
scan_type: ScanType,
|
||||
title: String,
|
||||
description: String,
|
||||
severity: Severity,
|
||||
) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id: None,
|
||||
repo_id,
|
||||
fingerprint,
|
||||
scanner,
|
||||
scan_type,
|
||||
rule_id: None,
|
||||
title,
|
||||
description,
|
||||
severity,
|
||||
confidence: None,
|
||||
cwe: None,
|
||||
cve: None,
|
||||
cvss_score: None,
|
||||
file_path: None,
|
||||
line_number: None,
|
||||
code_snippet: None,
|
||||
remediation: None,
|
||||
suggested_fix: None,
|
||||
status: FindingStatus::Open,
|
||||
tracker_issue_url: None,
|
||||
scan_run_id: None,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
77
compliance-core/src/models/issue.rs
Normal file
77
compliance-core/src/models/issue.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum TrackerType {
|
||||
GitHub,
|
||||
GitLab,
|
||||
Jira,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TrackerType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::GitHub => write!(f, "github"),
|
||||
Self::GitLab => write!(f, "gitlab"),
|
||||
Self::Jira => write!(f, "jira"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum IssueStatus {
|
||||
Open,
|
||||
InProgress,
|
||||
Closed,
|
||||
Resolved,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for IssueStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Open => write!(f, "open"),
|
||||
Self::InProgress => write!(f, "in_progress"),
|
||||
Self::Closed => write!(f, "closed"),
|
||||
Self::Resolved => write!(f, "resolved"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TrackerIssue {
|
||||
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
|
||||
pub id: Option<mongodb::bson::oid::ObjectId>,
|
||||
pub finding_id: String,
|
||||
pub tracker_type: TrackerType,
|
||||
pub external_id: String,
|
||||
pub external_url: String,
|
||||
pub title: String,
|
||||
pub status: IssueStatus,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl TrackerIssue {
|
||||
pub fn new(
|
||||
finding_id: String,
|
||||
tracker_type: TrackerType,
|
||||
external_id: String,
|
||||
external_url: String,
|
||||
title: String,
|
||||
) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id: None,
|
||||
finding_id,
|
||||
tracker_type,
|
||||
external_id,
|
||||
external_url,
|
||||
title,
|
||||
status: IssueStatus::Open,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
13
compliance-core/src/models/mod.rs
Normal file
13
compliance-core/src/models/mod.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
pub mod cve;
|
||||
pub mod finding;
|
||||
pub mod issue;
|
||||
pub mod repository;
|
||||
pub mod sbom;
|
||||
pub mod scan;
|
||||
|
||||
pub use cve::{CveAlert, CveSource};
|
||||
pub use finding::{Finding, FindingStatus, Severity};
|
||||
pub use issue::{IssueStatus, TrackerIssue, TrackerType};
|
||||
pub use repository::{ScanTrigger, TrackedRepository};
|
||||
pub use sbom::{SbomEntry, VulnRef};
|
||||
pub use scan::{ScanPhase, ScanRun, ScanRunStatus, ScanType};
|
||||
53
compliance-core/src/models/repository.rs
Normal file
53
compliance-core/src/models/repository.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::issue::TrackerType;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ScanTrigger {
|
||||
Scheduled,
|
||||
Webhook,
|
||||
Manual,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TrackedRepository {
|
||||
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
|
||||
pub id: Option<mongodb::bson::oid::ObjectId>,
|
||||
pub name: String,
|
||||
pub git_url: String,
|
||||
pub default_branch: String,
|
||||
pub local_path: Option<String>,
|
||||
pub scan_schedule: Option<String>,
|
||||
pub webhook_enabled: bool,
|
||||
pub tracker_type: Option<TrackerType>,
|
||||
pub tracker_owner: Option<String>,
|
||||
pub tracker_repo: Option<String>,
|
||||
pub last_scanned_commit: Option<String>,
|
||||
pub findings_count: u32,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl TrackedRepository {
|
||||
pub fn new(name: String, git_url: String) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id: None,
|
||||
name,
|
||||
git_url,
|
||||
default_branch: "main".to_string(),
|
||||
local_path: None,
|
||||
scan_schedule: None,
|
||||
webhook_enabled: false,
|
||||
tracker_type: None,
|
||||
tracker_owner: None,
|
||||
tracker_repo: None,
|
||||
last_scanned_commit: None,
|
||||
findings_count: 0,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
43
compliance-core/src/models/sbom.rs
Normal file
43
compliance-core/src/models/sbom.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct VulnRef {
|
||||
pub id: String,
|
||||
pub source: String,
|
||||
pub severity: Option<String>,
|
||||
pub url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SbomEntry {
|
||||
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
|
||||
pub id: Option<mongodb::bson::oid::ObjectId>,
|
||||
pub repo_id: String,
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub package_manager: String,
|
||||
pub license: Option<String>,
|
||||
pub purl: Option<String>,
|
||||
pub known_vulnerabilities: Vec<VulnRef>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl SbomEntry {
|
||||
pub fn new(repo_id: String, name: String, version: String, package_manager: String) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id: None,
|
||||
repo_id,
|
||||
name,
|
||||
version,
|
||||
package_manager,
|
||||
license: None,
|
||||
purl: None,
|
||||
known_vulnerabilities: Vec::new(),
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
81
compliance-core/src/models/scan.rs
Normal file
81
compliance-core/src/models/scan.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::repository::ScanTrigger;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ScanType {
|
||||
Sast,
|
||||
Sbom,
|
||||
Cve,
|
||||
Gdpr,
|
||||
OAuth,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ScanType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Sast => write!(f, "sast"),
|
||||
Self::Sbom => write!(f, "sbom"),
|
||||
Self::Cve => write!(f, "cve"),
|
||||
Self::Gdpr => write!(f, "gdpr"),
|
||||
Self::OAuth => write!(f, "oauth"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ScanRunStatus {
|
||||
Running,
|
||||
Completed,
|
||||
Failed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ScanPhase {
|
||||
ChangeDetection,
|
||||
Sast,
|
||||
SbomGeneration,
|
||||
CveScanning,
|
||||
PatternScanning,
|
||||
LlmTriage,
|
||||
IssueCreation,
|
||||
Completed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ScanRun {
|
||||
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
|
||||
pub id: Option<mongodb::bson::oid::ObjectId>,
|
||||
pub repo_id: String,
|
||||
pub trigger: ScanTrigger,
|
||||
pub commit_sha: Option<String>,
|
||||
pub status: ScanRunStatus,
|
||||
pub current_phase: ScanPhase,
|
||||
pub phases_completed: Vec<ScanPhase>,
|
||||
pub new_findings_count: u32,
|
||||
pub error_message: Option<String>,
|
||||
pub started_at: DateTime<Utc>,
|
||||
pub completed_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
impl ScanRun {
|
||||
pub fn new(repo_id: String, trigger: ScanTrigger) -> Self {
|
||||
Self {
|
||||
id: None,
|
||||
repo_id,
|
||||
trigger,
|
||||
commit_sha: None,
|
||||
status: ScanRunStatus::Running,
|
||||
current_phase: ScanPhase::ChangeDetection,
|
||||
phases_completed: Vec::new(),
|
||||
new_findings_count: 0,
|
||||
error_message: None,
|
||||
started_at: Utc::now(),
|
||||
completed_at: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
55
compliance-core/src/traits/issue_tracker.rs
Normal file
55
compliance-core/src/traits/issue_tracker.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use crate::error::CoreError;
|
||||
use crate::models::TrackerIssue;
|
||||
|
||||
#[allow(async_fn_in_trait)]
|
||||
pub trait IssueTracker: Send + Sync {
|
||||
fn name(&self) -> &str;
|
||||
|
||||
async fn create_issue(
|
||||
&self,
|
||||
owner: &str,
|
||||
repo: &str,
|
||||
title: &str,
|
||||
body: &str,
|
||||
labels: &[String],
|
||||
) -> Result<TrackerIssue, CoreError>;
|
||||
|
||||
async fn update_issue_status(
|
||||
&self,
|
||||
owner: &str,
|
||||
repo: &str,
|
||||
external_id: &str,
|
||||
status: &str,
|
||||
) -> Result<(), CoreError>;
|
||||
|
||||
async fn add_comment(
|
||||
&self,
|
||||
owner: &str,
|
||||
repo: &str,
|
||||
external_id: &str,
|
||||
body: &str,
|
||||
) -> Result<(), CoreError>;
|
||||
|
||||
async fn create_pr_review(
|
||||
&self,
|
||||
owner: &str,
|
||||
repo: &str,
|
||||
pr_number: u64,
|
||||
body: &str,
|
||||
comments: Vec<ReviewComment>,
|
||||
) -> Result<(), CoreError>;
|
||||
|
||||
async fn find_existing_issue(
|
||||
&self,
|
||||
owner: &str,
|
||||
repo: &str,
|
||||
fingerprint: &str,
|
||||
) -> Result<Option<TrackerIssue>, CoreError>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ReviewComment {
|
||||
pub path: String,
|
||||
pub line: u32,
|
||||
pub body: String,
|
||||
}
|
||||
5
compliance-core/src/traits/mod.rs
Normal file
5
compliance-core/src/traits/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub mod issue_tracker;
|
||||
pub mod scanner;
|
||||
|
||||
pub use issue_tracker::IssueTracker;
|
||||
pub use scanner::{ScanOutput, Scanner};
|
||||
17
compliance-core/src/traits/scanner.rs
Normal file
17
compliance-core/src/traits/scanner.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::error::CoreError;
|
||||
use crate::models::{Finding, SbomEntry, ScanType};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ScanOutput {
|
||||
pub findings: Vec<Finding>,
|
||||
pub sbom_entries: Vec<SbomEntry>,
|
||||
}
|
||||
|
||||
#[allow(async_fn_in_trait)]
|
||||
pub trait Scanner: Send + Sync {
|
||||
fn name(&self) -> &str;
|
||||
fn scan_type(&self) -> ScanType;
|
||||
async fn scan(&self, repo_path: &Path, repo_id: &str) -> Result<ScanOutput, CoreError>;
|
||||
}
|
||||
Reference in New Issue
Block a user