1use serde::{Deserialize, Serialize};
5use std::borrow::Cow;
6use std::collections::HashMap;
7use std::sync::Arc;
8
9use crate::Severity;
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
13pub struct RawMatch {
14 #[serde(with = "serde_arc_str")]
16 pub detector_id: Arc<str>,
17 #[serde(with = "serde_arc_str")]
19 pub detector_name: Arc<str>,
20 #[serde(with = "serde_arc_str")]
22 pub service: Arc<str>,
23 pub severity: Severity,
25 #[serde(with = "serde_arc_str")]
27 pub credential: Arc<str>,
28 pub credential_hash: String,
30 pub companions: std::collections::HashMap<String, String>,
32 pub location: MatchLocation,
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub entropy: Option<f64>,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub confidence: Option<f64>,
40}
41
42impl Eq for RawMatch {}
43
44impl PartialOrd for RawMatch {
45 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
46 Some(self.cmp(other))
47 }
48}
49
50impl Ord for RawMatch {
51 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
52 let self_conf = self.confidence.unwrap_or(0.0);
54 let other_conf = other.confidence.unwrap_or(0.0);
55
56 match other_conf.total_cmp(&self_conf) {
57 std::cmp::Ordering::Equal => {}
58 ord => return ord,
59 }
60
61 match other.severity.cmp(&self.severity) {
63 std::cmp::Ordering::Equal => {}
64 ord => return ord,
65 }
66
67 match self.detector_id.cmp(&other.detector_id) {
69 std::cmp::Ordering::Equal => self.credential.cmp(&other.credential),
70 ord => ord,
71 }
72 }
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
77pub struct MatchLocation {
78 #[serde(with = "serde_arc_str")]
80 pub source: Arc<str>,
81 #[serde(with = "serde_arc_str_opt")]
87 pub file_path: Option<Arc<str>>,
88 pub line: Option<usize>,
90 pub offset: usize,
92 #[serde(with = "serde_arc_str_opt")]
94 pub commit: Option<Arc<str>>,
95 #[serde(with = "serde_arc_str_opt")]
97 pub author: Option<Arc<str>>,
98 #[serde(with = "serde_arc_str_opt")]
100 pub date: Option<Arc<str>>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct VerifiedFinding {
106 #[serde(with = "serde_arc_str")]
108 pub detector_id: Arc<str>,
109 #[serde(with = "serde_arc_str")]
111 pub detector_name: Arc<str>,
112 #[serde(with = "serde_arc_str")]
114 pub service: Arc<str>,
115 pub severity: Severity,
117 pub credential_redacted: Cow<'static, str>,
119 pub credential_hash: String,
121 pub location: MatchLocation,
123 pub verification: VerificationResult,
125 pub metadata: HashMap<String, String>,
127 pub additional_locations: Vec<MatchLocation>,
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub confidence: Option<f64>,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
136#[serde(rename_all = "snake_case")]
137pub enum VerificationResult {
138 Live,
140 Revoked,
142 Dead,
144 RateLimited,
146 Error(String),
148 Unverifiable,
150 Skipped,
152}
153
154impl RawMatch {
155 pub fn deduplication_key(&self) -> (&str, &str) {
157 (&self.detector_id, &self.credential)
158 }
159}
160
161pub mod serde_arc_str {
162 use serde::{Deserialize, Deserializer, Serialize, Serializer};
163 use std::sync::Arc;
164
165 pub fn serialize<S>(val: &Arc<str>, serializer: S) -> Result<S::Ok, S::Error>
166 where
167 S: Serializer,
168 {
169 val.as_ref().serialize(serializer)
170 }
171
172 pub fn deserialize<'de, D>(deserializer: D) -> Result<Arc<str>, D::Error>
173 where
174 D: Deserializer<'de>,
175 {
176 String::deserialize(deserializer).map(Arc::from)
177 }
178}
179
180pub mod serde_arc_str_opt {
181 use serde::{Deserialize, Deserializer, Serialize, Serializer};
182 use std::sync::Arc;
183
184 pub fn serialize<S>(val: &Option<Arc<str>>, serializer: S) -> Result<S::Ok, S::Error>
185 where
186 S: Serializer,
187 {
188 val.as_ref().map(|s| s.as_ref()).serialize(serializer)
189 }
190
191 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Arc<str>>, D::Error>
192 where
193 D: Deserializer<'de>,
194 {
195 Option::<String>::deserialize(deserializer).map(|opt| opt.map(Arc::from))
196 }
197}