use crate::types::{Cookie, Result};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub mod cookie_decoder;
pub mod evidence_collector;
pub mod google_analytics;
pub mod ios_binary;
pub mod report_generator;
pub mod timeline;
pub use cookie_decoder::CookieDecoder;
pub use evidence_collector::EvidenceCollector;
pub use google_analytics::GoogleAnalyticsCookie;
pub use ios_binary::IosBinaryCookieParser;
pub use report_generator::{ForensicReport, ForensicReportBuilder};
pub use timeline::{CookieAction, CookieEvent, CookieTimeline, EventSource, TimelineAnomaly};
#[derive(Debug, Clone)]
pub struct ForensicAnalyzer {
config: ForensicConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ForensicConfig {
pub enable_chain_of_custody: bool,
pub include_raw_data: bool,
pub timezone: String,
pub anomaly_threshold: f64,
}
impl Default for ForensicConfig {
fn default() -> Self {
Self {
enable_chain_of_custody: true,
include_raw_data: true,
timezone: "UTC".to_string(),
anomaly_threshold: 0.7,
}
}
}
impl ForensicAnalyzer {
#[must_use]
pub fn new() -> Self {
Self {
config: ForensicConfig::default(),
}
}
#[must_use]
pub fn with_config(config: ForensicConfig) -> Self {
Self { config }
}
pub fn reconstruct_timeline(&self, cookies: &[Cookie]) -> Result<CookieTimeline> {
timeline::CookieTimeline::reconstruct(cookies, &self.config)
}
pub fn decode_ios_binary(&self, data: &[u8]) -> Result<Vec<Cookie>> {
ios_binary::IosBinaryCookieParser::parse_binary_cookie_file(data)
}
pub fn analyze_google_analytics(
&self,
cookies: &[Cookie],
) -> Result<Vec<GoogleAnalyticsCookie>> {
let mut ga_cookies = Vec::new();
for cookie in cookies {
if cookie.name.starts_with("_ga") || cookie.name.starts_with("_gid") {
if let Ok(ga_cookie) = GoogleAnalyticsCookie::decode(cookie) {
ga_cookies.push(ga_cookie);
}
}
}
Ok(ga_cookies)
}
pub fn decode_cookie(&self, cookie: &Cookie) -> Result<DecodedCookie> {
cookie_decoder::CookieDecoder::decode(cookie)
}
pub fn collect_evidence(&self, cookies: &[Cookie]) -> Result<evidence_collector::Evidence> {
evidence_collector::EvidenceCollector::collect(cookies, &self.config)
}
pub fn generate_report(&self, cookies: &[Cookie]) -> Result<ForensicReport> {
let timeline = self.reconstruct_timeline(cookies)?;
let evidence = self.collect_evidence(cookies)?;
ForensicReportBuilder::new()
.with_timeline(timeline)
.with_evidence(evidence)
.with_config(&self.config)
.build()
}
}
impl Default for ForensicAnalyzer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DecodedCookie {
pub original: Cookie,
pub encodings: Vec<EncodingType>,
pub decoded_values: HashMap<String, String>,
pub is_timestamp: bool,
pub timestamp: Option<DateTime<Utc>>,
pub is_guid: bool,
pub guid: Option<String>,
pub structured_data: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum EncodingType {
Base64,
Hex,
UrlEncoded,
UnixTimestamp,
Uuid,
Json,
PlainText,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_forensic_analyzer_creation() {
let analyzer = ForensicAnalyzer::new();
assert!(analyzer.config.enable_chain_of_custody);
}
#[test]
fn test_forensic_config_default() {
let config = ForensicConfig::default();
assert_eq!(config.timezone, "UTC");
assert_eq!(config.anomaly_threshold, 0.7);
}
}