Merge branch 'main' into clap_update_v3
This commit is contained in:
@@ -1,21 +1,20 @@
|
||||
# 変更点
|
||||
|
||||
## v1.3.1 [2022/xx/xx]
|
||||
## v1.3.1 [2022/06/13]
|
||||
|
||||
**新機能:**
|
||||
|
||||
- ルール内の`details`で複数の`Data`レコードから特定のデータを指定して出力できるようにした。 (#487) (@hitenkoku)
|
||||
- 読み込んだルールのステータス情報の要約を追加した。 (#583) (@hitenkoku)
|
||||
|
||||
**改善:**
|
||||
|
||||
- LinuxとmacOSのバイナリサイズをより小さくするために、デバッグシンボルをストリップします。(#568) (@YamatoSecurity)
|
||||
- Crateパッケージの更新 (@YamatoSecurity)
|
||||
- 新たな時刻表示のオプションとして`--US-time`、`--US-military-time`、`--European-time`の3つを追加した (#574) (@hitenkoku)
|
||||
- `--rfc-3339` オプションの時刻表示形式を変更した。 (#574) (@hitenkoku)
|
||||
- `-R/ --display-record-id`オプションを`-R/ --hide-record-id`に変更。レコードIDはデフォルトで出力するようにして`-R`オプションを付けた際に表示しないように変更した。(#579) (@hitenkoku)
|
||||
|
||||
**バグ修正:**
|
||||
|
||||
- xxx
|
||||
- ルール読み込み時のメッセージを追加した。 (#583) (@hitenkoku)
|
||||
|
||||
**バグ修正:**
|
||||
|
||||
|
||||
15
CHANGELOG.md
15
CHANGELOG.md
@@ -1,25 +1,24 @@
|
||||
# Changes
|
||||
|
||||
## v1.3.1 [2022/xx/xx]
|
||||
## v1.3.1 [2022/06/13]
|
||||
|
||||
**New Features:**
|
||||
|
||||
- You can now specify specific fields when there are multiple fields with the same name (Ex: `Data`). In the `details` line in a rule, specify a placeholder like `%Data[1]%` to display the first `Data` field. (#487) (@hitenkoku)
|
||||
- Added loaded rules status summary. (#583) (@hitenkoku)
|
||||
|
||||
**Enhancements:**
|
||||
|
||||
- Strip debug symbols by default for smaller Linux and macOS binaries. (#568) (@YamatoSecurity)
|
||||
- Debug symbols are stripped by default for smaller Linux and macOS binaries. (#568) (@YamatoSecurity)
|
||||
- Updated crate packages (@YamatoSecurity)
|
||||
- Added new output time format options. (`--US-time`, `--US-military-time`, `--European-time`) (#574) (@hitenkoku)
|
||||
- Changed output time format when `--rfc-3339` option is enabled. (#574) (@hitenkoku)
|
||||
- Changed the output time format when `--rfc-3339` option is enabled. (#574) (@hitenkoku)
|
||||
- Changed the `-R / --display-record-id` option to `-R / --hide-record-id` and now by default the event record ID is displayed. You can hide the record ID with `-R / --hide-record-id`. (#579) (@hitenkoku)
|
||||
- Added rule loading message. (#583) (@hitenkoku)
|
||||
|
||||
**Bug Fixes:**
|
||||
|
||||
- xxx
|
||||
|
||||
**Bug Fixes:**
|
||||
|
||||
- fixed bug that RecordID and RecordInformation column is showed when options is not enabled. (#577) (@hitenkoku)
|
||||
- The RecordID and RecordInformation column headers would be shown even if those options were not enabled. (#577) (@hitenkoku)
|
||||
|
||||
## v1.3.0 [2022/06/06]
|
||||
|
||||
|
||||
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -175,7 +175,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver 1.0.9",
|
||||
"semver 1.0.10",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
@@ -692,7 +692,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hayabusa"
|
||||
version = "1.3.1-dev"
|
||||
version = "1.3.1"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytesize",
|
||||
@@ -1474,9 +1474,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.9"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
|
||||
checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "hayabusa"
|
||||
version = "1.3.1-dev"
|
||||
version = "1.3.1"
|
||||
authors = ["Yamato Security @SecurityYamato"]
|
||||
edition = "2021"
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 737 KiB |
@@ -102,7 +102,7 @@ fn build_app<'a>() -> ArgMatches<'a> {
|
||||
--contributors 'Prints the list of contributors.'";
|
||||
App::new(&program)
|
||||
.about("Hayabusa: Aiming to be the world's greatest Windows event log analysis tool!")
|
||||
.version("1.3.1-dev")
|
||||
.version("1.3.1")
|
||||
.author("Yamato Security (https://github.com/Yamato-Security/hayabusa) @SecurityYamato")
|
||||
.setting(AppSettings::VersionlessSubcommands)
|
||||
.arg(
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::detections::print::{CH_CONFIG, IS_HIDE_RECORD_ID, TAGS_CONFIG};
|
||||
use crate::detections::rule;
|
||||
use crate::detections::rule::AggResult;
|
||||
use crate::detections::rule::RuleNode;
|
||||
use crate::detections::utils::get_serde_number_to_string;
|
||||
use crate::detections::utils::{get_serde_number_to_string, make_ascii_titlecase};
|
||||
use crate::filter;
|
||||
use crate::yaml::ParseYaml;
|
||||
use hashbrown;
|
||||
@@ -109,7 +109,7 @@ impl Detection {
|
||||
});
|
||||
}
|
||||
parseerror_count += 1;
|
||||
println!(); // 一行開けるためのprintln
|
||||
println!();
|
||||
});
|
||||
Option::None
|
||||
};
|
||||
@@ -126,12 +126,13 @@ impl Detection {
|
||||
.args
|
||||
.is_present("logon-summary")
|
||||
{
|
||||
let _ = &rulefile_loader
|
||||
.rule_load_cnt
|
||||
.insert(String::from("rule parsing error"), parseerror_count);
|
||||
Detection::print_rule_load_info(
|
||||
&rulefile_loader.rulecounter,
|
||||
&parseerror_count,
|
||||
&rulefile_loader.exclude_rule_count,
|
||||
&rulefile_loader.noisy_rule_count,
|
||||
&rulefile_loader.deprecate_rule_count,
|
||||
&rulefile_loader.rule_load_cnt,
|
||||
&rulefile_loader.rule_status_cnt,
|
||||
);
|
||||
}
|
||||
ret
|
||||
@@ -355,27 +356,49 @@ impl Detection {
|
||||
|
||||
pub fn print_rule_load_info(
|
||||
rc: &HashMap<String, u128>,
|
||||
parseerror_count: &u128,
|
||||
exclude_count: &u128,
|
||||
noisy_count: &u128,
|
||||
deprecate_count: &u128,
|
||||
ld_rc: &HashMap<String, u128>,
|
||||
st_rc: &HashMap<String, u128>,
|
||||
) {
|
||||
if *STATISTICS_FLAG {
|
||||
return;
|
||||
}
|
||||
println!("Deprecated rules: {}", deprecate_count);
|
||||
println!("Excluded rules: {}", exclude_count);
|
||||
println!("Noisy rules: {}", noisy_count);
|
||||
println!("Rule parsing errors: {}", parseerror_count);
|
||||
let mut sorted_ld_rc: Vec<(&String, &u128)> = ld_rc.iter().collect();
|
||||
sorted_ld_rc.sort_by(|a, b| a.0.cmp(b.0));
|
||||
sorted_ld_rc.into_iter().for_each(|(key, value)| {
|
||||
//タイトルに利用するものはascii文字であることを前提として1文字目を大文字にするように変更する
|
||||
println!(
|
||||
"{} rules: {}",
|
||||
make_ascii_titlecase(key.clone().as_mut()),
|
||||
value,
|
||||
);
|
||||
});
|
||||
println!();
|
||||
|
||||
let mut sorted_st_rc: Vec<(&String, &u128)> = st_rc.iter().collect();
|
||||
let total_loaded_rule_cnt: u128 = sorted_st_rc.iter().map(|(_, v)| v.to_owned()).sum();
|
||||
sorted_st_rc.sort_by(|a, b| a.0.cmp(b.0));
|
||||
sorted_st_rc.into_iter().for_each(|(key, value)| {
|
||||
let rate = if value == &0_u128 {
|
||||
0 as f64
|
||||
} else {
|
||||
(*value as f64) / (total_loaded_rule_cnt as f64) * 100.0
|
||||
};
|
||||
//タイトルに利用するものはascii文字であることを前提として1文字目を大文字にするように変更する
|
||||
println!(
|
||||
"{} rules: {} ({:.2}%)",
|
||||
make_ascii_titlecase(key.clone().as_mut()),
|
||||
value,
|
||||
rate
|
||||
);
|
||||
});
|
||||
println!();
|
||||
|
||||
let mut sorted_rc: Vec<(&String, &u128)> = rc.iter().collect();
|
||||
sorted_rc.sort_by(|a, b| a.0.cmp(b.0));
|
||||
let mut enable_total = 0;
|
||||
sorted_rc.into_iter().for_each(|(key, value)| {
|
||||
println!("{} rules: {}", key, value);
|
||||
enable_total += value;
|
||||
});
|
||||
println!("Total enabled detection rules: {}", enable_total);
|
||||
println!("Total enabled detection rules: {}", total_loaded_rule_cnt);
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,9 +341,26 @@ fn _collect_recordinfo<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 最初の文字を大文字にする関数
|
||||
*/
|
||||
pub fn make_ascii_titlecase(s: &mut str) -> String {
|
||||
let mut c = s.chars();
|
||||
match c.next() {
|
||||
None => String::new(),
|
||||
Some(f) => {
|
||||
if !f.is_ascii() {
|
||||
s.to_string()
|
||||
} else {
|
||||
f.to_uppercase().collect::<String>() + c.as_str()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::detections::utils;
|
||||
use crate::detections::utils::{self, make_ascii_titlecase};
|
||||
use regex::Regex;
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -494,4 +511,15 @@ mod tests {
|
||||
|
||||
assert!(utils::get_serde_number_to_string(&event_record["Event"]["EventData"]).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 文字列を与えてascii文字を大文字にするように対応する関数のテスト
|
||||
fn test_make_ascii_titlecase() {
|
||||
assert_eq!(make_ascii_titlecase("aaaa".to_string().as_mut()), "Aaaa");
|
||||
assert_eq!(
|
||||
make_ascii_titlecase("i am Test".to_string().as_mut()),
|
||||
"I am Test"
|
||||
);
|
||||
assert_eq!(make_ascii_titlecase("β".to_string().as_mut()), "β");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,6 +455,8 @@ impl App {
|
||||
}
|
||||
println!("Total file size: {}", total_file_size.to_string_as(false));
|
||||
println!();
|
||||
println!("Loading detections rules. Please wait.");
|
||||
println!();
|
||||
|
||||
let rule_files = detection::Detection::parse_rule_files(
|
||||
level,
|
||||
|
||||
62
src/yaml.rs
62
src/yaml.rs
@@ -18,9 +18,8 @@ use yaml_rust::YamlLoader;
|
||||
pub struct ParseYaml {
|
||||
pub files: Vec<(String, yaml_rust::Yaml)>,
|
||||
pub rulecounter: HashMap<String, u128>,
|
||||
pub exclude_rule_count: u128,
|
||||
pub noisy_rule_count: u128,
|
||||
pub deprecate_rule_count: u128,
|
||||
pub rule_load_cnt: HashMap<String, u128>,
|
||||
pub rule_status_cnt: HashMap<String, u128>,
|
||||
pub errorrule_count: u128,
|
||||
}
|
||||
|
||||
@@ -35,9 +34,11 @@ impl ParseYaml {
|
||||
ParseYaml {
|
||||
files: Vec::new(),
|
||||
rulecounter: HashMap::new(),
|
||||
exclude_rule_count: 0,
|
||||
noisy_rule_count: 0,
|
||||
deprecate_rule_count: 0,
|
||||
rule_load_cnt: HashMap::from([
|
||||
("excluded".to_string(), 0_u128),
|
||||
("noisy".to_string(), 0_u128),
|
||||
]),
|
||||
rule_status_cnt: HashMap::from([("deprecated".to_string(), 0_u128)]),
|
||||
errorrule_count: 0,
|
||||
}
|
||||
}
|
||||
@@ -221,19 +222,18 @@ impl ParseYaml {
|
||||
//除外されたルールは無視する
|
||||
let rule_id = &yaml_doc["id"].as_str();
|
||||
if rule_id.is_some() {
|
||||
match exclude_ids
|
||||
if let Some(v) = exclude_ids
|
||||
.no_use_rule
|
||||
.get(&rule_id.unwrap_or("").to_string())
|
||||
.get(&rule_id.unwrap_or(&String::default()).to_string())
|
||||
{
|
||||
None => (),
|
||||
Some(v) => {
|
||||
if v.contains("exclude_rule") {
|
||||
self.exclude_rule_count += 1;
|
||||
} else {
|
||||
self.noisy_rule_count += 1;
|
||||
}
|
||||
return Option::None;
|
||||
}
|
||||
let entry_key = if v.contains("exclude_rule") {
|
||||
"excluded"
|
||||
} else {
|
||||
"noisy"
|
||||
};
|
||||
let entry = self.rule_load_cnt.entry(entry_key.to_string()).or_insert(0);
|
||||
*entry += 1;
|
||||
return Option::None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,6 +245,17 @@ impl ParseYaml {
|
||||
+ 1,
|
||||
);
|
||||
|
||||
let status_cnt = self
|
||||
.rule_status_cnt
|
||||
.entry(
|
||||
yaml_doc["status"]
|
||||
.as_str()
|
||||
.unwrap_or("undefined")
|
||||
.to_string(),
|
||||
)
|
||||
.or_insert(0);
|
||||
*status_cnt += 1;
|
||||
|
||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||
println!("Loaded yml file path: {}", filepath);
|
||||
}
|
||||
@@ -269,7 +280,11 @@ impl ParseYaml {
|
||||
{
|
||||
let rule_status = &yaml_doc["status"].as_str().unwrap_or_default();
|
||||
if *rule_status == "deprecated" {
|
||||
self.deprecate_rule_count += 1;
|
||||
let entry = self
|
||||
.rule_status_cnt
|
||||
.entry(rule_status.to_string())
|
||||
.or_insert(0);
|
||||
*entry += 1;
|
||||
return Option::None;
|
||||
}
|
||||
}
|
||||
@@ -400,7 +415,7 @@ mod tests {
|
||||
let mut yaml = yaml::ParseYaml::new();
|
||||
let path = Path::new("test_files/rules/yaml");
|
||||
yaml.read_dir(path, "", &filter::exclude_ids()).unwrap();
|
||||
assert_eq!(yaml.exclude_rule_count, 5);
|
||||
assert_eq!(yaml.rule_load_cnt.get("excluded").unwrap().to_owned(), 5);
|
||||
}
|
||||
#[test]
|
||||
fn test_all_noisy_rules_file() {
|
||||
@@ -409,7 +424,7 @@ mod tests {
|
||||
let mut yaml = yaml::ParseYaml::new();
|
||||
let path = Path::new("test_files/rules/yaml");
|
||||
yaml.read_dir(path, "", &filter::exclude_ids()).unwrap();
|
||||
assert_eq!(yaml.noisy_rule_count, 5);
|
||||
assert_eq!(yaml.rule_load_cnt.get("noisy").unwrap().to_owned(), 5);
|
||||
}
|
||||
#[test]
|
||||
fn test_none_exclude_rules_file() {
|
||||
@@ -419,7 +434,7 @@ mod tests {
|
||||
let path = Path::new("test_files/rules/yaml");
|
||||
let exclude_ids = RuleExclude::default();
|
||||
yaml.read_dir(path, "", &exclude_ids).unwrap();
|
||||
assert_eq!(yaml.exclude_rule_count, 0);
|
||||
assert_eq!(yaml.rule_load_cnt.get("excluded").unwrap().to_owned(), 0);
|
||||
}
|
||||
#[test]
|
||||
fn test_exclude_deprecated_rules_file() {
|
||||
@@ -427,6 +442,9 @@ mod tests {
|
||||
let path = Path::new("test_files/rules/deprecated");
|
||||
let exclude_ids = RuleExclude::default();
|
||||
yaml.read_dir(path, "", &exclude_ids).unwrap();
|
||||
assert_eq!(yaml.deprecate_rule_count, 1);
|
||||
assert_eq!(
|
||||
yaml.rule_status_cnt.get("deprecated").unwrap().to_owned(),
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user