Merge branch 'main' into 654-enhancement-output-to-json
This commit is contained in:
@@ -203,12 +203,15 @@ fn emit_csv<W: std::io::Write>(
|
||||
HashMap::new();
|
||||
let mut detect_counts_by_computer_and_level: HashMap<String, HashMap<String, i128>> =
|
||||
HashMap::new();
|
||||
let mut detect_counts_by_rule_and_level: HashMap<String, HashMap<String, i128>> =
|
||||
HashMap::new();
|
||||
|
||||
let levels = Vec::from(["crit", "high", "med ", "low ", "info", "undefined"]);
|
||||
// レベル別、日ごとの集計用変数の初期化
|
||||
for level_init in levels {
|
||||
detect_counts_by_date_and_level.insert(level_init.to_string(), HashMap::new());
|
||||
detect_counts_by_computer_and_level.insert(level_init.to_string(), HashMap::new());
|
||||
detect_counts_by_rule_and_level.insert(level_init.to_string(), HashMap::new());
|
||||
}
|
||||
if displayflag {
|
||||
println!();
|
||||
@@ -283,6 +286,7 @@ fn emit_csv<W: std::io::Write>(
|
||||
)
|
||||
.unwrap_or(&0) as usize;
|
||||
let time_str_date = format_time(time, true);
|
||||
|
||||
let mut detect_counts_by_date = detect_counts_by_date_and_level
|
||||
.get(&detect_info.level.to_lowercase())
|
||||
.unwrap_or_else(|| detect_counts_by_date_and_level.get("undefined").unwrap())
|
||||
@@ -294,6 +298,7 @@ fn emit_csv<W: std::io::Write>(
|
||||
detected_rule_files.insert(detect_info.rulepath.clone());
|
||||
unique_detect_counts_by_level[level_suffix] += 1;
|
||||
}
|
||||
|
||||
let computer_rule_check_key =
|
||||
format!("{}|{}", &detect_info.computername, &detect_info.rulepath);
|
||||
if !detected_computer_and_rule_names.contains(&computer_rule_check_key) {
|
||||
@@ -313,6 +318,20 @@ fn emit_csv<W: std::io::Write>(
|
||||
.insert(detect_info.level.to_lowercase(), detect_counts_by_computer);
|
||||
}
|
||||
|
||||
let mut detect_counts_by_rules = detect_counts_by_rule_and_level
|
||||
.get(&detect_info.level.to_lowercase())
|
||||
.unwrap_or_else(|| {
|
||||
detect_counts_by_computer_and_level
|
||||
.get("undefined")
|
||||
.unwrap()
|
||||
})
|
||||
.clone();
|
||||
*detect_counts_by_rules
|
||||
.entry(Clone::clone(&detect_info.ruletitle))
|
||||
.or_insert(0) += 1;
|
||||
detect_counts_by_rule_and_level
|
||||
.insert(detect_info.level.to_lowercase(), detect_counts_by_rules);
|
||||
|
||||
total_detect_counts_by_level[level_suffix] += 1;
|
||||
detect_counts_by_date_and_level
|
||||
.insert(detect_info.level.to_lowercase(), detect_counts_by_date);
|
||||
@@ -415,6 +434,9 @@ fn emit_csv<W: std::io::Write>(
|
||||
println!();
|
||||
|
||||
_print_detection_summary_by_computer(detect_counts_by_computer_and_level, &color_map);
|
||||
println!();
|
||||
|
||||
_print_detection_summary_by_rule(detect_counts_by_rule_and_level, &color_map);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -610,6 +632,55 @@ fn _print_detection_summary_by_computer(
|
||||
buf_wtr.print(&wtr).ok();
|
||||
}
|
||||
|
||||
/// 各レベルごとで検出数が多かったルールのタイトルを出力する関数
|
||||
fn _print_detection_summary_by_rule(
|
||||
detect_counts_by_rule_and_level: HashMap<String, HashMap<String, i128>>,
|
||||
color_map: &HashMap<String, Color>,
|
||||
) {
|
||||
let buf_wtr = BufferWriter::stdout(ColorChoice::Always);
|
||||
let mut wtr = buf_wtr.buffer();
|
||||
wtr.set_color(ColorSpec::new().set_fg(None)).ok();
|
||||
let level_cnt = detect_counts_by_rule_and_level.len();
|
||||
for (idx, level) in LEVEL_ABBR.values().enumerate() {
|
||||
// output_levelsはlevelsからundefinedを除外した配列であり、各要素は必ず初期化されているのでSomeであることが保証されているのでunwrapをそのまま実施
|
||||
let detections_by_computer = detect_counts_by_rule_and_level.get(level).unwrap();
|
||||
let mut result_vec: Vec<String> = Vec::new();
|
||||
let mut sorted_detections: Vec<(&String, &i128)> = detections_by_computer.iter().collect();
|
||||
|
||||
sorted_detections.sort_by(|a, b| (-a.1).cmp(&(-b.1)));
|
||||
|
||||
for x in sorted_detections.iter().take(5) {
|
||||
result_vec.push(format!(
|
||||
"{} ({})",
|
||||
x.0,
|
||||
x.1.to_formatted_string(&Locale::en)
|
||||
));
|
||||
}
|
||||
let result_str = if result_vec.is_empty() {
|
||||
"None".to_string()
|
||||
} else {
|
||||
result_vec.join("\n")
|
||||
};
|
||||
|
||||
wtr.set_color(ColorSpec::new().set_fg(_get_output_color(
|
||||
color_map,
|
||||
LEVEL_FULL.get(level.as_str()).unwrap(),
|
||||
)))
|
||||
.ok();
|
||||
writeln!(
|
||||
wtr,
|
||||
"Top {} alerts:\n{}",
|
||||
LEVEL_FULL.get(level.as_str()).unwrap(),
|
||||
&result_str
|
||||
)
|
||||
.ok();
|
||||
if idx != level_cnt - 1 {
|
||||
writeln!(wtr).ok();
|
||||
}
|
||||
}
|
||||
buf_wtr.print(&wtr).ok();
|
||||
}
|
||||
|
||||
/// get timestamp to input datetime.
|
||||
fn _get_timestamp(time: &DateTime<Utc>) -> i64 {
|
||||
if configs::CONFIG.read().unwrap().args.utc {
|
||||
@@ -863,6 +934,7 @@ mod tests {
|
||||
output.to_string(),
|
||||
DetectInfo {
|
||||
rulepath: test_rulepath.to_string(),
|
||||
ruletitle: test_title.to_string(),
|
||||
level: test_level.to_string(),
|
||||
computername: test_computername.to_string(),
|
||||
eventid: test_eventid.to_string(),
|
||||
|
||||
@@ -37,7 +37,7 @@ use super::message::LEVEL_ABBR;
|
||||
// イベントファイルの1レコード分の情報を保持する構造体
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EvtxRecordInfo {
|
||||
pub evtx_filepath: String, // イベントファイルのファイルパス ログで出力するときに使う
|
||||
pub evtx_filepath: String, // イベントファイルのファイルパス ログで出力するときに使う
|
||||
pub record: Value, // 1レコード分のデータをJSON形式にシリアライズしたもの
|
||||
pub data_string: String,
|
||||
pub key_2_value: HashMap<String, String>,
|
||||
@@ -362,6 +362,7 @@ impl Detection {
|
||||
|
||||
let detect_info = DetectInfo {
|
||||
rulepath: (&rule.rulepath).to_owned(),
|
||||
ruletitle: rule.yaml["title"].as_str().unwrap_or("-").to_string(),
|
||||
level: LEVEL_ABBR.get(&level).unwrap_or(&level).to_string(),
|
||||
computername: record_info.record["Event"]["System"]["Computer"]
|
||||
.to_string()
|
||||
@@ -492,6 +493,7 @@ impl Detection {
|
||||
|
||||
let detect_info = DetectInfo {
|
||||
rulepath: (&rule.rulepath).to_owned(),
|
||||
ruletitle: rule.yaml["title"].as_str().unwrap_or("-").to_string(),
|
||||
level: LEVEL_ABBR.get(&level).unwrap_or(&level).to_string(),
|
||||
computername: "-".to_owned(),
|
||||
eventid: "-".to_owned(),
|
||||
|
||||
@@ -24,6 +24,7 @@ use termcolor::{BufferWriter, ColorChoice};
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DetectInfo {
|
||||
pub rulepath: String,
|
||||
pub ruletitle: String,
|
||||
pub level: String,
|
||||
pub computername: String,
|
||||
pub eventid: String,
|
||||
@@ -634,6 +635,7 @@ mod tests {
|
||||
for i in 1..2001 {
|
||||
let detect_info = DetectInfo {
|
||||
rulepath: "".to_string(),
|
||||
ruletitle: "".to_string(),
|
||||
level: "".to_string(),
|
||||
computername: "".to_string(),
|
||||
eventid: i.to_string(),
|
||||
|
||||
@@ -305,7 +305,6 @@ impl App {
|
||||
|
||||
let analysis_end_time: DateTime<Local> = Local::now();
|
||||
let analysis_duration = analysis_end_time.signed_duration_since(analysis_start_time);
|
||||
println!();
|
||||
write_color_buffer(
|
||||
&BufferWriter::stdout(ColorChoice::Always),
|
||||
None,
|
||||
|
||||
Reference in New Issue
Block a user