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, pub repo_id: String, pub fingerprint: String, pub scanner: String, pub scan_type: ScanType, pub rule_id: Option, pub title: String, pub description: String, pub severity: Severity, pub confidence: Option, pub cwe: Option, pub cve: Option, pub cvss_score: Option, pub file_path: Option, pub line_number: Option, pub code_snippet: Option, pub remediation: Option, pub suggested_fix: Option, pub status: FindingStatus, pub tracker_issue_url: Option, pub scan_run_id: Option, /// LLM triage action and reasoning pub triage_action: Option, pub triage_rationale: Option, /// Developer feedback on finding quality pub developer_feedback: Option, #[serde(with = "super::serde_helpers::bson_datetime")] pub created_at: DateTime, #[serde(with = "super::serde_helpers::bson_datetime")] pub updated_at: DateTime, } 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, triage_action: None, triage_rationale: None, developer_feedback: None, created_at: now, updated_at: now, } } }