use serde::Deserialize;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Verdict {
Admit { canonical: String },
AdmitWith { canonical: String },
AdmitNeeds { needs: Vec<Need>, canonical: String },
Refused {
class: Class,
rule: String,
depth: u32,
reason: String,
},
}
impl Verdict {
pub fn is_admitted(&self) -> bool {
matches!(self, Verdict::Admit { .. } | Verdict::AdmitWith { .. })
}
pub fn is_refused(&self) -> bool {
matches!(self, Verdict::Refused { .. })
}
pub fn canonical(&self) -> Option<&str> {
match self {
Verdict::Admit { canonical }
| Verdict::AdmitWith { canonical }
| Verdict::AdmitNeeds { canonical, .. } => Some(canonical),
Verdict::Refused { .. } => None,
}
}
pub fn needs(&self) -> &[Need] {
match self {
Verdict::AdmitNeeds { needs, .. } => needs,
_ => &[],
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Need {
Profile(String),
Predicate(String),
Other(serde_json::Value),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Class {
BenignMiss,
CapabilityProbe,
EscapeAttempt,
Reconnaissance,
Shadowing,
Semantics,
Evasion,
Other(String),
}
impl Class {
pub fn is_adversarial(&self) -> bool {
!matches!(self, Class::BenignMiss | Class::Semantics)
}
pub fn as_str(&self) -> &str {
match self {
Class::BenignMiss => "benign_miss",
Class::CapabilityProbe => "capability_probe",
Class::EscapeAttempt => "escape_attempt",
Class::Reconnaissance => "reconnaissance",
Class::Shadowing => "shadowing",
Class::Semantics => "semantics",
Class::Evasion => "evasion",
Class::Other(s) => s,
}
}
}
impl std::fmt::Display for Class {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl From<&str> for Class {
fn from(s: &str) -> Self {
match s {
"benign_miss" => Class::BenignMiss,
"capability_probe" => Class::CapabilityProbe,
"escape_attempt" => Class::EscapeAttempt,
"reconnaissance" => Class::Reconnaissance,
"shadowing" => Class::Shadowing,
"semantics" => Class::Semantics,
"evasion" => Class::Evasion,
other => Class::Other(other.to_string()),
}
}
}
#[derive(Debug, Deserialize)]
pub(crate) struct Response {
pub(crate) id: Option<u64>,
#[serde(default)]
pub(crate) verdict: Option<String>,
#[serde(default)]
pub(crate) canonical: Option<String>,
#[serde(default)]
pub(crate) needs: Option<Vec<serde_json::Value>>,
#[serde(default)]
pub(crate) class: Option<String>,
#[serde(default)]
pub(crate) rule: Option<String>,
#[serde(default)]
pub(crate) depth: Option<u32>,
#[serde(default)]
pub(crate) reason: Option<String>,
#[serde(default)]
pub(crate) ok: Option<bool>,
#[serde(default)]
pub(crate) profiles: Option<Vec<String>>,
#[serde(default)]
pub(crate) error: Option<String>,
#[serde(default)]
pub(crate) detail: Option<String>,
}
#[derive(Debug, Deserialize)]
pub(crate) struct Hello {
pub(crate) hello: String,
pub(crate) protocol: u32,
#[serde(default)]
pub(crate) engine: Option<String>,
#[serde(default)]
pub(crate) version: Option<String>,
#[serde(default)]
pub(crate) profiles: Vec<String>,
}
pub(crate) fn need_from_json(v: serde_json::Value) -> Need {
if let Some(p) = v.get("profile").and_then(|p| p.as_str()) {
Need::Profile(p.to_string())
} else if let Some(p) = v.get("predicate").and_then(|p| p.as_str()) {
Need::Predicate(p.to_string())
} else {
Need::Other(v)
}
}