diff --git a/src/afterfact.rs b/src/afterfact.rs index b8bc0ecb..da0fd00a 100644 --- a/src/afterfact.rs +++ b/src/afterfact.rs @@ -4,6 +4,7 @@ use crate::detections::message::{self, LEVEL_ABBR}; use crate::detections::message::{AlertMessage, LEVEL_FULL}; use crate::detections::utils::{self, format_time}; use crate::detections::utils::{get_writable_color, write_color_buffer}; +use crate::options::htmlreport; use crate::options::profile::PROFILES; use bytesize::ByteSize; use chrono::{DateTime, Local, TimeZone, Utc}; @@ -210,6 +211,8 @@ fn emit_csv( all_record_cnt: u128, profile: LinkedHashMap, ) -> io::Result<()> { + let mut html_output_stock: Vec = vec![]; + let html_output_flag = configs::CONFIG.read().unwrap().args.html_report.is_some(); let disp_wtr = BufferWriter::stdout(ColorChoice::Always); let mut disp_wtr_buf = disp_wtr.buffer(); let json_output_flag = configs::CONFIG.read().unwrap().args.json_timeline; @@ -454,31 +457,35 @@ fn emit_csv( ) .ok(); write_color_buffer(&disp_wtr, get_writable_color(None), ": ", false).ok(); + let saved_alerts_output = + (all_record_cnt - reducted_record_cnt).to_formatted_string(&Locale::en); write_color_buffer( &disp_wtr, get_writable_color(Some(Color::Rgb(255, 255, 0))), - &(all_record_cnt - reducted_record_cnt).to_formatted_string(&Locale::en), + &saved_alerts_output, false, ) .ok(); write_color_buffer(&disp_wtr, get_writable_color(None), " / ", false).ok(); + let all_record_output = all_record_cnt.to_formatted_string(&Locale::en); write_color_buffer( &disp_wtr, get_writable_color(Some(Color::Rgb(0, 255, 255))), - &all_record_cnt.to_formatted_string(&Locale::en), + &all_record_output, false, ) .ok(); write_color_buffer(&disp_wtr, get_writable_color(None), " (", false).ok(); + let reduction_output = format!( + "Data reduction: {} events ({:.2}%)", + reducted_record_cnt.to_formatted_string(&Locale::en), + reducted_percent + ); write_color_buffer( &disp_wtr, get_writable_color(Some(Color::Rgb(0, 255, 0))), - &format!( - "Data reduction: {} events ({:.2}%)", - reducted_record_cnt.to_formatted_string(&Locale::en), - reducted_percent - ), + &reduction_output, false, ) .ok(); @@ -487,6 +494,12 @@ fn emit_csv( println!(); println!(); + if html_output_flag { + html_output_stock.push(format!("Saved alerts and events: {}", &saved_alerts_output)); + html_output_stock.push(format!("Total events analyzed: {}", &all_record_output)); + html_output_stock.push(reduction_output); + } + _print_unique_results( total_detect_counts_by_level, unique_detect_counts_by_level, @@ -496,17 +509,40 @@ fn emit_csv( ); println!(); - _print_detection_summary_by_date(detect_counts_by_date_and_level, &color_map); + _print_detection_summary_by_date( + detect_counts_by_date_and_level, + &color_map, + &mut html_output_stock, + ); println!(); println!(); + if html_output_flag { + html_output_stock.push("".to_string()); + } - _print_detection_summary_by_computer(detect_counts_by_computer_and_level, &color_map); + _print_detection_summary_by_computer( + detect_counts_by_computer_and_level, + &color_map, + &mut html_output_stock, + ); println!(); + if html_output_flag { + html_output_stock.push("".to_string()); + } - _print_detection_summary_tables(detect_counts_by_rule_and_level, &color_map); + _print_detection_summary_tables( + detect_counts_by_rule_and_level, + &color_map, + &mut html_output_stock, + ); println!(); + if html_output_flag { + html_output_stock.push("".to_string()); + } + } + if html_output_flag { + htmlreport::add_md_data("Results Summary".to_string(), html_output_stock); } - Ok(()) } @@ -634,13 +670,16 @@ fn _print_unique_results( fn _print_detection_summary_by_date( detect_counts_by_date: HashMap>, color_map: &HashMap, + html_output_stock: &mut Vec, ) { let buf_wtr = BufferWriter::stdout(ColorChoice::Always); let mut wtr = buf_wtr.buffer(); wtr.set_color(ColorSpec::new().set_fg(None)).ok(); - - writeln!(wtr, "Dates with most total detections:").ok(); - + let output_header = "Dates with most total detections:"; + writeln!(wtr, "{}", output_header).ok(); + if configs::CONFIG.read().unwrap().args.html_report.is_some() { + html_output_stock.push(output_header.to_string()); + } for (idx, level) in LEVEL_ABBR.values().enumerate() { // output_levelsはlevelsからundefinedを除外した配列であり、各要素は必ず初期化されているのでSomeであることが保証されているのでunwrapをそのまま実施 let detections_by_day = detect_counts_by_date.get(level).unwrap(); @@ -662,32 +701,38 @@ fn _print_detection_summary_by_date( if !exist_max_data { max_detect_str = "n/a".to_string(); } - write!( - wtr, + let output_str = format!( "{}: {}", LEVEL_FULL.get(level.as_str()).unwrap(), &max_detect_str - ) - .ok(); + ); + write!(wtr, "{}", output_str).ok(); if idx != LEVEL_ABBR.len() - 1 { wtr.set_color(ColorSpec::new().set_fg(None)).ok(); write!(wtr, ", ").ok(); } + if configs::CONFIG.read().unwrap().args.html_report.is_some() { + html_output_stock.push(output_str); + } } buf_wtr.print(&wtr).ok(); } -/// 各レベル毎で最も高い検知数を出した日付を出力する +/// 各レベル毎で最も高い検知数を出したコンピュータ名を出力する fn _print_detection_summary_by_computer( detect_counts_by_computer: HashMap>, color_map: &HashMap, + html_output_stock: &mut Vec, ) { let buf_wtr = BufferWriter::stdout(ColorChoice::Always); let mut wtr = buf_wtr.buffer(); wtr.set_color(ColorSpec::new().set_fg(None)).ok(); writeln!(wtr, "Top 5 computers with most unique detections:").ok(); + if configs::CONFIG.read().unwrap().args.html_report.is_some() { + html_output_stock.push("Computers with most unique critical detections:".to_string()); + } for level in LEVEL_ABBR.values() { // output_levelsはlevelsからundefinedを除外した配列であり、各要素は必ず初期化されているのでSomeであることが保証されているのでunwrapをそのまま実施 let detections_by_computer = detect_counts_by_computer.get(level).unwrap(); @@ -700,6 +745,17 @@ fn _print_detection_summary_by_computer( sorted_detections.sort_by(|a, b| (-a.1).cmp(&(-b.1))); + // html出力は各種すべてのコンピュータ名を表示するようにする + if configs::CONFIG.read().unwrap().args.html_report.is_some() { + for x in sorted_detections.iter() { + html_output_stock.push(format!( + "{} ({})", + x.0, + x.1.to_formatted_string(&Locale::en) + )); + } + html_output_stock.push("".to_string()); + } for x in sorted_detections.iter().take(5) { result_vec.push(format!( "{} ({})", @@ -733,6 +789,7 @@ fn _print_detection_summary_by_computer( fn _print_detection_summary_tables( detect_counts_by_rule_and_level: HashMap>, color_map: &HashMap, + html_output_stock: &mut Vec, ) { let buf_wtr = BufferWriter::stdout(ColorChoice::Always); let mut wtr = buf_wtr.buffer(); @@ -741,10 +798,8 @@ fn _print_detection_summary_tables( let mut col_color = vec![]; for level in LEVEL_ABBR.values() { let mut col_output: Vec = vec![]; - col_output.push(format!( - "Top {} alerts:", - LEVEL_FULL.get(level.as_str()).unwrap() - )); + let header_output = &format!("Top {} alerts:", LEVEL_FULL.get(level.as_str()).unwrap()); + col_output.push(header_output.to_owned()); col_color.push(_get_table_color( color_map, @@ -757,6 +812,19 @@ fn _print_detection_summary_tables( sorted_detections.sort_by(|a, b| (-a.1).cmp(&(-b.1))); + // html出力の場合はすべての内容を出力するようにする + if configs::CONFIG.read().unwrap().args.html_report.is_some() { + html_output_stock.push(header_output.to_string()); + for x in sorted_detections.iter() { + html_output_stock.push(format!( + "{} ({})", + x.0, + x.1.to_formatted_string(&Locale::en) + )); + } + html_output_stock.push("".to_string()); + } + let take_cnt = if LEVEL_FULL.get(level.as_str()).unwrap_or(&"-".to_string()) == "informational" { 10 diff --git a/src/detections/detection.rs b/src/detections/detection.rs index 019dd95e..ec7a22bd 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -666,8 +666,7 @@ impl Detection { ) .ok(); if configs::CONFIG.read().unwrap().args.html_report.is_some() { - html_report_stock.push(format!( - "- {}", output_str)); + html_report_stock.push(format!("- {}", output_str)); } } }); @@ -697,10 +696,7 @@ impl Detection { html_report_stock.push(format!("- {}", tmp_total_detect_output)); } if !html_report_stock.is_empty() { - htmlreport::add_md_data( - "General Overview".to_string(), - html_report_stock - ); + htmlreport::add_md_data("General Overview".to_string(), html_report_stock); } } }