From 9bfe55b2075de9278b3d323b6110e325a8f126ab Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Fri, 15 Nov 2024 17:38:59 +0900 Subject: [PATCH 01/22] feat: output field information in green and messages in orange --- src/detections/detection.rs | 130 +++++++++--- src/detections/utils.rs | 35 ++-- src/main.rs | 387 +++++++++++++++++++++++++++--------- 3 files changed, 415 insertions(+), 137 deletions(-) diff --git a/src/detections/detection.rs b/src/detections/detection.rs index 72191078d..6b103f344 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -1,11 +1,5 @@ extern crate csv; -use std::collections::HashSet; -use std::default::Default; -use std::fmt::Write; -use std::path::Path; -use std::sync::Arc; - use chrono::{TimeZone, Utc}; use compact_str::CompactString; use hashbrown::HashMap; @@ -13,6 +7,11 @@ use itertools::Itertools; use nested::Nested; use num_format::{Locale, ToFormattedString}; use serde_json::Value; +use std::collections::HashSet; +use std::default::Default; +use std::fmt::Write; +use std::path::Path; +use std::sync::Arc; use termcolor::{BufferWriter, Color, ColorChoice}; use tokio::{runtime::Runtime, spawn, task::JoinHandle}; use yaml_rust2::Yaml; @@ -24,7 +23,9 @@ use crate::detections::message::{AlertMessage, DetectInfo, ERROR_LOG_STACK, TAGS use crate::detections::rule::correlation_parser::parse_correlation_rules; use crate::detections::rule::count::AggRecordTimeInfo; use crate::detections::rule::{self, AggResult, RuleNode}; -use crate::detections::utils::{create_recordinfos, format_time, write_color_buffer}; +use crate::detections::utils::{ + create_recordinfos, format_time, get_writable_color, write_color_buffer, +}; use crate::detections::utils::{get_serde_number_to_string, make_ascii_titlecase}; use crate::filter; use crate::options::htmlreport; @@ -1142,14 +1143,24 @@ impl Detection { "" }; //タイトルに利用するものはascii文字であることを前提として1文字目を大文字にするように変更する - let output_str = format!( - "{} rules: {}{}", - make_ascii_titlecase(key), - value.to_formatted_string(&Locale::en), - disable_flag - ); - println!("{output_str}"); + let key = format!("{} rules: ", make_ascii_titlecase(key)); + let val = format!("{}{}", value.to_formatted_string(&Locale::en), disable_flag); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + Some(Color::Rgb(0, 255, 0)), + key.as_str(), + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + val.as_str(), + true, + ) + .ok(); if stored_static.html_report_flag { + let output_str = format!("{}{}", key, val); html_report_stock.push(format!("- {output_str}")); } } @@ -1188,22 +1199,29 @@ impl Detection { } else { "" }; - let output_str = format!( - "{} rules: {} ({:.2}%){}", - make_ascii_titlecase(key), + let key = format!("{} rules: ", make_ascii_titlecase(key)); + let val = format!( + "{} ({:.2}%){}", value.to_formatted_string(&Locale::en), rate, disabled_flag ); - //タイトルに利用するものはascii文字であることを前提として1文字目を大文字にするように変更する + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + Some(Color::Rgb(0, 255, 0)), + key.as_str(), + false, + ) + .ok(); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), None, - &output_str, + val.as_str(), true, ) .ok(); if stored_static.html_report_flag { + let output_str = format!("{}{}", key, val); html_report_stock.push(format!("- {output_str}")); } } @@ -1213,24 +1231,54 @@ impl Detection { let cor_total: u128 = cor_rc.values().sum(); let cor_ref_total: u128 = cor_ref_rc.values().sum(); if cor_total != 0 { - let col = format!( - "Correlation rules: {} ({:.2}%)", + let key = "Correlation rules: "; + let val = format!( + "{} ({:.2}%)", cor_total.to_formatted_string(&Locale::en), (cor_total as f64) / (total_loaded_rule_cnt as f64) * 100.0 ); - write_color_buffer(&BufferWriter::stdout(ColorChoice::Always), None, &col, true).ok(); - let col_ref = format!( - "Correlation referenced rules: {} ({:.2}%)", + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + key, + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color(None, stored_static.common_options.no_color), + val.as_str(), + true, + ) + .ok(); + let col = format!("{}{}", key, val); + let key = "Correlation referenced rules: "; + let val = format!( + "{} ({:.2}%)", cor_ref_total.to_formatted_string(&Locale::en), (cor_ref_total as f64) / (total_loaded_rule_cnt as f64) * 100.0 ); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + key, + false, + ) + .ok(); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), None, - &col_ref, + val.as_str(), true, ) .ok(); + let col_ref = format!("{}{}", key, val); if stored_static.html_report_flag { html_report_stock.push(format!("- {col}")); html_report_stock.push(format!("- {col_ref}")); @@ -1241,24 +1289,46 @@ impl Detection { let mut sorted_rc: Vec<(&CompactString, &u128)> = rc.iter().collect(); sorted_rc.sort_by(|a, b| a.0.cmp(b.0)); sorted_rc.into_iter().for_each(|(key, value)| { - let output_str = format!("{key} rules: {}", value.to_formatted_string(&Locale::en)); + let key = format!("{key} rules: "); + let val = value.to_formatted_string(&Locale::en); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + Some(Color::Rgb(0, 255, 0)), + key.as_str(), + false, + ) + .ok(); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), None, - &output_str, + val.as_str(), true, ) .ok(); if stored_static.html_report_flag { - html_report_stock.push(format!("- {output_str}")); + html_report_stock.push(format!("- {key}{val}")); } }); - + let key = "Total detection rules: "; + let val = total_loaded_rule_cnt.to_formatted_string(&Locale::en); let tmp_total_detect_output = format!( "Total detection rules: {}", total_loaded_rule_cnt.to_formatted_string(&Locale::en) ); - println!("{tmp_total_detect_output}"); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + Some(Color::Rgb(0, 255, 0)), + key, + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + val.as_str(), + true, + ) + .ok(); println!(); if stored_static.html_report_flag { html_report_stock.push(format!("- {tmp_total_detect_output}")); diff --git a/src/detections/utils.rs b/src/detections/utils.rs index a86678b0c..c33fd8087 100644 --- a/src/detections/utils.rs +++ b/src/detections/utils.rs @@ -704,17 +704,9 @@ pub fn output_and_data_stack_for_html( section_name: &str, html_report_flag: &bool, ) { - write_color_buffer( - &BufferWriter::stdout(ColorChoice::Always), - None, - output_str, - true, - ) - .ok(); - if *html_report_flag { let mut output_data = Nested::::new(); - output_data.extend(vec![format!("- {output_str}")]); + output_data.extend(vec![format!("- Elapsed time: {output_str}")]); htmlreport::add_md_data(section_name, output_data); } } @@ -723,7 +715,7 @@ pub fn contains_str(input: &str, check: &str) -> bool { memmem::find(input.as_bytes(), check.as_bytes()).is_some() } -pub fn output_profile_name(output_option: &Option, stdout: bool) { +pub fn output_profile_name(output_option: &Option, stdout: bool, no_color: bool) { // output profile name if let Some(profile_opt) = output_option { // default profile name check @@ -740,7 +732,7 @@ pub fn output_profile_name(output_option: &Option, stdout: bool) { name.trim().to_string() } else { let default_profile_name = DefaultProfileName::get("default_profile_name.txt").unwrap(); - std::str::from_utf8(default_profile_name.data.as_ref()) + str::from_utf8(default_profile_name.data.as_ref()) .unwrap_or("n/a") .to_string() }; @@ -750,10 +742,23 @@ pub fn output_profile_name(output_option: &Option, stdout: bool) { .profile .as_ref() .unwrap_or(&default_profile_name); - let output_saved_str = format!("Output profile: {profile_name}"); if stdout { - println!("{output_saved_str}"); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color(Some(Color::Rgb(0, 255, 0)), no_color), + "Output profile: ", + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + profile_name, + true, + ) + .ok(); } + let output_saved_str = format!("Output profile: {profile_name}"); // profileの表示位置とHTMLの出力順が異なるため引数で管理をした if !stdout && profile_opt.html_report.is_some() { htmlreport::add_md_data( @@ -1175,8 +1180,8 @@ mod tests { })), debug: false, })); - output_profile_name(&stored_static.output_option, true); - output_profile_name(&stored_static.output_option, false); + output_profile_name(&stored_static.output_option, true, false); + output_profile_name(&stored_static.output_option, false, false); let expect: HashMap<&str, Nested> = HashMap::from_iter(vec![ ("Results Summary {#results_summary}", Nested::new()), ( diff --git a/src/main.rs b/src/main.rs index 5cc99644d..6e9a2ab0e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -184,8 +184,7 @@ impl App { // カレントディレクトリ以外からの実行の際にrules-configオプションの指定がないとエラーが発生することを防ぐための処理 if stored_static.config_path == Path::new("./rules/config") { stored_static.config_path = - utils::check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), "rules/config", true) - .unwrap(); + check_setting_path(&CURRENT_EXE_PATH.to_path_buf(), "rules/config", true).unwrap(); } let time_filter = TargetEventTime::new(stored_static); @@ -196,7 +195,10 @@ impl App { if stored_static.metrics_flag { write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - None, + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), "Generating Event ID Metrics", true, ) @@ -206,7 +208,10 @@ impl App { if stored_static.logon_summary_flag { write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - None, + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), "Generating Logon Summary", true, ) @@ -216,7 +221,10 @@ impl App { if stored_static.search_flag { write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - None, + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), "Searching...", true, ) @@ -226,12 +234,19 @@ impl App { let _ = self.output_open_close_message("opening_messages.txt", stored_static); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - None, - &format!( - "Start time: {}\n", - analysis_start_time.format("%Y/%m/%d %H:%M") + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, ), - true, + "Start time: ", + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + &format!("{}\n", analysis_start_time.format("%Y/%m/%d %H:%M")), + false, ) .ok(); CHECKPOINT @@ -334,7 +349,11 @@ impl App { } self.analysis_start(&target_extensions, &time_filter, stored_static); - output_profile_name(&stored_static.output_option, false); + output_profile_name( + &stored_static.output_option, + false, + stored_static.common_options.no_color, + ); output_saved_file( &stored_static.output_path, "Saved file", @@ -471,7 +490,7 @@ impl App { //ファイル出力の場合 pivot_key_unions.iter().for_each(|(key, pivot_keyword)| { let mut f = BufWriter::new( - fs::File::create( + File::create( pivot_file.as_path().display().to_string() + "-" + key + ".txt", ) .unwrap(), @@ -511,12 +530,14 @@ impl App { let output = "\nThe following pivot keywords were found:\n"; write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - None, + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), output, true, ) .ok(); - pivot_key_unions.iter().for_each(|(key, pivot_keyword)| { create_output( String::default(), @@ -561,7 +582,10 @@ impl App { copy(&mut res.into_reader(), &mut dst).unwrap(); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - None, + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), "Rules file encoded_rules.yml updated successfully.", true, ) @@ -581,7 +605,10 @@ impl App { copy(&mut res.into_reader(), &mut dst).unwrap(); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - None, + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), "Config file rules_config_files.txt updated successfully.", true, ) @@ -601,7 +628,10 @@ impl App { if output != "You currently have the latest rules." { write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - None, + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), "Rules updated successfully.", true, ) @@ -633,7 +663,10 @@ impl App { if split_latest_version > split_now_version { write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - None, + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), &format!( "There is a new version of Hayabusa: {}", latest_version_data.unwrap().replace('\"', "") @@ -643,7 +676,10 @@ impl App { .ok(); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - None, + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), "You can download it at https://github.com/Yamato-Security/hayabusa/releases", true, ) @@ -654,36 +690,23 @@ impl App { return; } Action::LevelTuning(option) => { - let level_tuning_config_path = if option.level_tuning.to_str().unwrap() - != "./rules/config/level_tuning.txt" - { - utils::check_setting_path( - option - .level_tuning - .parent() - .unwrap_or_else(|| Path::new("")), - option - .level_tuning - .file_name() - .unwrap() - .to_str() - .unwrap_or_default(), - false, - ) - .unwrap_or_else(|| { - utils::check_setting_path( - &CURRENT_EXE_PATH.to_path_buf(), - "rules/config/level_tuning.txt", - true, + let level_tuning_config_path = + if option.level_tuning.to_str().unwrap() != "./rules/config/level_tuning.txt" { + check_setting_path( + option + .level_tuning + .parent() + .unwrap_or_else(|| Path::new("")), + option + .level_tuning + .file_name() + .unwrap() + .to_str() + .unwrap_or_default(), + false, ) - .unwrap() - }) - .display() - .to_string() - } else { - utils::check_setting_path(&stored_static.config_path, "level_tuning.txt", false) .unwrap_or_else(|| { - utils::check_setting_path( + check_setting_path( &CURRENT_EXE_PATH.to_path_buf(), "rules/config/level_tuning.txt", true, @@ -692,7 +715,19 @@ impl App { }) .display() .to_string() - }; + } else { + check_setting_path(&stored_static.config_path, "level_tuning.txt", false) + .unwrap_or_else(|| { + check_setting_path( + &CURRENT_EXE_PATH.to_path_buf(), + "rules/config/level_tuning.txt", + true, + ) + .unwrap() + }) + .display() + .to_string() + }; let rules_path = if stored_static.output_option.as_ref().is_some() { stored_static @@ -757,7 +792,10 @@ impl App { let profile_list = options::profile::get_profile_list("config/profiles.yaml"); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - None, + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), "List of available profiles:", true, ) @@ -784,14 +822,28 @@ impl App { } // 処理時間の出力 - let elapsed_output_str = format!( - "Elapsed time: {}", - CHECKPOINT - .lock() - .as_mut() - .unwrap() - .calculate_all_stocked_results() - ); + let elapsed_output_str = CHECKPOINT + .lock() + .as_mut() + .unwrap() + .calculate_all_stocked_results(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + "Elapsed time: ", + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + elapsed_output_str.as_str(), + true, + ) + .ok(); output_and_data_stack_for_html( &elapsed_output_str, "General Overview {#general_overview}", @@ -800,9 +852,57 @@ impl App { match stored_static.config.action { Some(Action::CsvTimeline(_)) | Some(Action::JsonTimeline(_)) => { println!(); - println!("Please report any issues with Hayabusa rules to: https://github.com/Yamato-Security/hayabusa-rules/issues"); - println!("Please report any false positives with Sigma rules to: https://github.com/SigmaHQ/sigma/issues"); - println!("Please submit new Sigma rules with pull requests to: https://github.com/SigmaHQ/sigma/pulls"); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + "Please report any issues with Hayabusa rules to: ", + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color(None, stored_static.common_options.no_color), + "https://github.com/Yamato-Security/hayabusa-rules/issues", + true, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + "Please report any false positives with Sigma rules to: ", + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color(None, stored_static.common_options.no_color), + "https://github.com/SigmaHQ/sigma/issues", + true, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + "Please submit new Sigma rules with pull requests to: ", + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + "https://github.com/SigmaHQ/sigma/pulls", + true, + ) + .ok(); } _ => {} } @@ -1055,11 +1155,19 @@ impl App { .to_uppercase(); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - None, - &format!( - "Total event log files: {}", - evtx_files.len().to_formatted_string(&Locale::en) + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, ), + "Total event log files: ", + false, + ) + .ok(); + let log_files = &evtx_files.len().to_formatted_string(&Locale::en); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + log_files, true, ) .ok(); @@ -1082,8 +1190,24 @@ impl App { }; total_file_size += ByteSize::b(file_size); } - let total_size_output = format!("Total file size: {}", total_file_size.to_string_as(false)); - println!("{total_size_output}"); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + "Total file size: ", + false, + ) + .ok(); + let total_size = total_file_size.to_string_as(false); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + &total_file_size.to_string_as(false), + true, + ) + .ok(); let mut status_append_output = None; if !(stored_static.metrics_flag || stored_static.logon_summary_flag @@ -1112,8 +1236,13 @@ impl App { ("CRITICAL", 5), ]); println!(); - println!("Scan wizard:"); - println!(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + Some(Color::Rgb(0, 255, 0)), + "Scan wizard:", + true, + ) + .ok(); let calcurate_wizard_rule_count = |exclude_noisytarget_flag: bool, exclude_noisy_status: Vec<&str>, min_level: &str, @@ -1428,7 +1557,7 @@ impl App { "- Analyzed event files: {}", evtx_files.len().to_formatted_string(&Locale::en) ), - format!("- {total_size_output}"), + format!("- {total_size}"), ]); if let Some(status_report) = status_append_output { html_report_data.push(format!("- Selected deteciton rule set: {status_report}")); @@ -1454,24 +1583,34 @@ impl App { .unwrap() .min_level .to_uppercase(); - println!(); + let mut wait_message = ""; if !(stored_static.logon_summary_flag || stored_static.search_flag || stored_static.metrics_flag || stored_static.computer_metrics_flag || stored_static.log_metrics_flag) { - println!("Loading detection rules. Please wait."); + wait_message = "Loading detection rules. Please wait."; } else if stored_static.logon_summary_flag { - println!("Currently scanning for the logon summary. Please wait."); + wait_message = "Currently scanning for the logon summary. Please wait."; } else if stored_static.search_flag { - println!("Currently searching. Please wait."); + wait_message = "Currently searching. Please wait."; } else if stored_static.metrics_flag { - println!("Currently scanning for event ID metrics. Please wait."); + wait_message = "Currently scanning for event ID metrics. Please wait."; } else if stored_static.computer_metrics_flag { - println!("Currently scanning for computer metrics. Please wait."); + wait_message = "Currently scanning for computer metrics. Please wait."; } else if stored_static.log_metrics_flag { - println!("Currently scanning for log metrics. Please wait."); + wait_message = "Currently scanning for log metrics. Please wait."; + } + if !wait_message.is_empty() { + println!(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + Some(Color::Rgb(255, 175, 0)), + wait_message, + true, + ) + .ok(); } println!(); @@ -1515,33 +1654,78 @@ impl App { && !stored_static.scan_all_evtx_files && !stored_static.enable_all_rules { - println!("Creating the channel filter. Please wait."); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + Some(Color::Rgb(255, 175, 0)), + "Creating the channel filter. Please wait.", + true, + ) + .ok(); println!(); let mut channel_filter = create_channel_filter(&evtx_files, &rule_files); if !stored_static.scan_all_evtx_files { evtx_files.retain(|e| channel_filter.scanable_rule_exists(e)); - let evtx_files_after_channel_filter = format!( - "Evtx files loaded after channel filter: {}", - (evtx_files.len()).to_formatted_string(&Locale::en) - ); - println!("{evtx_files_after_channel_filter}"); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + "Evtx files loaded after channel filter: ", + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + evtx_files.len().to_formatted_string(&Locale::en).as_str(), + true, + ) + .ok(); } if !stored_static.enable_all_rules { rule_files.retain(|r| { channel_filter.rulepathes.contains(&r.rulepath) || !r.yaml["correlation"].is_badvalue() }); - let rules_after_channel_filter = format!( - "Detection rules enabled after channel filter: {}", - (rule_files.len()).to_formatted_string(&Locale::en) - ); - println!("{rules_after_channel_filter}"); + let rules_after_channel_filter = + rule_files.len().to_formatted_string(&Locale::en); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + "Detection rules enabled after channel filter: ", + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + rules_after_channel_filter.as_str(), + true, + ) + .ok(); println!(); } } - output_profile_name(&stored_static.output_option, true); + output_profile_name( + &stored_static.output_option, + true, + stored_static.common_options.no_color, + ); println!(); - println!("Scanning in progress. Please wait."); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), + "Scanning in progress. Please wait.", + true, + ) + .ok(); println!(); } @@ -1675,7 +1859,6 @@ impl App { "Scanning finished.\n" }; pb.finish_with_message(msg); - // output afterfact if stored_static.is_low_memory { afterfact::output_additional_afterfact( @@ -2400,12 +2583,32 @@ impl App { let lines: Vec = io::BufReader::new(file).lines().collect::>()?; if let Some(random_line) = lines.choose(&mut rand::thread_rng()) { - println!("{}\n", random_line); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), + random_line, + true, + ) + .ok(); + println!() } } else if let Some(contents) = ONE_CONFIG_MAP.get(file_path) { let lines: Vec<&str> = contents.lines().collect(); if let Some(random_line) = lines.choose(&mut rand::thread_rng()) { - println!("{}\n", random_line); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), + random_line, + true, + ) + .ok(); + println!() } } } From 8100cd890df0cd6d6b4b7d67d86282347f3342dd Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Fri, 15 Nov 2024 17:51:14 +0900 Subject: [PATCH 02/22] test: add -C option --- .github/workflows/integration-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 26983a3c3..fdf609933 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -45,7 +45,7 @@ jobs: run: cd main && cargo run --release -- computer-metrics -d ../hayabusa-sample-evtx -q -o out.csv -C - name: csv-timeline - run: cd main && cargo run --release -- csv-timeline -d ../hayabusa-sample-evtx -o out.csv -q -w -D -n -u + run: cd main && cargo run --release -- csv-timeline -d ../hayabusa-sample-evtx -o out.csv -q -w -D -n -u -C - name: csv-timeline(-p super-verbose) run: cd main && cargo run --release -- csv-timeline -d ../hayabusa-sample-evtx -o out-s.csv -p super-verbose -q -w -D -n -u From bdd77b68397394f1111e2afbe9801ef64153da82 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Fri, 15 Nov 2024 17:55:30 +0900 Subject: [PATCH 03/22] fix: --no-color case --- src/detections/detection.rs | 10 ++++++++-- src/main.rs | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/detections/detection.rs b/src/detections/detection.rs index 6b103f344..f951c56d9 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -1147,7 +1147,10 @@ impl Detection { let val = format!("{}{}", value.to_formatted_string(&Locale::en), disable_flag); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - Some(Color::Rgb(0, 255, 0)), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), key.as_str(), false, ) @@ -1208,7 +1211,10 @@ impl Detection { ); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - Some(Color::Rgb(0, 255, 0)), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), key.as_str(), false, ) diff --git a/src/main.rs b/src/main.rs index 6e9a2ab0e..473f04c7d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1606,7 +1606,10 @@ impl App { println!(); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - Some(Color::Rgb(255, 175, 0)), + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), wait_message, true, ) @@ -1656,7 +1659,10 @@ impl App { { write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - Some(Color::Rgb(255, 175, 0)), + get_writable_color( + Some(Color::Rgb(255, 175, 0)), + stored_static.common_options.no_color, + ), "Creating the channel filter. Please wait.", true, ) From c66945d2f1750f9278cd9a35f03a83795a690e16 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Fri, 15 Nov 2024 17:58:02 +0900 Subject: [PATCH 04/22] fix: --no-color case --- src/detections/detection.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/detections/detection.rs b/src/detections/detection.rs index f951c56d9..e8cd82255 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -1299,7 +1299,10 @@ impl Detection { let val = value.to_formatted_string(&Locale::en); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - Some(Color::Rgb(0, 255, 0)), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), key.as_str(), false, ) From 5381b7b397c214e0b6632ef7fdeead224f496a47 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Fri, 15 Nov 2024 18:03:18 +0900 Subject: [PATCH 05/22] fix: --no-color case --- src/detections/detection.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/detections/detection.rs b/src/detections/detection.rs index e8cd82255..8d712e097 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -1326,7 +1326,10 @@ impl Detection { ); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - Some(Color::Rgb(0, 255, 0)), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), key, false, ) From 33e521d295b5b9e0fe3540ae4fa1d8b2d7a1c52e Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Fri, 15 Nov 2024 18:55:19 +0900 Subject: [PATCH 06/22] update changelog --- CHANGELOG-Japanese.md | 8 +++++++- CHANGELOG.md | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG-Japanese.md b/CHANGELOG-Japanese.md index 2d67e7ed0..c075f617f 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -6,7 +6,12 @@ - `gt`、`gte`、`lt`、`lte`のフィールドモディファイアに対応した。(#1433) (@fukusuket) - 新しい`log-metrics`コマンドで`.evtx`ファイルの情報を取得できるようになった。(コンピュータ名、イベント数、最初のタイムスタンプ、最後のタイムスタンプ、チャネル、プロバイダ) (#1474) (@fukusuket) -- 新しい`-b, --disable-abbreviations`コマンドは、`Channel`と`Provider`の略称を無効にする。元の値を確認したい時に便利。 (#1485) (@fukusuket) +- 以下のコマンドに`Channel`と`Provider`の略称を無効にする`-b, --disable-abbreviations`オプションを追加した。元の値を確認したい時に便利。 (#1485) (@fukusuket) + * csv-timeline + * json-timeline + * eid-metrics + * log-metrics + * search **改善:** @@ -15,6 +20,7 @@ - `logon-summary`コマンドがRDPイベントからのログオン情報を表示するようになった。注意: ファイルに保存する場合、Hayabusaはより詳細な情報を出力する。(#1468) (@fukusuket) - 見やすくなるように色を更新した。 (#1480) (@yamatosecurity) - 実行開始と終了のメッセージを出力するようにした。 (#1492) (@fukusuket) +- 出力に新しい配色を追加した。 (#1491) (@fukusuket) **バグ修正:** diff --git a/CHANGELOG.md b/CHANGELOG.md index b07cd7bbe..7fa319025 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,12 @@ - Support for the `gt`, `gte`, `lt`, `lte` field modifiers. (#1433) (@fukusuket) - New `log-metrics` command to get information about `.evtx` files. (computer names, event count, first timestamp, last timestamp, channels, providers) (#1474) (@fukusuket) -- New `-b, --disable-abbreviations` command to disable `Channel` and `Provider` abbreviations for when you want to check the original values. (#1485) (@fukusuket) +- New `-b, --disable-abbreviations` options for the following commands to disable `Channel` and `Provider` abbreviations for when you want to check the original values. (#1485) (@fukusuket) + * csv-timeline + * json-timeline + * eid-metrics + * log-metrics + * search **Enhancements:** @@ -15,6 +20,7 @@ - `logon-summary` command now displays logon information from RDP events. Note: Hayabusa will output more detailed information when saving to a file. (#1468) (@fukusuket) - The colors were updated to make it easier to read. (#1480) (@yamatosecurity) - Added start and finish messages of the day. (#1492) (@fukusuket) +- New color scheme added to output. (#1491) (@fukusuket) **Bug Fixes:** From 2b266e8b84fc4e12294798269f9d83cb55d6fc22 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Fri, 15 Nov 2024 19:59:54 +0900 Subject: [PATCH 07/22] feat: output field information in green and messages in orange(pivot-keywords-list) --- src/main.rs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 473f04c7d..d481ff84f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -442,7 +442,7 @@ impl App { } Action::PivotKeywordsList(_) => { load_pivot_keywords( - utils::check_setting_path( + check_setting_path( &CURRENT_EXE_PATH.to_path_buf(), "rules/config/pivot_keywords.txt", true, @@ -507,9 +507,19 @@ impl App { ) .unwrap(); }); - let mut output = - "Pivot keyword results were saved to the following files:\n".to_string(); - + println!(); + println!(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + "Pivot keyword results were saved to the following files: ", + true, + ) + .ok(); + let mut output = "".to_string(); pivot_key_unions.iter().for_each(|(key, _)| { writeln!( output, @@ -520,18 +530,18 @@ impl App { }); write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), - None, - &output, + get_writable_color(None, stored_static.common_options.no_color), + output.as_str(), true, ) .ok(); } else { //標準出力の場合 - let output = "\nThe following pivot keywords were found:\n"; + let output = "The following pivot keywords were found:"; write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), get_writable_color( - Some(Color::Rgb(255, 175, 0)), + Some(Color::Rgb(0, 255, 0)), stored_static.common_options.no_color, ), output, From e298d0a46da995b777c4e372fbec792aea04bb6a Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Fri, 15 Nov 2024 20:10:19 +0900 Subject: [PATCH 08/22] feat: output field information in green and messages in orange(computer-metrics) --- src/timeline/timelines.rs | 40 +++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/timeline/timelines.rs b/src/timeline/timelines.rs index f63429e7d..8776392c9 100644 --- a/src/timeline/timelines.rs +++ b/src/timeline/timelines.rs @@ -1,7 +1,9 @@ use crate::detections::configs::{Action, EventInfoConfig, StoredStatic}; use crate::detections::detection::EvtxRecordInfo; use crate::detections::message::AlertMessage; -use crate::detections::utils::{self, make_ascii_titlecase, write_color_buffer}; +use crate::detections::utils::{ + self, get_writable_color, make_ascii_titlecase, write_color_buffer, +}; use crate::timeline::search::search_result_dsp_msg; use comfy_table::modifiers::UTF8_ROUND_CORNERS; use comfy_table::presets::UTF8_FULL; @@ -483,7 +485,6 @@ impl Timeline { if let Action::ComputerMetrics(computer_metrics_option) = &stored_static.config.action.as_ref().unwrap() { - let mut sammsges: Nested = Nested::new(); if self.stats.stats_list.is_empty() { write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), @@ -493,18 +494,29 @@ impl Timeline { ) .ok(); } else { - sammsges.push(format!( - "\nTotal computers: {}", - self.stats.stats_list.len().to_formatted_string(&Locale::en) - )); - } - println!(); - computer_metrics::computer_metrics_dsp_msg( - &self.stats.stats_list, - &computer_metrics_option.output, - ); - for msgprint in sammsges.iter() { - println!("{}", msgprint); + println!(); + println!(); + computer_metrics::computer_metrics_dsp_msg( + &self.stats.stats_list, + &computer_metrics_option.output, + ); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + "Total computers: ", + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + &self.stats.stats_list.len().to_formatted_string(&Locale::en), + true, + ) + .ok(); } } } From bf1d2dfb8640232f76553913753789e91bf7f8c5 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Fri, 15 Nov 2024 20:29:40 +0900 Subject: [PATCH 09/22] feat: output field information in green and messages in orange(logon-summary) --- src/timeline/timelines.rs | 53 +++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/src/timeline/timelines.rs b/src/timeline/timelines.rs index 8776392c9..61fad023c 100644 --- a/src/timeline/timelines.rs +++ b/src/timeline/timelines.rs @@ -272,10 +272,32 @@ impl Timeline { } for msgprint in sammsges.iter() { - println!("{msgprint}"); + let mut parts = msgprint.splitn(2, ':'); + let first_part = parts.next().unwrap_or_default(); + let second_part = format!(": {}", parts.next().unwrap_or_default()); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + first_part, + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + second_part.as_str(), + true, + ) + .ok(); } - self.tm_loginstats_tb_set_msg(&logon_summary_option.output); + self.tm_loginstats_tb_set_msg( + &logon_summary_option.output, + stored_static.common_options.no_color, + ); } } @@ -328,9 +350,16 @@ impl Timeline { } /// ユーザ毎のログイン統計情報出力メッセージ生成 - fn tm_loginstats_tb_set_msg(&self, output: &Option) { + fn tm_loginstats_tb_set_msg(&self, output: &Option, no_color: bool) { if output.is_none() { - println!("Logon Summary:\n"); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color(Some(Color::Rgb(0, 255, 0)), no_color), + "Logon Summary:", + true, + ) + .ok(); + write_color_buffer(&BufferWriter::stdout(ColorChoice::Always), None, "", false).ok(); } if self.stats.stats_login_list.is_empty() { let mut loginmsges: Vec = Vec::new(); @@ -341,16 +370,16 @@ impl Timeline { println!("{msgprint}"); } } else { - self.tm_loginstats_tb_dsp_msg("successful", output); + self.tm_loginstats_tb_dsp_msg("successful", output, no_color); if output.is_none() { println!("\n\n"); } - self.tm_loginstats_tb_dsp_msg("failed", output); + self.tm_loginstats_tb_dsp_msg("failed", output, no_color); } } /// ユーザ毎のログイン統計情報出力 - fn tm_loginstats_tb_dsp_msg(&self, logon_res: &str, output: &Option) { + fn tm_loginstats_tb_dsp_msg(&self, logon_res: &str, output: &Option, no_color: bool) { let header_column = make_ascii_titlecase(logon_res); let header = vec![ header_column.as_str(), @@ -366,7 +395,15 @@ impl Timeline { ]; let target; if output.is_none() { - println!("{} Logons:", make_ascii_titlecase(logon_res)); + let msg = format!("{} Logons:", make_ascii_titlecase(logon_res)); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color(Some(Color::Rgb(0, 255, 0)), no_color), + msg.as_str(), + true, + ) + .ok(); + write_color_buffer(&BufferWriter::stdout(ColorChoice::Always), None, "", false).ok(); } let mut wtr = if let Some(csv_path) = output { let file_name = csv_path.as_path().display().to_string() + "-" + logon_res + ".csv"; From 6d75d165285ae701ab709d497d7a5eb78afcc946 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Fri, 15 Nov 2024 20:37:36 +0900 Subject: [PATCH 10/22] feat: output field information in green and messages in orange(search) --- src/timeline/timelines.rs | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/timeline/timelines.rs b/src/timeline/timelines.rs index 61fad023c..8f15fb1c5 100644 --- a/src/timeline/timelines.rs +++ b/src/timeline/timelines.rs @@ -479,7 +479,6 @@ impl Timeline { event_timeline_config: &EventInfoConfig, stored_static: &StoredStatic, ) { - let mut sammsges: Vec = Vec::new(); if let Action::Search(search_summary_option) = &stored_static.config.action.as_ref().unwrap() { @@ -491,14 +490,6 @@ impl Timeline { true, ) .ok(); - } else { - sammsges.push(format!( - "\nTotal findings: {}", - self.event_search - .search_result - .len() - .to_formatted_string(&Locale::en) - )); } let search_result = self.event_search.search_result.clone(); search_result_dsp_msg( @@ -511,9 +502,27 @@ impl Timeline { search_summary_option.jsonl_output, ), ); - for msgprint in sammsges.iter() { - println!("{}", msgprint); - } + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + "Total findings: ", + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + self.event_search + .search_result + .len() + .to_formatted_string(&Locale::en) + .as_str(), + true, + ) + .ok(); } } From 8a22b073193b0f06412e6ef035733988e22fb8cf Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Fri, 15 Nov 2024 20:46:14 +0900 Subject: [PATCH 11/22] feat: output field information in green and messages in orange(list-profile) --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index d481ff84f..51b552d97 100644 --- a/src/main.rs +++ b/src/main.rs @@ -803,7 +803,7 @@ impl App { write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), get_writable_color( - Some(Color::Rgb(255, 175, 0)), + Some(Color::Rgb(0, 255, 0)), stored_static.common_options.no_color, ), "List of available profiles:", From a853d791b60fc021145c4cdf9a381ebb6bece027 Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Sat, 16 Nov 2024 08:03:49 +0900 Subject: [PATCH 12/22] make scanning finished orange --- src/main.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 51b552d97..691cd3565 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1870,11 +1870,13 @@ impl App { afterfact_info.tl_endtime = tl.stats.end_time; let msg = if stored_static.output_path.is_some() { - "Scanning finished. Please wait while the results are being saved.\n" + style("Scanning finished. Please wait while the results are being saved.\n").color256(214) } else { - "Scanning finished.\n" + style("Scanning finished.\n").color256(214) }; - pb.finish_with_message(msg); + // Convert the ColoredString to a String before passing it + pb.finish_with_message(msg.to_string()); + // output afterfact if stored_static.is_low_memory { afterfact::output_additional_afterfact( From 770322875e294b4985ef2464bd28b2400bf4205c Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Sat, 16 Nov 2024 08:07:44 +0900 Subject: [PATCH 13/22] cargo fmt --- src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 691cd3565..75eb3648d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1870,7 +1870,8 @@ impl App { afterfact_info.tl_endtime = tl.stats.end_time; let msg = if stored_static.output_path.is_some() { - style("Scanning finished. Please wait while the results are being saved.\n").color256(214) + style("Scanning finished. Please wait while the results are being saved.\n") + .color256(214) } else { style("Scanning finished.\n").color256(214) }; From b4a98f74daae790758174048cac3c757bc4a9ee5 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Sat, 16 Nov 2024 08:09:07 +0900 Subject: [PATCH 14/22] feat: output field information in green and messages in orange(eid-metrics) --- src/timeline/timelines.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/timeline/timelines.rs b/src/timeline/timelines.rs index 8f15fb1c5..d347a20b5 100644 --- a/src/timeline/timelines.rs +++ b/src/timeline/timelines.rs @@ -195,7 +195,26 @@ impl Timeline { self.tm_stats_set_msg(mapsorted, event_timeline_config, stored_static); for msgprint in sammsges.iter() { - println!("{msgprint}"); + let mut parts = msgprint.splitn(2, ':'); + let first_part = parts.next().unwrap_or_default(); + let second_part = format!(": {}", parts.next().unwrap_or_default()); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + get_writable_color( + Some(Color::Rgb(0, 255, 0)), + stored_static.common_options.no_color, + ), + first_part, + false, + ) + .ok(); + write_color_buffer( + &BufferWriter::stdout(ColorChoice::Always), + None, + second_part.as_str(), + true, + ) + .ok(); } if wtr.is_some() { for msg in stats_msges.iter() { From 964beb811a045c6e2a1cb5858d1933989a4043a9 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Sat, 16 Nov 2024 08:12:25 +0900 Subject: [PATCH 15/22] feat: add no-color short option(-K) --- src/detections/configs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/detections/configs.rs b/src/detections/configs.rs index 8900c4beb..91f4ff4d7 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -1630,7 +1630,7 @@ pub struct OutputOption { #[derive(Copy, Args, Clone, Debug)] pub struct CommonOptions { /// Disable color output - #[arg(help_heading = Some("Display Settings"), long = "no-color", global = true, display_order = 400)] + #[arg(help_heading = Some("Display Settings"), short = 'K', long = "no-color", global = true, display_order = 400)] pub no_color: bool, /// Quiet mode: do not display the launch banner From ba1da35d92c434281606c8a69aa2ac2d36de380b Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Sat, 16 Nov 2024 08:13:41 +0900 Subject: [PATCH 16/22] feat: add no-color short option(-k) --- src/detections/configs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/detections/configs.rs b/src/detections/configs.rs index 91f4ff4d7..308e78cd5 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -1630,7 +1630,7 @@ pub struct OutputOption { #[derive(Copy, Args, Clone, Debug)] pub struct CommonOptions { /// Disable color output - #[arg(help_heading = Some("Display Settings"), short = 'K', long = "no-color", global = true, display_order = 400)] + #[arg(help_heading = Some("Display Settings"), short = 'k', long = "no-color", global = true, display_order = 400)] pub no_color: bool, /// Quiet mode: do not display the launch banner From 81dfe1cd0ad36dc2a5d690b08ea0fe3230c56cc2 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Sat, 16 Nov 2024 08:25:02 +0900 Subject: [PATCH 17/22] feat: output field information in green and messages in orange(progress bar msg) --- src/main.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 75eb3648d..43777489a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1828,7 +1828,12 @@ impl App { || stored_static.log_metrics_flag); if !is_timeline_cmd { - pb.finish_with_message("Scanning finished."); + let msg = if stored_static.common_options.no_color { + style("Scanning finished.\n").color256(15).to_string() + } else { + style("Scanning finished.\n").color256(214).to_string() + }; + pb.finish_with_message(msg); } CHECKPOINT .lock() @@ -1870,13 +1875,22 @@ impl App { afterfact_info.tl_endtime = tl.stats.end_time; let msg = if stored_static.output_path.is_some() { - style("Scanning finished. Please wait while the results are being saved.\n") - .color256(214) + if stored_static.common_options.no_color { + style("Scanning finished. Please wait while the results are being saved.\n") + .color256(15) + .to_string() + } else { + style("Scanning finished. Please wait while the results are being saved.\n") + .color256(214) + .to_string() + } + } else if stored_static.common_options.no_color { + style("Scanning finished.\n").color256(15).to_string() } else { - style("Scanning finished.\n").color256(214) + style("Scanning finished.\n").color256(214).to_string() }; // Convert the ColoredString to a String before passing it - pb.finish_with_message(msg.to_string()); + pb.finish_with_message(msg); // output afterfact if stored_static.is_low_memory { From dd29c10a86af2b660848b62bcdc07dcd98af08e6 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Sat, 16 Nov 2024 08:47:46 +0900 Subject: [PATCH 18/22] fix: add space --- src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.rs b/src/main.rs index 43777489a..05d93b881 100644 --- a/src/main.rs +++ b/src/main.rs @@ -826,6 +826,7 @@ impl App { ) .ok(); } + println!(); let _ = self.output_open_close_message("closing_messages.txt", stored_static); return; } From 2f7ef487cdf1924544e2deb63a99216db0dfcb99 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Sat, 16 Nov 2024 08:54:34 +0900 Subject: [PATCH 19/22] chg: do not display start time when list-contributors --- src/main.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 05d93b881..76bf1e8cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -232,6 +232,11 @@ impl App { println!(); } let _ = self.output_open_close_message("opening_messages.txt", stored_static); + if let Action::ListContributors(_) = &stored_static.config.action.as_ref().unwrap() { + self.print_contributors(); + return; + } + write_color_buffer( &BufferWriter::stdout(ColorChoice::Always), get_writable_color( @@ -376,10 +381,6 @@ impl App { ) } } - Action::ListContributors(_) => { - self.print_contributors(); - return; - } Action::LogonSummary(_) => { let mut target_output_path = Nested::::new(); if let Some(path) = &stored_static.output_path { @@ -830,6 +831,7 @@ impl App { let _ = self.output_open_close_message("closing_messages.txt", stored_static); return; } + _ => {} } // 処理時間の出力 From f2f6945ee39809487a480bd85f37e9ab62d639ac Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Sat, 16 Nov 2024 08:55:41 +0900 Subject: [PATCH 20/22] chg: no-color short option --- src/detections/configs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/detections/configs.rs b/src/detections/configs.rs index 308e78cd5..91f4ff4d7 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -1630,7 +1630,7 @@ pub struct OutputOption { #[derive(Copy, Args, Clone, Debug)] pub struct CommonOptions { /// Disable color output - #[arg(help_heading = Some("Display Settings"), short = 'k', long = "no-color", global = true, display_order = 400)] + #[arg(help_heading = Some("Display Settings"), short = 'K', long = "no-color", global = true, display_order = 400)] pub no_color: bool, /// Quiet mode: do not display the launch banner From 8d3e8bf4deff4d3c509355f02c527f499e010b3f Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Sat, 16 Nov 2024 08:59:33 +0900 Subject: [PATCH 21/22] contributor typo --- contributors.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributors.txt b/contributors.txt index 0b88a8b24..59287f844 100644 --- a/contributors.txt +++ b/contributors.txt @@ -6,7 +6,7 @@ Garigariganzy (@garigariganzy31): Developer (Event ID metrics implementation, et ItiB (@itiB_S144): Core developer (sigmac hayabusa backend, search command, etc...) James Takai / hachiyone(@hach1yon): Second lead developer (Tokio multi-threading, sigma aggregation logic, sigmac backend, rule creation, sigma count implementation etc…) Kazuminn (@k47_um1n): Core Developer (Many features.) -Matthew Seyer: adding the ability to carve out records +Matthew Seyer: Adding the ability to carve out records Tsubokku (@ytsuboi0322): Translations Yusuke Matsui (@apt773): AD hacking working group leader, rule testing, documentation, research, support, etc... Zach Mathis (@yamatosecurity, Yamato Security Founder): Project leader, tool and concept design, rule creation and tuning, etc… From b4c56cd4f8cde4561ef379e07b4e21f90a91c8b9 Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Sat, 16 Nov 2024 09:13:06 +0900 Subject: [PATCH 22/22] chg: add space --- src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.rs b/src/main.rs index 76bf1e8cc..f08bd9c5d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -578,6 +578,7 @@ impl App { Action::UpdateRules(option) => Some(option.rules.to_owned()), _ => None, }; + println!(); // エラーが出た場合はインターネット接続がそもそもできないなどの問題点もあるためエラー等の出力は行わない let latest_version_data = Update::get_latest_hayabusa_version().unwrap_or_default(); let now_version = &format!("v{}", env!("CARGO_PKG_VERSION")); @@ -769,6 +770,7 @@ impl App { return; } Action::SetDefaultProfile(_) => { + println!(); let is_existed_config_path = CURRENT_EXE_PATH.to_path_buf().join("config").exists() || Path::new("config").exists(); if !is_existed_config_path {