diff --git a/CHANGELOG-Japanese.md b/CHANGELOG-Japanese.md index e210c2bc7..77c21d165 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -16,6 +16,7 @@ **バグ修正:** - Sigmaの相関ルールのカウントが`Events with hits`に表示されていなかった。(#1373) (@fukusuket) +- 相関ルールのカウントが`Events with hits`に表示されていなかった。(#1374) (@fukusuket) - 集計ルールのカウントが`Events with hits`に表示されていなかった。(#1375) (@fukusuket) ## 2.16.0 [2024/06/11] diff --git a/CHANGELOG.md b/CHANGELOG.md index a62764ee3..22cdef898 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ **Bug Fixes:** - Sigma correlation rule count was not showing up in `Events with hits`. (#1373) (@fukusuket) +- Correlation rule count was not showing up in `Events with hits`. (#1374) (@fukusuket) - Aggregation condition rule count was not showing up in `Events with hits`. (#1375) (@fukusuket) ## 2.16.0 [2024/06/11] diff --git a/Cargo.lock b/Cargo.lock index bd8e7f5b3..e1998a0b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,13 +261,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.3" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e2d530f35b40a84124146478cd16f34225306a8441998836466a2e2961c950" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" dependencies = [ "jobserver", "libc", - "once_cell", ] [[package]] @@ -354,7 +353,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1371,9 +1370,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -1392,7 +1391,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1412,9 +1411,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -1472,9 +1471,9 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "portable-atomic" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" [[package]] name = "ppv-lite86" @@ -1572,9 +1571,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags 2.6.0", ] @@ -1663,7 +1662,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.71", + "syn 2.0.72", "walkdir", ] @@ -1720,9 +1719,9 @@ checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" -version = "0.102.5" +version = "0.102.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" dependencies = [ "ring", "rustls-pki-types", @@ -1782,7 +1781,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1893,7 +1892,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1915,9 +1914,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.71" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -1963,22 +1962,22 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1998,9 +1997,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.0" +version = "1.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" dependencies = [ "backtrace", "bytes", @@ -2023,7 +2022,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2156,7 +2155,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "wasm-bindgen-shared", ] @@ -2178,7 +2177,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2426,7 +2425,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] diff --git a/src/detections/detection.rs b/src/detections/detection.rs index a385936c8..d3f1d30f7 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -153,6 +153,8 @@ impl Detection { &rulefile_loader.rulecounter, &rulefile_loader.rule_load_cnt, &rulefile_loader.rule_status_cnt, + &rulefile_loader.rule_cor_cnt, + &rulefile_loader.rule_cor_ref_cnt, &parseerror_count, stored_static, ); @@ -1104,6 +1106,8 @@ impl Detection { rc: &HashMap, ld_rc: &HashMap, st_rc: &HashMap, + cor_rc: &HashMap, + cor_ref_rc: &HashMap, err_rc: &u128, stored_static: &StoredStatic, ) { @@ -1146,8 +1150,9 @@ impl Detection { ) .ok(); } - println!(); - + if !ld_rc.is_empty() { + println!(); + } let mut sorted_st_rc: Vec<(&CompactString, &u128)> = st_rc.iter().collect(); let output_opt = stored_static.output_option.as_ref().unwrap(); let enable_deprecated_flag = output_opt.enable_deprecated_rules; @@ -1192,6 +1197,34 @@ impl Detection { }); println!(); + 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}%)", + 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}%)", + 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), + None, + &col_ref, + true, + ) + .ok(); + if stored_static.html_report_flag { + html_report_stock.push(format!("- {col}")); + html_report_stock.push(format!("- {col_ref}")); + } + println!(); + } + 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)| { diff --git a/src/yaml.rs b/src/yaml.rs index 527c1f2d4..cbc87f4dc 100644 --- a/src/yaml.rs +++ b/src/yaml.rs @@ -13,13 +13,15 @@ use std::ffi::OsStr; use std::fs; use std::io::{self, BufReader, Read}; use std::path::{Path, PathBuf}; -use yaml_rust::YamlLoader; +use yaml_rust::{Yaml, YamlLoader}; pub struct ParseYaml { pub files: Vec<(String, yaml_rust::Yaml)>, pub rulecounter: HashMap, pub rule_load_cnt: HashMap, pub rule_status_cnt: HashMap, + pub rule_cor_cnt: HashMap, + pub rule_cor_ref_cnt: HashMap, pub errorrule_count: u128, pub exclude_status: HashSet, pub level_map: HashMap, @@ -41,6 +43,8 @@ impl ParseYaml { ("deprecated".into(), 0_u128), ("unsupported".into(), 0_u128), ]), + rule_cor_cnt: Default::default(), + rule_cor_ref_cnt: Default::default(), errorrule_count: 0, exclude_status: configs::convert_option_vecs_to_hs(exclude_status_vec.as_ref()), level_map: HashMap::from([ @@ -67,6 +71,31 @@ impl ParseYaml { Ok(file_content) } + fn update_correlation_counts(&mut self, yaml_docs: &Vec) { + for doc in yaml_docs { + if let Some(correlation) = doc["correlation"].as_hash() { + let entry = self + .rule_cor_cnt + .entry(CompactString::from("correlation")) + .or_insert(0); + *entry += 1; + if let Some(rules) = correlation.get(&Yaml::String("rules".to_string())) { + if let Some(rules_list) = rules.as_vec() { + for rule in rules_list { + if let Some(rule_str) = rule.as_str() { + // Update rules count, storing each unique rule + let rule_entry = self + .rule_cor_ref_cnt + .entry(CompactString::from(rule_str)) + .or_insert(0); + *rule_entry += 1; + } + } + } + } + } + } + } pub fn read_dir>( &mut self, path: P, @@ -137,30 +166,32 @@ impl ParseYaml { } // ここも個別のファイルの読み込みは即終了としない。 - let yaml_contents = YamlLoader::load_from_str(&read_content.unwrap()); - if yaml_contents.is_err() { - let errmsg = format!( - "Failed to parse yml: {}\n{} ", - path.as_ref().to_path_buf().display(), - yaml_contents.unwrap_err() - ); - if stored_static.verbose_flag { - AlertMessage::warn(&errmsg)?; + match YamlLoader::load_from_str(&read_content.unwrap()) { + Ok(contents) => { + Self::update_correlation_counts(self, &contents); + yaml_docs.extend(contents.into_iter().map(|yaml_content| { + let filepath = format!("{}", path.as_ref().to_path_buf().display()); + (filepath, yaml_content) + })); } - if !stored_static.quiet_errors_flag { - ERROR_LOG_STACK - .lock() - .unwrap() - .push(format!("[WARN] {errmsg}")); + Err(error) => { + let errmsg = format!( + "Failed to parse yml: {}\n{} ", + path.as_ref().to_path_buf().display(), + error + ); + if stored_static.verbose_flag { + AlertMessage::warn(&errmsg)?; + } + if !stored_static.quiet_errors_flag { + ERROR_LOG_STACK + .lock() + .unwrap() + .push(format!("[WARN] {errmsg}")); + } + self.errorrule_count += 1; } - self.errorrule_count += 1; - return io::Result::Ok(String::default()); } - - yaml_docs.extend(yaml_contents.unwrap().into_iter().map(|yaml_content| { - let filepath = format!("{}", path.as_ref().to_path_buf().display()); - (filepath, yaml_content) - })); } else { let mut entries = fs::read_dir(path)?; yaml_docs = entries.try_fold(vec![], |mut ret, entry| { @@ -224,32 +255,35 @@ impl ParseYaml { } // ここも個別のファイルの読み込みは即終了としない。 - let yaml_contents = YamlLoader::load_from_str(&read_content.unwrap()); - if yaml_contents.is_err() { - let errmsg = format!( - "Failed to parse yml: {}\n{} ", - entry.path().display(), - yaml_contents.unwrap_err() - ); - if stored_static.verbose_flag { - AlertMessage::warn(&errmsg)?; + match YamlLoader::load_from_str(&read_content.unwrap()) { + Ok(contents) => { + Self::update_correlation_counts(self, &contents); + let pair = contents.into_iter().map(|yaml_content| { + let filepath = format!("{}", entry.path().display()); + (filepath, yaml_content) + }); + ret.extend(pair); + io::Result::Ok(ret) } - if !stored_static.quiet_errors_flag { - ERROR_LOG_STACK - .lock() - .unwrap() - .push(format!("[WARN] {errmsg}")); + Err(error) => { + let errmsg = format!( + "Failed to parse yml: {}\n{} ", + entry.path().display(), + error + ); + if stored_static.verbose_flag { + AlertMessage::warn(&errmsg)?; + } + if !stored_static.quiet_errors_flag { + ERROR_LOG_STACK + .lock() + .unwrap() + .push(format!("[WARN] {errmsg}")); + } + self.errorrule_count += 1; + io::Result::Ok(ret) } - self.errorrule_count += 1; - return io::Result::Ok(ret); } - - let yaml_contents = yaml_contents.unwrap().into_iter().map(|yaml_content| { - let filepath = format!("{}", entry.path().display()); - (filepath, yaml_content) - }); - ret.extend(yaml_contents); - io::Result::Ok(ret) })?; } let exist_output_opt = stored_static.output_option.is_some();