sscan 0.8.0

A scriptable file/process/network scanner
Documentation
//! # Result and Error Types for [`YaraEngine`](super::YaraEngine)
//!
//! Definitions for the result and error types a
//! [`YaraEngine`](super::YaraEngine) can return during compilation or
//! scanning operations.
//!

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;
use yara_x::Rule;

/// Metadata about a YARA rule that matched during a scan operation.
#[derive(Serialize, Deserialize, Debug)]
#[must_use]
pub struct MatchedRule {
    /// The name of the YARA rule that matched the byte sequence.
    pub identifier: String,

    /// The namespace in which the YARA rule was compiled.
    pub namespace: String,

    /// Arbitrary metadata set in the `meta:` fields of the YARA rule.
    pub metadata: HashMap<String, String>,

    /// Arbitrary tags attached to a YARA rule.
    pub tags: Vec<String>,
}

impl From<Rule<'_, '_>> for MatchedRule {
    fn from(value: Rule) -> Self {
        // Get the identifier and namespace
        let identifier: String = value.identifier().to_owned();
        let namespace: String = value.namespace().to_owned();

        // Get the rule's metadata
        let mut metadata: HashMap<String, String> = HashMap::new();
        for (key, value) in value.metadata() {
            let value = match value {
                yara_x::MetaValue::Bool(b) => b.to_string(),
                yara_x::MetaValue::Bytes(bs) => bs.to_string(),
                yara_x::MetaValue::Float(f) => f.to_string(),
                yara_x::MetaValue::Integer(i) => i.to_string(),
                yara_x::MetaValue::String(s) => s.to_string(),
            };
            metadata.insert(key.to_string(), value);
        }

        // Get the rules tags
        let mut tags: Vec<String> = Vec::new();
        for tag in value.tags() {
            tags.push(tag.identifier().to_string());
        }

        // Returned a new owned ScanResult
        Self {
            identifier,
            namespace,
            metadata,
            tags,
        }
    }
}

/// Comprehensive error type for [`YaraEngine`](super::YaraEngine) errors.
#[derive(Error, Debug)]
#[must_use]
pub enum Error {
    /// Occurs during compilation for invalid YARA rule definitions.
    #[error(
        "failed to compile YARA rule(s): {code} - {title}\n\nFor Rule(s):\n{yara_src}\n\n{source}"
    )]
    CompilationError {
        /// The error code, as reported by YARA-X
        code: String,

        /// The title of the error message, as reported by YARA-X
        title: String,

        /// The YARA rule source that triggered the compilation error.
        yara_src: String,

        /// The underlying error generated by [`yara_x::Compiler`]
        source: yara_x::errors::CompileError,
    },

    /// Occurs during scanning if YARA-X cannot complete a scan operation.
    #[error("the YARA-X scanner encountered an error: {source}\n\nFor byte(s):\n{bytes:?}")]
    ScanError {
        /// The byte sequence YARA-X was attempting to scan.
        bytes: Vec<u8>,

        /// The underlying error generated by [`yara_x::Scanner`]
        source: yara_x::errors::ScanError,
    },

    /// Occurs when no compiled rules exist for a YARA-X scanner to use.
    #[error("failed to launch scan: no compiled rules.\n\nFor byte(s):\n{bytes:?}\n\nHint: did you compile before launching a scan?")]
    NoCompiledRules {
        /// The byte sequence YARA-X was attempting to scan.
        bytes: Vec<u8>,
    },
}

impl Error {
    /// Create a new [`Error::CompilationError`].
    pub fn compile_error<S>(yara_src: &S, inner: yara_x::errors::CompileError) -> Self
    where
        S: ToString,
    {
        let code: String = inner.code().to_owned();
        let title: String = inner.title().to_string();
        let yara_src: String = yara_src.to_string();
        Self::CompilationError {
            code,
            title,
            yara_src,
            source: inner,
        }
    }

    /// Create a new [`Error::ScanError`]
    pub fn scan_error(bytes: Vec<u8>, inner: yara_x::errors::ScanError) -> Self {
        Self::ScanError {
            bytes,
            source: inner,
        }
    }

    /// Create a new [`Error::NoCompiledRules`] error.
    pub fn no_compiled_rules(bytes: Vec<u8>) -> Self {
        Self::NoCompiledRules { bytes }
    }
}