use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::Arc;
use crate::Severity;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RawMatch {
#[serde(with = "serde_arc_str")]
pub detector_id: Arc<str>,
#[serde(with = "serde_arc_str")]
pub detector_name: Arc<str>,
#[serde(with = "serde_arc_str")]
pub service: Arc<str>,
pub severity: Severity,
#[serde(with = "serde_arc_str")]
pub credential: Arc<str>,
pub credential_hash: String,
pub companions: std::collections::HashMap<String, String>,
pub location: MatchLocation,
#[serde(skip_serializing_if = "Option::is_none")]
pub entropy: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence: Option<f64>,
}
impl Eq for RawMatch {}
impl PartialOrd for RawMatch {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for RawMatch {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let self_conf = self.confidence.unwrap_or(0.0);
let other_conf = other.confidence.unwrap_or(0.0);
match other_conf.total_cmp(&self_conf) {
std::cmp::Ordering::Equal => {}
ord => return ord,
}
match other.severity.cmp(&self.severity) {
std::cmp::Ordering::Equal => {}
ord => return ord,
}
match self.detector_id.cmp(&other.detector_id) {
std::cmp::Ordering::Equal => self.credential.cmp(&other.credential),
ord => ord,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MatchLocation {
#[serde(with = "serde_arc_str")]
pub source: Arc<str>,
#[serde(with = "serde_arc_str_opt")]
pub file_path: Option<Arc<str>>,
pub line: Option<usize>,
pub offset: usize,
#[serde(with = "serde_arc_str_opt")]
pub commit: Option<Arc<str>>,
#[serde(with = "serde_arc_str_opt")]
pub author: Option<Arc<str>>,
#[serde(with = "serde_arc_str_opt")]
pub date: Option<Arc<str>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerifiedFinding {
#[serde(with = "serde_arc_str")]
pub detector_id: Arc<str>,
#[serde(with = "serde_arc_str")]
pub detector_name: Arc<str>,
#[serde(with = "serde_arc_str")]
pub service: Arc<str>,
pub severity: Severity,
pub credential_redacted: Cow<'static, str>,
pub credential_hash: String,
pub location: MatchLocation,
pub verification: VerificationResult,
pub metadata: HashMap<String, String>,
pub additional_locations: Vec<MatchLocation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum VerificationResult {
Live,
Revoked,
Dead,
RateLimited,
Error(String),
Unverifiable,
Skipped,
}
impl RawMatch {
pub fn deduplication_key(&self) -> (&str, &str) {
(&self.detector_id, &self.credential)
}
}
pub mod serde_arc_str {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::sync::Arc;
pub fn serialize<S>(val: &Arc<str>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
val.as_ref().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Arc<str>, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer).map(Arc::from)
}
}
pub mod serde_arc_str_opt {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::sync::Arc;
pub fn serialize<S>(val: &Option<Arc<str>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
val.as_ref().map(|s| s.as_ref()).serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Arc<str>>, D::Error>
where
D: Deserializer<'de>,
{
Option::<String>::deserialize(deserializer).map(|opt| opt.map(Arc::from))
}
}