use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "lowercase")]
pub enum Severity {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ArgsFilter {
#[serde(skip_serializing_if = "Option::is_none")]
pub fd_path_pattern: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub arg_contains: Option<String>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ClusterDefinition {
pub name: String,
pub description: String,
pub syscalls: Vec<String>,
pub expected_for_transpiler: bool,
pub anomaly_threshold: f64,
pub severity: Severity,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub args_filter: Option<ArgsFilter>,
}
impl ClusterDefinition {
pub fn is_anomalous(&self, baseline_count: usize, current_count: usize) -> bool {
if baseline_count == 0 {
return current_count > 0;
}
let delta = current_count as f64 - baseline_count as f64;
let pct_change = delta / baseline_count as f64;
pct_change.abs() > self.anomaly_threshold
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_anomalous_new_cluster() {
let cluster = ClusterDefinition {
name: "Networking".to_string(),
description: "HTTP/network calls".to_string(),
syscalls: vec!["socket".to_string()],
expected_for_transpiler: false,
anomaly_threshold: 0.0,
severity: Severity::Critical,
args_filter: None,
};
assert!(cluster.is_anomalous(0, 1)); assert!(!cluster.is_anomalous(0, 0)); }
#[test]
fn test_is_anomalous_threshold() {
let cluster = ClusterDefinition {
name: "MemoryAllocation".to_string(),
description: "Heap management".to_string(),
syscalls: vec!["mmap".to_string()],
expected_for_transpiler: true,
anomaly_threshold: 0.50, severity: Severity::Medium,
args_filter: None,
};
assert!(!cluster.is_anomalous(100, 140)); assert!(cluster.is_anomalous(100, 160)); assert!(cluster.is_anomalous(100, 40)); }
#[test]
fn test_severity_ordering() {
assert!(Severity::Critical > Severity::High);
assert!(Severity::High > Severity::Medium);
assert!(Severity::Medium > Severity::Low);
}
}