1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
//! Implements file output dumping given a file format. __binsec__ currently supports the
//! following backends for structured deserialization:
//!
//! * Normal output
//! * JSON
//! * TOML

use crate::detect::Detector;
use crate::errors::BinResult;

use colored::*;
use serde_json::Value;
use term_table::{
    row::Row,
    table_cell::{Alignment, TableCell},
};
use term_table::{Table, TableStyle};

use std::collections::BTreeMap;

/// Aliases a finalized output type for a detector, storing all the checks that
/// were performed and their results for consumption by a `BinTable` for creating a table.
pub type FeatureMap = BTreeMap<&'static str, Value>;

/// Helper struct that helps convert a `FeatureMap` into a normalized ASCII table
pub struct BinTable;

impl BinTable {
    /// initializes a stringified version of the ASCII table given a table name and a `FeatureMap`.
    pub fn parse(name: &str, mapping: FeatureMap) -> String {
        // initialize blank style term table
        let mut table = Table::new();
        table.max_column_width = 90;
        table.style = TableStyle::rounded();

        // create bolded header
        table.add_row(Row::new(vec![TableCell::new_with_alignment(
            name.bold().underline(),
            2,
            Alignment::Center,
        )]));

        // add features to table
        for (name, feature) in mapping {
            // format display based on content
            let feature_cell = match feature {
                Value::Bool(true) => {
                    TableCell::new_with_alignment("✔️".green(), 1, Alignment::Center)
                }
                Value::Bool(false) => {
                    TableCell::new_with_alignment("✖️".red(), 1, Alignment::Center)
                }
                Value::String(val) => {
                    TableCell::new_with_alignment(val.bold(), 1, Alignment::Center)
                }
                _ => TableCell::new_with_alignment(feature, 1, Alignment::Center),
            };

            table.add_row(Row::new(vec![TableCell::new(name), feature_cell]));
        }
        table.render()
    }
}

/// Defines the output format variants that are supported by binsec. Enforces a uniform `dump()`
/// function to perform serialization to the respective format when outputting back to user.
pub enum BinFormat {
    Normal,
    Json,
    Toml,
}

impl BinFormat {
    #[inline]
    fn make_normal(input: &Detector) -> String {
        // finalized output string
        let mut output: String = String::new();

        // check if basic information specified
        if let Some(info) = &input.bin_info {
            output.push_str(&info.output());
        }

        // check if kernel checks were specified
        if let Some(kernchecks) = &input.kernel_features {
            output.push_str(&kernchecks.output());
        }

        // check if hardening checks were specified
        if let Some(harden_checks) = &input.harden_features {
            output.push_str(&harden_checks.output());
        }

        // check if enhanced checks were specified
        if let Some(rule_checks) = &input.rule_features {
            output.push_str(&rule_checks.output());
        }
        output
    }

    /// Constructs a printable string for respective output format for display or persistent
    /// storage by consuming a `Detector`.
    pub fn dump(&self, input: Detector) -> BinResult<String> {
        match self {
            BinFormat::Normal => Ok(BinFormat::make_normal(&input)),
            BinFormat::Toml => Ok(toml::to_string(&input).unwrap()),
            BinFormat::Json => Ok(serde_json::to_string_pretty(&input).unwrap()),
        }
    }
}