malwaredb-server 0.3.4

Server data storage logic for MalwareDB.
Documentation
// SPDX-License-Identifier: Apache-2.0

use malwaredb_api::YaraSearchRequest;

use tracing::error;
use uuid::Uuid;
use yara_x::errors::CompileError;
use yara_x::{Compiler, Rules};

#[cfg(not(any(
    target_arch = "aarch64",
    target_arch = "riscv64",
    target_arch = "s390x",
    target_arch = "x86_64"
)))]
compile_error!("Compile error: Yara not supported on this architecture.");

pub(crate) const MAX_YARA_PROCESSES: usize = 4;

/// Yara task information to find matching files
pub struct YaraTask {
    /// Identifier of the Yara search task
    pub id: Uuid,

    /// Original Yara rules
    pub yara_string: String,

    /// Compiled Yara rules
    pub yara_bytes: Vec<u8>,

    /// Last file ID if on a further pagination search.
    pub last_file_id: Option<u64>,

    /// User ID associated with the Yara task
    pub user_id: u32,
}

impl YaraTask {
    pub(crate) fn process_yara_rules(&self, sample: &[u8]) -> anyhow::Result<Vec<String>> {
        // TODO: Put this deserialization and `Scanner` creation somewhere else.
        let rules = Rules::deserialize(&self.yara_bytes)?;
        let mut scanner = yara_x::Scanner::new(&rules);
        let mut results = Vec::new();
        match scanner.scan(sample) {
            Ok(matches) => {
                for rule in matches.matching_rules() {
                    results.push(String::from(rule.identifier()));
                }
            }
            Err(e) => {
                error!("Error scanning sample: {e}");
            }
        }

        Ok(results)
    }
}

pub(crate) fn compile_yara_rules(request: YaraSearchRequest) -> Result<Rules, CompileError> {
    let mut compiler = Compiler::new();

    for rule in request.rules {
        compiler.add_source(&*rule)?;
    }

    Ok(compiler.build())
}