clawscan 1.0.0

OpenClaw/Moltbot/Clawdbot vulnerability scanner for prompt injection, supply chain, and RAG poisoning attacks
Documentation
//! Core type definitions for ClawScan vulnerability scanner

use serde::{Deserialize, Serialize};

/// Attack module identifier
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AttackModuleId {
    #[serde(rename = "cve-2026-25253")]
    Cve202625253,

    #[serde(rename = "cve-2026-22708")]
    Cve202622708,

    #[serde(rename = "cve-2026-25157")]
    Cve202625157,

    #[serde(rename = "prompt-injection")]
    PromptInjection,

    #[serde(rename = "rag-poisoning")]
    RagPoisoning,

    #[serde(rename = "supply-chain")]
    SupplyChain,

    #[serde(rename = "mcp-tool-poisoning")]
    McpToolPoisoning,

    #[serde(rename = "elevated-bypass")]
    ElevatedBypass,

    #[serde(rename = "zero-click-rce")]
    ZeroClickRce,
}

impl AttackModuleId {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Cve202625253 => "cve-2026-25253",
            Self::Cve202622708 => "cve-2026-22708",
            Self::Cve202625157 => "cve-2026-25157",
            Self::PromptInjection => "prompt-injection",
            Self::RagPoisoning => "rag-poisoning",
            Self::SupplyChain => "supply-chain",
            Self::McpToolPoisoning => "mcp-tool-poisoning",
            Self::ElevatedBypass => "elevated-bypass",
            Self::ZeroClickRce => "zero-click-rce",
        }
    }
}

/// Vulnerability severity levels
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Severity {
    Critical,
    High,
    Medium,
    Low,
    Informational,
}

impl Severity {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Critical => "critical",
            Self::High => "high",
            Self::Medium => "medium",
            Self::Low => "low",
            Self::Informational => "informational",
        }
    }
}

/// Result from capability check (shared across attack modules 2-9)
#[derive(Debug, Clone)]
pub struct WriteCapabilityResult {
    pub success: bool,
    pub has_write_scope: bool,
    pub device_token: Option<String>,
}

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

    #[test]
    fn test_attack_module_id_as_str() {
        assert_eq!(AttackModuleId::Cve202625253.as_str(), "cve-2026-25253");
        assert_eq!(AttackModuleId::PromptInjection.as_str(), "prompt-injection");
        assert_eq!(AttackModuleId::ZeroClickRce.as_str(), "zero-click-rce");
    }

    #[test]
    fn test_severity_as_str() {
        assert_eq!(Severity::Critical.as_str(), "critical");
        assert_eq!(Severity::High.as_str(), "high");
        assert_eq!(Severity::Medium.as_str(), "medium");
        assert_eq!(Severity::Low.as_str(), "low");
        assert_eq!(Severity::Informational.as_str(), "informational");
    }

    #[test]
    fn test_severity_ordering() {
        assert!(Severity::Critical == Severity::Critical);
        assert!(Severity::Critical != Severity::High);
    }

    #[test]
    fn test_attack_module_id_serialization() {
        let id = AttackModuleId::Cve202625253;
        let json = serde_json::to_string(&id).unwrap();
        assert_eq!(json, "\"cve-2026-25253\"");
    }

    #[test]
    fn test_attack_module_id_deserialization() {
        let json = "\"prompt-injection\"";
        let id: AttackModuleId = serde_json::from_str(json).unwrap();
        assert_eq!(id, AttackModuleId::PromptInjection);
    }

    #[test]
    fn test_severity_serialization() {
        let sev = Severity::Critical;
        let json = serde_json::to_string(&sev).unwrap();
        assert_eq!(json, "\"critical\"");
    }
}