Merge pull request #316 from Yamato-Security/feature/output_error_log_file_and_options#301

fixed #301 #303 #309
This commit is contained in:
Yamato Security
2021-12-22 16:08:13 +09:00
committed by GitHub
8 changed files with 239 additions and 88 deletions
+6 -4
View File
@@ -5,6 +5,7 @@ use clap::{App, AppSettings, ArgMatches};
use hashbrown::HashMap;
use hashbrown::HashSet;
use lazy_static::lazy_static;
use std::io::BufWriter;
use std::sync::RwLock;
lazy_static! {
pub static ref CONFIG: RwLock<ConfigReader> = RwLock::new(ConfigReader::new());
@@ -68,6 +69,7 @@ fn build_app<'a>() -> ArgMatches<'a> {
-t --thread-number=[NUMBER] 'Thread number (default: optimal number for performance)'
-s --statistics 'Prints statistics of event IDs'
-q --quiet 'Quiet mode. Do not display the launch banner'
-Q --quiet-errors 'Quiet errors mode. Do not save error logs.'
--contributors 'Prints the list of contributors'";
App::new(&program)
.about("Hayabusa: Aiming to be the world's greatest Windows event log analysis tool!")
@@ -139,8 +141,8 @@ impl TargetEventTime {
Ok(dt) => Some(dt.with_timezone(&Utc)),
Err(err) => {
AlertMessage::alert(
&mut std::io::stderr().lock(),
format!("starttimeline field: {}", err),
&mut BufWriter::new(std::io::stderr().lock()),
&format!("start-timeline field: {}", err),
)
.ok();
None
@@ -156,8 +158,8 @@ impl TargetEventTime {
Ok(dt) => Some(dt.with_timezone(&Utc)),
Err(err) => {
AlertMessage::alert(
&mut std::io::stderr().lock(),
format!("endtimeline field: {}", err),
&mut BufWriter::new(std::io::stderr().lock()),
&format!("end-timeline field: {}", err),
)
.ok();
None
+17 -9
View File
@@ -1,7 +1,10 @@
extern crate csv;
use crate::detections::configs;
use crate::detections::print::AlertMessage;
use crate::detections::print::ERROR_LOG_STACK;
use crate::detections::print::MESSAGES;
use crate::detections::print::QUIET_ERRORS_FLAG;
use crate::detections::rule;
use crate::detections::rule::AggResult;
use crate::detections::rule::RuleNode;
@@ -11,9 +14,9 @@ use crate::yaml::ParseYaml;
use hashbrown;
use serde_json::Value;
use std::collections::HashMap;
use tokio::{runtime::Runtime, spawn, task::JoinHandle};
use std::io::BufWriter;
use std::sync::Arc;
use tokio::{runtime::Runtime, spawn, task::JoinHandle};
const DIRPATH_RULES: &str = "rules";
@@ -57,11 +60,16 @@ impl Detection {
let result_readdir =
rulefile_loader.read_dir(rulespath.unwrap_or(DIRPATH_RULES), &level, exclude_ids);
if result_readdir.is_err() {
AlertMessage::alert(
&mut std::io::stderr().lock(),
format!("{}", result_readdir.unwrap_err()),
)
.ok();
let errmsg = format!("{}", result_readdir.unwrap_err());
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg).ok();
}
if !*QUIET_ERRORS_FLAG {
ERROR_LOG_STACK
.lock()
.unwrap()
.push(format!("[ERROR] {}", errmsg));
}
return vec![];
}
let mut parseerror_count = rulefile_loader.errorrule_count;
@@ -75,10 +83,10 @@ impl Detection {
err_msgs_result.err().iter().for_each(|err_msgs| {
let errmsg_body =
format!("Failed to parse rule file. (FilePath : {})", rule.rulepath);
AlertMessage::warn(&mut std::io::stdout().lock(), errmsg_body).ok();
AlertMessage::warn(&mut std::io::stdout().lock(), &errmsg_body).ok();
err_msgs.iter().for_each(|err_msg| {
AlertMessage::warn(&mut std::io::stdout().lock(), err_msg.to_string()).ok();
AlertMessage::warn(&mut std::io::stdout().lock(), err_msg).ok();
});
parseerror_count += 1;
println!(""); // 一行開けるためのprintln
+67 -9
View File
@@ -2,13 +2,18 @@ extern crate lazy_static;
use crate::detections::configs;
use crate::detections::utils;
use crate::detections::utils::get_serde_number_to_string;
use chrono::{DateTime, TimeZone, Utc};
use chrono::{DateTime, Local, TimeZone, Utc};
use lazy_static::lazy_static;
use regex::Regex;
use serde_json::Value;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::env;
use std::fs::create_dir;
use std::fs::File;
use std::io::BufWriter;
use std::io::{self, Write};
use std::path::Path;
use std::sync::Mutex;
#[derive(Debug)]
@@ -32,6 +37,16 @@ pub struct AlertMessage {}
lazy_static! {
pub static ref MESSAGES: Mutex<Message> = Mutex::new(Message::new());
pub static ref ALIASREGEX: Regex = Regex::new(r"%[a-zA-Z0-9-_]+%").unwrap();
pub static ref ERROR_LOG_PATH: String = format!(
"./logs/errorlog-{}.log",
Local::now().format("%Y%m%d_%H%M%S")
);
pub static ref QUIET_ERRORS_FLAG: bool = configs::CONFIG
.read()
.unwrap()
.args
.is_present("quiet-errors");
pub static ref ERROR_LOG_STACK: Mutex<Vec<String>> = Mutex::new(Vec::new());
}
impl Message {
@@ -180,10 +195,48 @@ impl Message {
}
impl AlertMessage {
pub fn alert<W: Write>(w: &mut W, contents: String) -> io::Result<()> {
///対象のディレクトリが存在することを確認後、最初の定型文を追加して、ファイルのbufwriterを返す関数
pub fn create_error_log(path_str: String) {
if *QUIET_ERRORS_FLAG {
return;
}
let path = Path::new(&path_str);
if !path.parent().unwrap().exists() {
create_dir(path.parent().unwrap()).ok();
}
let mut error_log_writer = BufWriter::new(File::create(path).unwrap());
error_log_writer
.write(
format!(
"user input: {:?}\n",
format_args!(
"{}",
env::args()
.map(|arg| arg)
.collect::<Vec<String>>()
.join(" ")
)
)
.as_bytes(),
)
.unwrap();
for error_log in ERROR_LOG_STACK.lock().unwrap().iter() {
writeln!(error_log_writer, "{}", error_log).ok();
}
println!(
"Errors were generated. Please check {} for details.",
ERROR_LOG_PATH.to_string()
);
println!("");
}
/// ERRORメッセージを表示する関数
pub fn alert<W: Write>(w: &mut W, contents: &String) -> io::Result<()> {
writeln!(w, "[ERROR] {}", contents)
}
pub fn warn<W: Write>(w: &mut W, contents: String) -> io::Result<()> {
/// WARNメッセージを表示する関数
pub fn warn<W: Write>(w: &mut W, contents: &String) -> io::Result<()> {
writeln!(w, "[WARN] {}", contents)
}
}
@@ -192,6 +245,7 @@ impl AlertMessage {
mod tests {
use crate::detections::print::{AlertMessage, Message};
use serde_json::Value;
use std::io::BufWriter;
#[test]
fn test_create_and_append_message() {
@@ -304,17 +358,21 @@ mod tests {
#[test]
fn test_error_message() {
let input = "TEST!";
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
AlertMessage::alert(&mut stdout, input.to_string()).expect("[ERROR] TEST!");
AlertMessage::alert(
&mut BufWriter::new(std::io::stdout().lock()),
&input.to_string(),
)
.expect("[ERROR] TEST!");
}
#[test]
fn test_warn_message() {
let input = "TESTWarn!";
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
AlertMessage::alert(&mut stdout, input.to_string()).expect("[WARN] TESTWarn!");
AlertMessage::warn(
&mut BufWriter::new(std::io::stdout().lock()),
&input.to_string(),
)
.expect("[WARN] TESTWarn!");
}
#[test]
+53 -34
View File
@@ -1,10 +1,14 @@
use crate::detections::configs;
use crate::detections::print::AlertMessage;
use crate::detections::print::ERROR_LOG_STACK;
use crate::detections::print::QUIET_ERRORS_FLAG;
use crate::detections::rule::AggResult;
use crate::detections::rule::Message;
use crate::detections::rule::RuleNode;
use chrono::{DateTime, TimeZone, Utc};
use hashbrown::HashMap;
use serde_json::Value;
use std::io::BufWriter;
use std::num::ParseIntError;
use std::path::Path;
@@ -65,30 +69,35 @@ fn get_alias_value_in_record(
return Some(value.to_string().replace("\"", ""));
}
None => {
AlertMessage::alert(
&mut std::io::stderr().lock(),
match is_by_alias {
true => format!(
"count by clause alias value not found in count process. rule file:{} EventID:{}",
Path::new(&rule.rulepath)
.file_name()
.unwrap()
.to_str()
.unwrap(),
utils::get_event_value(&utils::get_event_id_key(), record).unwrap()
),
false => format!(
"count field clause alias value not found in count process. rule file:{} EventID:{}",
Path::new(&rule.rulepath)
.file_name()
.unwrap()
.to_str()
.unwrap(),
utils::get_event_value(&utils::get_event_id_key(), record).unwrap()
),
},
)
.ok();
let errmsg = match is_by_alias {
true => format!(
"count by clause alias value not found in count process. rule file:{} EventID:{}",
Path::new(&rule.rulepath)
.file_name()
.unwrap()
.to_str()
.unwrap(),
utils::get_event_value(&utils::get_event_id_key(), record).unwrap()
),
false => format!(
"count field clause alias value not found in count process. rule file:{} EventID:{}",
Path::new(&rule.rulepath)
.file_name()
.unwrap()
.to_str()
.unwrap(),
utils::get_event_value(&utils::get_event_id_key(), record).unwrap()
),
};
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg).ok();
}
if !*QUIET_ERRORS_FLAG {
ERROR_LOG_STACK
.lock()
.unwrap()
.push(format!("[ERROR] {}", errmsg));
}
return None;
}
};
@@ -181,11 +190,16 @@ impl TimeFrameInfo {
ttype = "d".to_owned();
tnum.retain(|c| c != 'd');
} else {
AlertMessage::alert(
&mut std::io::stderr().lock(),
format!("Timeframe is invalid. Input value:{}", value),
)
.ok();
let errmsg = format!("Timeframe is invalid. Input value:{}", value);
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg).ok();
}
if !*QUIET_ERRORS_FLAG {
ERROR_LOG_STACK
.lock()
.unwrap()
.push(format!("[ERROR] {}", errmsg));
}
}
return TimeFrameInfo {
timetype: ttype,
@@ -214,11 +228,16 @@ pub fn get_sec_timeframe(rule: &RuleNode) -> Option<i64> {
}
}
Err(err) => {
AlertMessage::alert(
&mut std::io::stderr().lock(),
format!("Timeframe number is invalid. timeframe.{}", err),
)
.ok();
let errmsg = format!("Timeframe number is invalid. timeframe. {}", err);
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg).ok();
}
if !*QUIET_ERRORS_FLAG {
ERROR_LOG_STACK
.lock()
.unwrap()
.push(format!("[ERROR] {}", errmsg.to_string()));
}
return Option::None;
}
}