malwaredb 0.3.2

Service for storing malicious, benign, or unknown files and related metadata and relationships.
// SPDX-License-Identifier: Apache-2.0

use malwaredb_server::db::types::FileMetadata;

use std::fs::File;
use std::io::Cursor;
use std::path::PathBuf;
use std::process::ExitCode;

use anyhow::Result;
use clap::{Parser, ValueEnum};

#[derive(Clone, ValueEnum, Debug, Eq, PartialEq)]
pub enum Action {
    /// Encode a `CaRT` file
    Encode,

    /// Decode a `CaRT` file
    Decode,
}

#[derive(Parser, Clone, Debug, Eq, PartialEq)]
pub struct CartIO {
    /// Action to be performed, or no action to display `CaRT` information
    #[arg(short, long)]
    pub action: Option<Action>,

    /// Output file
    #[arg(short, long, value_hint = clap::ValueHint::FilePath)]
    pub output: Option<PathBuf>,

    /// File to encode, decode, or preview
    #[arg(value_name = "FILE", value_hint = clap::ValueHint::FilePath)]
    pub file: PathBuf,
}

impl CartIO {
    pub fn execute(&self) -> Result<ExitCode> {
        if self.output.is_none() != self.action.is_none() {
            eprintln!("Action requires an output, and vise-versa");
            return Ok(ExitCode::FAILURE);
        }

        let mut input_file = File::open(&self.file)?;

        if self.output.is_none() && self.action.is_none() {
            let mut output_buffer = Cursor::new(vec![]);

            return if let Ok((header, footer)) =
                cart_container::unpack_stream(&mut input_file, &mut output_buffer, None)
            {
                let data = output_buffer.into_inner();
                let magic = hex::encode(&data[0..10]);
                println!("First ten bytes: {magic}");
                if let Some(meta) = header {
                    if !meta.is_empty() {
                        println!("Header:");
                    }
                    for (key, value) in meta {
                        println!("{key}: {value}");
                    }
                }
                if let Some(meta) = footer {
                    if !meta.is_empty() {
                        println!("Footer:");
                    }
                    for (key, value) in meta {
                        println!("{key}: {value}");
                    }
                }
                Ok(ExitCode::SUCCESS)
            } else {
                eprintln!("{} is not a CaRT file.", self.file.display());
                Ok(ExitCode::FAILURE)
            };
        }

        let output_file = self.output.as_ref().unwrap();
        let mut output_file = File::create(output_file)?;

        match self.action.as_ref().unwrap() {
            Action::Encode => {
                let file_contents = std::fs::read(&self.file)?;
                let report = FileMetadata::new(&file_contents, None);

                let mut output_metadata = cart_container::JsonMap::new();
                output_metadata.insert("sha384".into(), report.sha384.into());
                output_metadata.insert("sha512".into(), report.sha512.into());
                output_metadata.insert("entropy".into(), report.entropy.into());
                cart_container::pack_stream(
                    &mut input_file,
                    &mut output_file,
                    Some(output_metadata),
                    None,
                    cart_container::digesters::default_digesters(),
                    None,
                )?;
            }
            Action::Decode => {
                cart_container::unpack_stream(&mut input_file, &mut output_file, None)?;
            }
        }
        Ok(ExitCode::SUCCESS)
    }
}