1use serde::{Deserialize, Serialize};
2
3use crate::ir::{data_surface::TaintPath, SourceLocation};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct Finding {
8 pub rule_id: String,
10 pub rule_name: String,
12 pub severity: Severity,
14 pub confidence: Confidence,
16 pub attack_category: AttackCategory,
18 pub message: String,
20 pub location: Option<SourceLocation>,
22 pub evidence: Vec<Evidence>,
24 pub taint_path: Option<TaintPath>,
26 pub remediation: Option<String>,
28 pub cwe_id: Option<String>,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
33#[serde(rename_all = "lowercase")]
34pub enum Severity {
35 Info,
36 Low,
37 Medium,
38 High,
39 Critical,
40}
41
42impl Severity {
43 pub fn from_str_lenient(s: &str) -> Option<Self> {
44 match s.to_lowercase().as_str() {
45 "info" => Some(Self::Info),
46 "low" => Some(Self::Low),
47 "medium" | "med" => Some(Self::Medium),
48 "high" => Some(Self::High),
49 "critical" | "crit" => Some(Self::Critical),
50 _ => None,
51 }
52 }
53}
54
55impl std::fmt::Display for Severity {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 match self {
58 Self::Info => write!(f, "info"),
59 Self::Low => write!(f, "low"),
60 Self::Medium => write!(f, "medium"),
61 Self::High => write!(f, "high"),
62 Self::Critical => write!(f, "critical"),
63 }
64 }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
68#[serde(rename_all = "lowercase")]
69pub enum Confidence {
70 Low,
71 Medium,
72 High,
73}
74
75impl std::fmt::Display for Confidence {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 match self {
78 Self::Low => write!(f, "low"),
79 Self::Medium => write!(f, "medium"),
80 Self::High => write!(f, "high"),
81 }
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
86#[serde(rename_all = "snake_case")]
87pub enum AttackCategory {
88 CommandInjection,
89 CodeInjection,
90 CredentialExfiltration,
91 Ssrf,
92 ArbitraryFileAccess,
93 SupplyChain,
94 SelfModification,
95 PromptInjectionSurface,
96 ExcessivePermissions,
97 DataExfiltration,
98}
99
100impl std::fmt::Display for AttackCategory {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 match self {
103 Self::CommandInjection => write!(f, "Command Injection"),
104 Self::CodeInjection => write!(f, "Code Injection"),
105 Self::CredentialExfiltration => write!(f, "Credential Exfiltration"),
106 Self::Ssrf => write!(f, "SSRF"),
107 Self::ArbitraryFileAccess => write!(f, "Arbitrary File Access"),
108 Self::SupplyChain => write!(f, "Supply Chain"),
109 Self::SelfModification => write!(f, "Self-Modification"),
110 Self::PromptInjectionSurface => write!(f, "Prompt Injection Surface"),
111 Self::ExcessivePermissions => write!(f, "Excessive Permissions"),
112 Self::DataExfiltration => write!(f, "Data Exfiltration"),
113 }
114 }
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct Evidence {
120 pub description: String,
121 pub location: Option<SourceLocation>,
122 pub snippet: Option<String>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct RuleMetadata {
128 pub id: String,
129 pub name: String,
130 pub description: String,
131 pub default_severity: Severity,
132 pub attack_category: AttackCategory,
133 pub cwe_id: Option<String>,
134}