oletools_rs 0.1.0

Rust port of oletools — analysis tools for Microsoft Office files (VBA macros, DDE, OLE objects, RTF exploits)
Documentation
//! Indicator types for OleID analysis results.

use std::fmt;

/// Risk level for a security indicator.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum RiskLevel {
    /// No risk detected.
    None,
    /// Informational only.
    Info,
    /// Low risk.
    Low,
    /// Medium risk.
    Medium,
    /// High risk.
    High,
    /// Risk level unknown (check failed or inconclusive).
    Unknown,
    /// Error during check.
    Error,
}

impl fmt::Display for RiskLevel {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            RiskLevel::None => write!(f, "none"),
            RiskLevel::Info => write!(f, "info"),
            RiskLevel::Low => write!(f, "low"),
            RiskLevel::Medium => write!(f, "medium"),
            RiskLevel::High => write!(f, "high"),
            RiskLevel::Unknown => write!(f, "unknown"),
            RiskLevel::Error => write!(f, "error"),
        }
    }
}

/// A single security indicator from document analysis.
#[derive(Debug, Clone)]
pub struct Indicator {
    /// Unique identifier (e.g., "vba_macros", "encrypted", "ext_rels").
    pub id: String,
    /// Human-readable name.
    pub name: String,
    /// Description of what this indicator checks.
    pub description: String,
    /// Value found (e.g., "True", "False", format name).
    pub value: String,
    /// Risk level.
    pub risk: RiskLevel,
}

impl Indicator {
    /// Create a new indicator.
    pub fn new(
        id: impl Into<String>,
        name: impl Into<String>,
        description: impl Into<String>,
        value: impl Into<String>,
        risk: RiskLevel,
    ) -> Self {
        Self {
            id: id.into(),
            name: name.into(),
            description: description.into(),
            value: value.into(),
            risk,
        }
    }
}

impl fmt::Display for Indicator {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}: {} = {} ({})",
            self.id, self.name, self.value, self.risk
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_risk_level_ordering() {
        assert!(RiskLevel::None < RiskLevel::Info);
        assert!(RiskLevel::Info < RiskLevel::Low);
        assert!(RiskLevel::Low < RiskLevel::Medium);
        assert!(RiskLevel::Medium < RiskLevel::High);
    }

    #[test]
    fn test_risk_level_display() {
        assert_eq!(RiskLevel::High.to_string(), "high");
        assert_eq!(RiskLevel::None.to_string(), "none");
        assert_eq!(RiskLevel::Error.to_string(), "error");
    }

    #[test]
    fn test_indicator_new() {
        let ind = Indicator::new("test_id", "Test Name", "Test description", "True", RiskLevel::Medium);
        assert_eq!(ind.id, "test_id");
        assert_eq!(ind.name, "Test Name");
        assert_eq!(ind.value, "True");
        assert_eq!(ind.risk, RiskLevel::Medium);
    }

    #[test]
    fn test_indicator_display() {
        let ind = Indicator::new("vba", "VBA Macros", "Contains VBA macros", "True", RiskLevel::Medium);
        let display = format!("{}", ind);
        assert!(display.contains("vba"));
        assert!(display.contains("True"));
        assert!(display.contains("medium"));
    }
}