use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::time::Duration;
use tokio::sync::mpsc;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EventKind {
FileRead {
path: PathBuf,
sensitive: bool,
category: FileCategory,
},
FileWrite {
path: PathBuf,
diff_summary: Option<String>,
},
FileDelete {
path: PathBuf,
},
NetworkConnection {
remote_addr: String,
remote_port: u16,
domain: Option<String>,
category: NetCategory,
bytes_sent: u64,
bytes_recv: u64,
},
ProcessSpawn {
pid: u32,
name: String,
cmdline: String,
parent_pid: u32,
},
ProcessExit {
pid: u32,
exit_code: Option<i32>,
},
ShellCommand {
command: String,
working_dir: PathBuf,
risk: RiskLevel,
},
CommandComplete {
command: String,
exit_code: i32,
duration: Duration,
},
SecretAccess {
name: String,
source: SecretSource,
},
EnvVarRead {
name: String,
sensitive: bool,
},
ClipboardRead {
content_type: String,
contains_secret: bool,
},
ClipboardWrite {
content_type: String,
},
Alert {
message: String,
severity: RiskLevel,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum RiskLevel {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum FileCategory {
Source,
Config,
Secret,
Binary,
Data,
Documentation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum NetCategory {
ExpectedApi,
Telemetry,
Tracking,
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SecretSource {
File,
EnvVar,
Clipboard,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Event {
pub timestamp: DateTime<Utc>,
pub kind: EventKind,
pub risk_score: u32,
}
impl Event {
pub fn new(kind: EventKind) -> Self {
Self {
timestamp: Utc::now(),
kind,
risk_score: 0,
}
}
#[allow(dead_code)]
pub fn with_risk(kind: EventKind, risk_score: u32) -> Self {
Self {
timestamp: Utc::now(),
kind,
risk_score,
}
}
}
pub fn create_event_bus() -> (mpsc::Sender<Event>, mpsc::Receiver<Event>) {
mpsc::channel(10_000)
}
pub fn join_cmdline(cmdline: &[std::ffi::OsString]) -> String {
cmdline
.iter()
.map(|arg| arg.to_string_lossy().to_string())
.collect::<Vec<_>>()
.join(" ")
}
#[derive(Debug, Clone)]
pub struct AgentInfo {
pub pid: u32,
pub name: String,
pub uptime_secs: u64,
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn test_event_creation_basics() {
let e = Event::new(EventKind::FileRead {
path: PathBuf::from("/etc/passwd"),
sensitive: true,
category: crate::events::FileCategory::Config,
});
assert_eq!(e.risk_score, 0); match e.kind {
EventKind::FileRead { path, .. } => {
assert_eq!(path.to_str().unwrap(), "/etc/passwd");
}
_ => panic!("Wrong variant!"),
}
}
#[test]
fn test_event_with_risk() {
let e = Event::with_risk(
EventKind::ClipboardRead {
content_type: "text".to_string(),
contains_secret: true,
},
55,
);
assert_eq!(e.risk_score, 55);
}
}