diff --git a/src/detections/configs.rs b/src/detections/configs.rs index 86019b44..cbd02fe4 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -1,49 +1,30 @@ +use clap::{App, AppSettings, Arg, ArgMatches}; use std::fs::File; use std::io::prelude::*; use std::sync::Once; -use clap::ArgMatches; #[derive(Clone)] pub struct SingletonReader { pub regex: Vec>, pub whitelist: Vec>, - pub args: Config<'static>, + pub args: ArgMatches<'static>, } -#[derive(Debug, Clone)] -pub struct Config<'a> { - pub filepath: Option<&'a str>, - pub attackhunt: Option<&'a str>, - pub csv_timeline: Option<&'a str>, - pub human_readable_timeline: Option<&'a str>, - pub lang: Option<&'a str>, - pub timezone: Option<&'a str>, +pub enum Lang { + Ja, + En, } -impl<'a> Config<'a> { - fn new(args: ArgMatches<'a>) -> Self { - Config { - filepath: args.value_of("filepath"), - attackhunt: args.value_of("attackhunt"), - csv_timeline: args.value_of("csv-timeline"), - human_readable_timeline: args.value_of("human-readable-timeline"), - lang: args.value_of("lang"), - timezone: args.value_of("timezone"), - } - } -} - -pub fn init_singleton(args: ArgMatches<'static>) -> Box { +pub fn singleton() -> Box { static mut SINGLETON: Option> = Option::None; static ONCE: Once = Once::new(); - static CONFIG: Config = Config::new(args); unsafe { ONCE.call_once(|| { let singleton = SingletonReader { regex: read_csv("regexes.txt"), whitelist: read_csv("whitelist.txt"), - args: CONFIG, + args: build_app().get_matches(), }; SINGLETON = Some(Box::new(singleton)); @@ -53,11 +34,31 @@ pub fn init_singleton(args: ArgMatches<'static>) -> Box { } } -pub fn singleton() -> Box { - static mut SINGLETON: Option> = Option::None; - unsafe { - return SINGLETON.clone().unwrap(); - } +fn build_app() -> clap::App<'static, 'static> { + let program = std::env::args() + .nth(0) + .and_then(|s| { + std::path::PathBuf::from(s) + .file_stem() + .map(|s| s.to_string_lossy().into_owned()) + }) + .unwrap(); + + App::new(program) + .about("Yea! (Yamato Event Analyzer). Aiming to be the world's greatest Windows event log analysis tool!") + .version("0.0.1") + .author("Author name ") + .setting(AppSettings::VersionlessSubcommands) + .arg(Arg::from_usage("-f --filepath=[FILEPATH] 'event file path'")) + .arg(Arg::from_usage("--attackhunt=[ATTACK_HUNT] 'Attack Hunt'")) + .arg(Arg::from_usage("--csv-timeline=[CSV_TIMELINE] 'csv output timeline'")) + .arg(Arg::from_usage("--human-readable-timeline=[HUMAN_READABLE_TIMELINE] 'human readable timeline'")) + .arg(Arg::from_usage("-l --lang=[LANG] 'output language'")) + .arg(Arg::from_usage("-t --timezone=[TIMEZONE] 'timezone setting'")) + .arg(Arg::from_usage("-d --directory 'event log files directory'")) + .arg(Arg::from_usage("-s --statistics 'event statistics'")) + .arg(Arg::from_usage("-u --update 'signature update'")) + .arg(Arg::from_usage("--credits 'Zachary Mathis, Akira Nishikawa'")) } fn read_csv(filename: &str) -> Vec> { @@ -82,3 +83,14 @@ fn read_csv(filename: &str) -> Vec> { return ret; } + +/// Argsから言語情報を読み取り Lang を返す +pub fn get_lang() -> Lang { + let lang: String = singleton().args.value_of("lang").unwrap_or("").to_string(); + + match &*lang { + "Ja" | "ja" => Lang::Ja, + "En" | "en" => Lang::En, + _ => Lang::En, + } +} diff --git a/src/detections/mod.rs b/src/detections/mod.rs index 30eac958..4aaee9ff 100644 --- a/src/detections/mod.rs +++ b/src/detections/mod.rs @@ -4,7 +4,7 @@ mod common; pub mod configs; pub mod detection; mod powershell; -mod print; +pub mod print; mod security; mod sysmon; mod system; diff --git a/src/detections/print.rs b/src/detections/print.rs index 64b40923..c950f9a5 100644 --- a/src/detections/print.rs +++ b/src/detections/print.rs @@ -1,15 +1,11 @@ +use crate::detections::configs::{get_lang, Lang}; use std::collections::HashMap; - -#[derive(Debug)] -pub enum Lang { - Ja, - En, -} +use std::fmt; #[derive(Debug)] pub struct MessageText { - ja: String, - en: String, + pub ja: String, + pub en: String, } #[derive(Debug)] @@ -35,18 +31,25 @@ impl Message { self.map.insert(error_code, message); } - /// メッセージを指定された言語で返す - pub fn return_error_message(&self, error_num: &str, lang: Lang) -> String { - let messages = self.map.get(error_num).unwrap_or(self.map.get("undefined").unwrap()); - match lang { - Lang::Ja => messages.ja.clone(), - Lang::En => messages.en.clone(), + /// メッセージを返す + pub fn return_message(&self, message_num: &str) -> &MessageText { + self.map.get(message_num).unwrap_or(self.map.get("undefined").unwrap()) + } +} + +/// メッセージテキストを言語設定に合わせて返す +/// println!("{}", ) とすると今の言語設定で出力される +impl fmt::Display for MessageText { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match get_lang() { + Lang::Ja => write!(f, "{}", self.ja), + Lang::En => write!(f, "{}", self.en), } } } #[test] -fn test_create_error_message() { +fn test_create_and_read_message() { let mut error_message = Message::new(); error_message.insert_message( @@ -57,18 +60,7 @@ fn test_create_error_message() { }, ); - let message_ja1 = error_message.return_error_message("4103", Lang::Ja); - assert_eq!(message_ja1, "パイプライン実行をしています"); - let message_ja2 = error_message.return_error_message("4103", Lang::Ja); - assert_eq!(message_ja2, "パイプライン実行をしています"); + let display = format!("{}", format_args!("{}", error_message.return_message("4103"))); - let message_en1 = error_message.return_error_message("4103", Lang::En); - assert_eq!(message_en1, "Execute pipeline"); - let message_en2 = error_message.return_error_message("4103", Lang::En); - assert_eq!(message_en2, "Execute pipeline"); - - let undef_ja = error_message.return_error_message("HOGE", Lang::Ja); - assert_eq!(undef_ja, "未設定"); - let undef_en = error_message.return_error_message("HOGE", Lang::En); - assert_eq!(undef_en, "Undefined"); + assert_eq!(display, "Execute pipeline") } diff --git a/src/main.rs b/src/main.rs index b4ea6830..79a27530 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,51 +1,24 @@ -extern crate clap; extern crate serde; -use clap::{App, AppSettings, Arg}; use evtx::EvtxParser; use quick_xml::de::DeError; use std::{fs, path::PathBuf, process}; use yamato_event_analyzer::detections::configs; use yamato_event_analyzer::detections::detection; +use yamato_event_analyzer::detections::print; use yamato_event_analyzer::omikuji::Omikuji; use yamato_event_analyzer::toml; -fn build_app() -> clap::App<'static, 'static> { - let program = std::env::args() - .nth(0) - .and_then(|s| { - std::path::PathBuf::from(s) - .file_stem() - .map(|s| s.to_string_lossy().into_owned()) - }) - .unwrap(); - - App::new(program) - .about("Yea! (Yamato Event Analyzer). Aiming to be the world's greatest Windows event log analysis tool!") - .version("0.0.1") - .author("Author name ") - .setting(AppSettings::VersionlessSubcommands) - .arg(Arg::from_usage("-f --filepath=[FILEPATH] 'event file path'")) - .arg(Arg::from_usage("--attackhunt=[ATTACK_HUNT] 'Attack Hunt'")) - .arg(Arg::from_usage("--csv-timeline=[CSV_TIMELINE] 'csv output timeline'")) - .arg(Arg::from_usage("--human-readable-timeline=[HUMAN_READABLE_TIMELINE] 'human readable timeline'")) - .arg(Arg::from_usage("-l --lang=[LANG] 'output language'")) - .arg(Arg::from_usage("-t --timezone=[TIMEZONE] 'timezone setting'")) - .arg(Arg::from_usage("-d --directory 'event log files directory'")) - .arg(Arg::from_usage("-s --statistics 'event statistics'")) - .arg(Arg::from_usage("-u --update 'signature update'")) - .arg(Arg::from_usage("-o --omikuji 'output with omikuji'")) - .arg(Arg::from_usage("--credits 'Zachary Mathis, Akira Nishikawa'")) -} - fn main() -> Result<(), DeError> { - let args = build_app().get_matches(); - configs::init_singleton(&args); + configs::singleton(); - let filepath: Option<&str> = configs::singleton().args.filepath; - - if let Some(filepath) = filepath { - parse_file(filepath); + let filepath: String = configs::singleton() + .args + .value_of("filepath") + .unwrap_or("") + .to_string(); + if filepath != "" { + parse_file(&filepath); } Ok(())