diff --git a/CHANGELOG-Japanese.md b/CHANGELOG-Japanese.md index d878456d4..e210c2bc7 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -12,6 +12,11 @@ - `Data`フィールドは、すべて`Data`フィールドとして、またはJSONの配列としてではなく、インデックス化された文字列として表示されるようになった。(#1371) (@fukusuket) - 前: `"Data": ["17514", "Multiprocessor Free", "Service Pack 1"]` - 後: `"Data[3]": "17514", "Data[4]": "Multiprocessor Free", "Data[5]": "Service Pack 1"` +- 集計ルールのアラートに、複数の結果がある場合でも`Channel`と`EventID`の情報が表示されるようにした。 (#1342) (@fukusuket) + +**バグ修正:** +- Sigmaの相関ルールのカウントが`Events with hits`に表示されていなかった。(#1373) (@fukusuket) +- 集計ルールのカウントが`Events with hits`に表示されていなかった。(#1375) (@fukusuket) ## 2.16.0 [2024/06/11] diff --git a/CHANGELOG.md b/CHANGELOG.md index 9de2b80d5..a62764ee3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ - `Data` fields are now displayed as indexed strings instead of as all `Data` fields or in an array for JSON. (#1371) (@fukusuket) - Before: `"Data": ["17514", "Multiprocessor Free", "Service Pack 1"]` - After: `"Data[3]": "17514", "Data[4]": "Multiprocessor Free", "Data[5]": "Service Pack 1"` +- Aggregation rule alerts now show `Channel` and `EventID` information even when there are multiple results. (#1342) (@fukusuket) + +**Bug Fixes:** +- Sigma correlation rule count was not showing up in `Events with hits`. (#1373) (@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 873663fff..bd8e7f5b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -209,9 +209,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" [[package]] name = "bytesize" @@ -252,18 +252,18 @@ dependencies = [ [[package]] name = "castaway" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" dependencies = [ "rustversion", ] [[package]] name = "cc" -version = "1.0.104" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" +checksum = "18e2d530f35b40a84124146478cd16f34225306a8441998836466a2e2961c950" dependencies = [ "jobserver", "libc", @@ -288,7 +288,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -325,9 +325,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.8" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" +checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" dependencies = [ "clap_builder", "clap_derive", @@ -335,9 +335,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.8" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" +checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" dependencies = [ "anstream", "anstyle", @@ -354,7 +354,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -831,7 +831,7 @@ dependencies = [ "bytesize", "chrono", "cidr-utils", - "clap 4.5.8", + "clap 4.5.9", "comfy-table", "compact_str", "console", @@ -1072,7 +1072,7 @@ checksum = "313560d2dd5dcabbc1a9690c88e1f443136d6025ca8a421df2d5719f45357979" dependencies = [ "anyhow", "chrono", - "clap 4.5.8", + "clap 4.5.9", "file-chunker", "memmap2 0.9.4", "num_cpus", @@ -1392,7 +1392,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -1449,7 +1449,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -1504,9 +1504,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e446ed58cef1bbfe847bc2fda0e2e4ea9f0e57b90c507d4781292590d72a4e" +checksum = "4091e032efecb09d7b1f711f487b85ab925632a842627e3200fb088382cde32c" dependencies = [ "memchr", ] @@ -1645,9 +1645,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "8.4.0" +version = "8.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19549741604902eb99a7ed0ee177a0663ee1eda51a29f71401f166e47e77806a" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -1656,22 +1656,22 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.4.0" +version = "8.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb9f96e283ec64401f30d3df8ee2aaeb2561f34c824381efa24a35f79bf40ee4" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.68", + "syn 2.0.71", "walkdir", ] [[package]] name = "rust-embed-utils" -version = "8.4.0" +version = "8.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c74a686185620830701348de757fd36bef4aa9680fd23c49fc539ddcc1af32" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" dependencies = [ "globset", "sha2", @@ -1699,11 +1699,12 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.4" +version = "0.23.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" dependencies = [ "log", + "once_cell", "ring", "rustls-pki-types", "rustls-webpki", @@ -1719,9 +1720,9 @@ checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" -version = "0.102.4" +version = "0.102.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" dependencies = [ "ring", "rustls-pki-types", @@ -1766,22 +1767,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -1892,7 +1893,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -1914,9 +1915,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.68" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", @@ -1962,29 +1963,29 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] name = "tinyvec" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -2022,7 +2023,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] @@ -2075,9 +2076,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.9.7" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd" +checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea" dependencies = [ "base64", "flate2", @@ -2085,7 +2086,6 @@ dependencies = [ "once_cell", "rustls", "rustls-pki-types", - "rustls-webpki", "url", "webpki-roots", ] @@ -2156,7 +2156,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", "wasm-bindgen-shared", ] @@ -2178,7 +2178,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2241,7 +2241,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2259,7 +2259,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2279,18 +2279,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -2301,9 +2301,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -2313,9 +2313,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -2325,15 +2325,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -2343,9 +2343,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -2355,9 +2355,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -2367,9 +2367,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -2379,9 +2379,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winstructs" @@ -2426,7 +2426,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.71", ] [[package]] diff --git a/src/afterfact.rs b/src/afterfact.rs index 2289753ad..37541b5ea 100644 --- a/src/afterfact.rs +++ b/src/afterfact.rs @@ -495,15 +495,23 @@ fn calc_statistic_info( afterfact_info .timestamps .push(detect_info.detected_time.timestamp()); - if !detect_info.is_condition { - afterfact_info - .detected_record_idset - .insert(CompactString::from(format!( - "{}_{}", - detect_info.detected_time, detect_info.eventid - ))); + match &detect_info.agg_result { + None => { + afterfact_info + .detected_record_idset + .insert(CompactString::from(format!( + "{}_{}", + detect_info.detected_time, detect_info.eventid + ))); + } + Some(agg_result) => { + agg_result.agg_record_time_info.iter().for_each(|a| { + afterfact_info + .detected_record_idset + .insert(CompactString::from(format!("{}_{}", a.time, a.event_id))); + }); + } } - if !output_option.no_summary { let level_suffix = get_level_suffix(detect_info.level.as_str()); let author_list = afterfact_info @@ -539,23 +547,33 @@ fn calc_statistic_info( .insert(detect_info.ruleid.to_owned()); afterfact_info.unique_detect_counts_by_level[level_suffix] += 1; } - - let computer_rule_check_key = CompactString::from(format!( - "{}|{}", - &detect_info.computername, &detect_info.rulepath - )); - if !afterfact_info - .detected_computer_and_rule_names - .contains(&computer_rule_check_key) - { - afterfact_info + let computer_names = match &detect_info.agg_result { + None => vec![detect_info.computername.clone()], + Some(agg) => agg + .agg_record_time_info + .iter() + .map(|a| CompactString::from(a.computer.clone())) + .collect::>() // Convert to HashSet to remove duplicates + .into_iter() + .sorted() + .collect(), + }; + for computername in &computer_names { + let computer_rule_check_key = + CompactString::from(format!("{}|{}", computername, &detect_info.rulepath)); + if !afterfact_info .detected_computer_and_rule_names - .insert(computer_rule_check_key); - countup_aggregation( - &mut afterfact_info.detect_counts_by_computer_and_level, - &detect_info.level, - &detect_info.computername, - ); + .contains(&computer_rule_check_key) + { + afterfact_info + .detected_computer_and_rule_names + .insert(computer_rule_check_key); + countup_aggregation( + &mut afterfact_info.detect_counts_by_computer_and_level, + &detect_info.level, + computername, + ); + } } afterfact_info.rule_title_path_map.insert( detect_info.ruletitle.to_owned(), @@ -572,6 +590,7 @@ fn calc_statistic_info( &detect_info.level, &detect_info.ruletitle, ); + let level_suffix = get_level_suffix(detect_info.level.as_str()); afterfact_info.total_detect_counts_by_level[level_suffix] += 1; } } @@ -1887,7 +1906,7 @@ pub fn output_json_str( let mut children_output_stock: HashMap> = HashMap::new(); let mut children_output_order = vec![]; - if detect_info.is_condition { + if detect_info.agg_result.is_some() { if details_target_stock[0] == "-" { output_stock.push(_create_json_output_format( key, @@ -2463,7 +2482,7 @@ mod tests { eventid: CompactString::from(test_eventid), detail: CompactString::default(), ext_field: output_profile.to_owned(), - is_condition: false, + agg_result: None, details_convert_map: HashMap::default(), }, &profile_converter, @@ -2487,7 +2506,7 @@ mod tests { eventid: CompactString::from(test_eventid), detail: CompactString::default(), ext_field: output_profile.to_owned(), - is_condition: false, + agg_result: None, details_convert_map: HashMap::default(), }, &profile_converter, @@ -2810,7 +2829,7 @@ mod tests { eventid: CompactString::from(test_eventid), detail: CompactString::default(), ext_field: output_profile.to_owned(), - is_condition: false, + agg_result: None, details_convert_map: HashMap::default(), }, &profile_converter, @@ -2834,7 +2853,7 @@ mod tests { eventid: CompactString::from(test_eventid), detail: CompactString::default(), ext_field: output_profile.to_owned(), - is_condition: false, + agg_result: None, details_convert_map: HashMap::default(), }, &profile_converter, @@ -3137,7 +3156,7 @@ mod tests { eventid: CompactString::from(test_eventid), detail: CompactString::default(), ext_field: output_profile.to_owned(), - is_condition: false, + agg_result: None, details_convert_map: HashMap::default(), }, &profile_converter, @@ -3161,7 +3180,7 @@ mod tests { eventid: CompactString::from(test_eventid), detail: CompactString::default(), ext_field: output_profile.to_owned(), - is_condition: false, + agg_result: None, details_convert_map: HashMap::default(), }, &profile_converter, @@ -3474,7 +3493,7 @@ mod tests { eventid: CompactString::from(test_eventid), detail: CompactString::default(), ext_field: output_profile.to_owned(), - is_condition: false, + agg_result: None, details_convert_map, }, &profile_converter, @@ -3498,7 +3517,7 @@ mod tests { eventid: CompactString::from(test_eventid), detail: CompactString::default(), ext_field: output_profile.to_owned(), - is_condition: false, + agg_result: None, details_convert_map: HashMap::default(), }, &profile_converter, @@ -3883,7 +3902,7 @@ mod tests { eventid: CompactString::from(test_eventid), detail: CompactString::default(), ext_field: output_profile.to_owned(), - is_condition: false, + agg_result: None, details_convert_map, }, &profile_converter, @@ -4237,7 +4256,7 @@ mod tests { eventid: CompactString::from(test_eventid), detail: CompactString::default(), ext_field: output_profile.to_owned(), - is_condition: false, + agg_result: None, details_convert_map, }, &profile_converter, @@ -4517,7 +4536,7 @@ mod tests { eventid: CompactString::from(test_eventid), detail: CompactString::default(), ext_field: output_profile.to_owned(), - is_condition: false, + agg_result: None, details_convert_map, }, &profile_converter, diff --git a/src/detections/detection.rs b/src/detections/detection.rs index 53d42567f..a385936c8 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -1,5 +1,6 @@ extern crate csv; +use std::collections::HashSet; use std::default::Default; use std::fmt::Write; use std::path::Path; @@ -21,6 +22,7 @@ use crate::detections::configs::STORED_EKEY_ALIAS; use crate::detections::field_data_map::FieldDataMapKey; use crate::detections::message::{AlertMessage, DetectInfo, ERROR_LOG_STACK, TAGS_CONFIG}; 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::{get_serde_number_to_string, make_ascii_titlecase}; @@ -742,7 +744,7 @@ impl Detection { eventid: eid, detail: CompactString::default(), ext_field: stored_static.profiles.as_ref().unwrap().to_owned(), - is_condition: false, + agg_result: None, details_convert_map: HashMap::default(), }; @@ -771,7 +773,6 @@ impl Detection { let mut profile_converter: HashMap<&str, Profile> = HashMap::new(); let level = rule.yaml["level"].as_str().unwrap_or("-").to_string(); let tags_config_values: Vec<&CompactString> = TAGS_CONFIG.values().collect(); - let is_json_timeline = matches!(stored_static.config.action, Some(Action::JsonTimeline(_))); for (key, profile) in stored_static.profiles.as_ref().unwrap().iter() { match profile { @@ -789,10 +790,33 @@ impl Detection { ); } Computer(_) => { - profile_converter.insert(key.as_str(), Computer("-".into())); + profile_converter.insert( + key.as_str(), + Computer( + Detection::join_agg_values(&agg_result.agg_record_time_info, |x| { + x.computer.clone() + }) + .into(), + ), + ); } Channel(_) => { - profile_converter.insert(key.as_str(), Channel("-".into())); + profile_converter.insert( + key.as_str(), + Channel( + Detection::join_agg_values(&agg_result.agg_record_time_info, |x| { + stored_static.disp_abbr_generic.replace_all( + stored_static + .ch_config + .get(&CompactString::from(&x.channel.to_ascii_lowercase())) + .unwrap_or(&CompactString::from(&x.channel)) + .as_str(), + &stored_static.disp_abbr_general_values, + ) + }) + .into(), + ), + ); } Level(_) => { let str_level = level.as_str(); @@ -805,7 +829,15 @@ impl Detection { profile_converter.insert(key.as_str(), Level(prof_level.to_string().into())); } EventID(_) => { - profile_converter.insert(key.as_str(), EventID("-".into())); + profile_converter.insert( + key.as_str(), + EventID( + Detection::join_agg_values(&agg_result.agg_record_time_info, |x| { + x.event_id.clone() + }) + .into(), + ), + ); } RecordID(_) => { profile_converter.insert(key.as_str(), RecordID("-".into())); @@ -833,7 +865,15 @@ impl Detection { profile_converter.insert(key.as_str(), RuleFile(rule_path.into())); } EvtxFile(_) => { - profile_converter.insert(key.as_str(), EvtxFile("-".into())); + profile_converter.insert( + key.as_str(), + EvtxFile( + Detection::join_agg_values(&agg_result.agg_record_time_info, |x| { + x.evtx_file_path.clone() + }) + .into(), + ), + ); } MitreTactics(_) => { let tactics = tag_info @@ -965,7 +1005,7 @@ impl Detection { eventid: CompactString::from("-"), detail: output, ext_field: stored_static.profiles.as_ref().unwrap().to_owned(), - is_condition: true, + agg_result: Some(agg_result), details_convert_map: HashMap::default(), }; let binding = STORED_EKEY_ALIAS.read().unwrap(); @@ -984,6 +1024,22 @@ impl Detection { detect_info } + fn join_agg_values( + agg_record_time_infos: &[AggRecordTimeInfo], + extractor: F, + ) -> CompactString + where + F: Fn(&AggRecordTimeInfo) -> String, + { + agg_record_time_infos + .iter() + .map(&extractor) + .collect::>() // Convert to HashSet to remove duplicates + .into_iter() + .sorted() + .join(" ¦ ") + .into() + } /// rule内のtagsの内容を配列として返却する関数 fn get_tag_info(rule: &RuleNode) -> Nested { Nested::from_iter( @@ -1313,7 +1369,7 @@ mod tests { fn test_output_aggregation_output_with_output() { let default_time = Utc.with_ymd_and_hms(1977, 1, 1, 0, 0, 0).unwrap(); let agg_result: AggResult = - AggResult::new(2, "_".to_string(), vec![], default_time, ">= 1".to_string()); + AggResult::new(2, "_".to_string(), vec![], default_time, vec![]); let rule_str = r#" enabled: true detection: @@ -1341,7 +1397,7 @@ mod tests { fn test_output_aggregation_output_no_filed_by() { let default_time = Utc.with_ymd_and_hms(1977, 1, 1, 0, 0, 0).unwrap(); let agg_result: AggResult = - AggResult::new(2, "_".to_string(), vec![], default_time, ">= 1".to_string()); + AggResult::new(2, "_".to_string(), vec![], default_time, vec![]); let rule_str = r#" enabled: true detection: @@ -1368,7 +1424,7 @@ mod tests { fn test_output_aggregation_output_with_timeframe() { let default_time = Utc.with_ymd_and_hms(1977, 1, 1, 0, 0, 0).unwrap(); let agg_result: AggResult = - AggResult::new(2, "_".to_string(), vec![], default_time, ">= 1".to_string()); + AggResult::new(2, "_".to_string(), vec![], default_time, vec![]); let rule_str = r#" enabled: true detection: @@ -1400,7 +1456,7 @@ mod tests { "_".to_string(), vec!["7040".to_owned(), "9999".to_owned()], default_time, - ">= 1".to_string(), + vec![], ); let rule_str = r#" enabled: true @@ -1430,7 +1486,7 @@ mod tests { "lsass.exe".to_string(), vec!["0000".to_owned(), "1111".to_owned()], default_time, - ">= 1".to_string(), + vec![], ); let rule_str = r#" enabled: true @@ -1454,13 +1510,8 @@ mod tests { #[test] fn test_output_aggregation_output_with_by() { let default_time = Utc.with_ymd_and_hms(1977, 1, 1, 0, 0, 0).unwrap(); - let agg_result: AggResult = AggResult::new( - 2, - "lsass.exe".to_string(), - vec![], - default_time, - ">= 1".to_string(), - ); + let agg_result: AggResult = + AggResult::new(2, "lsass.exe".to_string(), vec![], default_time, vec![]); let rule_str = r#" enabled: true detection: diff --git a/src/detections/message.rs b/src/detections/message.rs index 0f0b841d4..d246ba04b 100644 --- a/src/detections/message.rs +++ b/src/detections/message.rs @@ -1,6 +1,9 @@ extern crate lazy_static; +use super::configs::EventKeyAliasConfig; +use super::utils::remove_sp_char; use crate::detections::configs::CURRENT_EXE_PATH; use crate::detections::field_data_map::{convert_field_data, FieldDataMap, FieldDataMapKey}; +use crate::detections::rule::AggResult; use crate::detections::utils::{self, get_serde_number_to_string, write_color_buffer}; use crate::options::profile::Profile::{ self, AllFieldInfo, Details, ExtraFieldInfo, Literal, SrcASN, SrcCity, SrcCountry, TgtASN, @@ -23,9 +26,6 @@ use std::path::Path; use std::sync::Mutex; use termcolor::{BufferWriter, ColorChoice}; -use super::configs::EventKeyAliasConfig; -use super::utils::remove_sp_char; - /* * This struct express log record */ @@ -40,7 +40,7 @@ pub struct DetectInfo { pub eventid: CompactString, pub detail: CompactString, pub ext_field: Vec<(CompactString, Profile)>, - pub is_condition: bool, + pub agg_result: Option, pub details_convert_map: HashMap>, } diff --git a/src/detections/rule/count.rs b/src/detections/rule/count.rs index 76236f558..c7278c85f 100644 --- a/src/detections/rule/count.rs +++ b/src/detections/rule/count.rs @@ -1,9 +1,11 @@ use crate::detections::configs::EventKeyAliasConfig; use crate::detections::configs::StoredStatic; use crate::detections::configs::STORED_EKEY_ALIAS; +use crate::detections::detection::EvtxRecordInfo; use crate::detections::message; use crate::detections::message::AlertMessage; use crate::detections::message::ERROR_LOG_STACK; +use crate::detections::rule::aggregation_parser::AggregationConditionToken; use crate::detections::rule::AggResult; use crate::detections::rule::RuleNode; use chrono::{DateTime, TimeZone, Utc}; @@ -12,21 +14,19 @@ use serde_json::Value; use std::num::ParseIntError; use std::path::Path; -use crate::detections::rule::aggregation_parser::AggregationConditionToken; - use crate::detections::utils; /// 検知された際にカウント情報を投入する関数 pub fn count( rule: &mut RuleNode, - record: &Value, + evtx_rec: &EvtxRecordInfo, verbose_flag: bool, quiet_errors_flag: bool, json_input_flag: bool, ) { let key: String = create_count_key( rule, - record, + &evtx_rec.record, verbose_flag, quiet_errors_flag, STORED_EKEY_ALIAS.read().unwrap().as_ref().unwrap(), @@ -43,20 +43,14 @@ pub fn count( let field_value = get_alias_value_in_record( rule, field_name, - record, + &evtx_rec.record, false, verbose_flag, quiet_errors_flag, STORED_EKEY_ALIAS.read().unwrap().as_ref().unwrap(), ) .unwrap_or_default(); - let default_time = Utc.with_ymd_and_hms(1977, 1, 1, 0, 0, 0).unwrap(); - countup( - rule, - key, - field_value, - message::get_event_time(record, json_input_flag).unwrap_or(default_time), - ); + countup(rule, key, field_value, evtx_rec, json_input_flag); } ///count byの条件に合致する検知済みレコードの数を増やすための関数 @@ -64,12 +58,42 @@ pub fn countup( rule: &mut RuleNode, key: String, field_value: String, - record_time_value: DateTime, + evtx_rec: &EvtxRecordInfo, + json_input_flag: bool, ) { + let record = &evtx_rec.record; + let default_time = Utc.with_ymd_and_hms(1977, 1, 1, 0, 0, 0).unwrap(); + let time = message::get_event_time(record, json_input_flag).unwrap_or(default_time); + let event_id = utils::get_event_value( + "Event.System.EventID", + record, + STORED_EKEY_ALIAS.read().unwrap().as_ref().unwrap(), + ) + .unwrap(); + let event_id = event_id.to_string().trim_matches('\"').to_string(); + let computer = utils::get_event_value( + "Event.System.Computer", + record, + STORED_EKEY_ALIAS.read().unwrap().as_ref().unwrap(), + ) + .unwrap(); + let computer = computer.to_string().trim_matches('\"').to_string(); + let channel = utils::get_event_value( + "Event.System.Channel", + record, + STORED_EKEY_ALIAS.read().unwrap().as_ref().unwrap(), + ) + .unwrap(); + let channel = channel.to_string().trim_matches('\"').to_string(); + let evtx_file_path = evtx_rec.evtx_filepath.to_string(); let value_map = rule.countdata.entry(key).or_default(); value_map.push(AggRecordTimeInfo { - field_record_value: field_value, - record_time: record_time_value, + field_value, + time, + event_id, + computer, + channel, + evtx_file_path, }); } @@ -188,41 +212,15 @@ pub fn aggregation_condition_select( ret } -/// aggregation condition内での条件式を文字として返す関数 -pub fn get_str_agg_eq(rule: &RuleNode) -> String { - //この関数はaggregation ruleのパースが正常終了した後に呼ばれる想定のためOptionの判定は行わない - let agg_condition = rule.detection.aggregation_condition.as_ref().unwrap(); - let mut ret: String = String::default(); - match agg_condition._cmp_op { - AggregationConditionToken::EQ => { - ret.push_str("== "); - } - AggregationConditionToken::GE => { - ret.push_str(">= "); - } - AggregationConditionToken::LE => { - ret.push_str("<= "); - } - AggregationConditionToken::GT => { - ret.push_str("> "); - } - AggregationConditionToken::LT => { - ret.push_str("< "); - } - _ => { - //想定しない演算子のため、空白文字で対応するものがない - return "".to_string(); - } - } - ret.push_str(&agg_condition._cmp_num.to_string()); - ret -} - -#[derive(Clone, Debug)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] /// countの括弧内の情報とレコードの情報を所持する構造体 pub struct AggRecordTimeInfo { - pub field_record_value: String, - pub record_time: DateTime, + pub field_value: String, + pub time: DateTime, + pub event_id: String, + pub computer: String, + pub channel: String, + pub evtx_file_path: String, } #[derive(Debug)] @@ -348,14 +346,7 @@ trait CountStrategy { /** * AggResultを作成します。 */ - fn create_agg_result( - &mut self, - left: i64, - datas: &[AggRecordTimeInfo], - cnt: i64, - key: &str, - rule: &RuleNode, - ) -> AggResult; + fn create_agg_result(&mut self, datas: &[AggRecordTimeInfo], cnt: i64, key: &str) -> AggResult; } /** @@ -371,7 +362,7 @@ impl CountStrategy for FieldStrategy { return; } - let value = &datas[idx as usize].field_record_value; + let value = &datas[idx as usize].field_value; let key_val = self.value_2_cnt.get_key_value_mut(value); if let Some(kv) = key_val { let (_, val) = kv; @@ -386,7 +377,7 @@ impl CountStrategy for FieldStrategy { return; } - let record_value = &datas[idx as usize].field_record_value; + let record_value = &datas[idx as usize].field_value; let key_val = self.value_2_cnt.get_key_value_mut(record_value); if key_val.is_none() { return; @@ -407,19 +398,17 @@ impl CountStrategy for FieldStrategy { fn create_agg_result( &mut self, - left: i64, datas: &[AggRecordTimeInfo], _cnt: i64, key: &str, - rule: &RuleNode, ) -> AggResult { let values: Vec = self.value_2_cnt.drain().map(|(key, _)| key).collect(); // drainで初期化 AggResult::new( values.len() as i64, key.to_string(), values, - datas[left as usize].record_time, - get_str_agg_eq(rule), + datas.first().unwrap().time, + datas.to_vec(), ) } } @@ -452,20 +441,13 @@ impl CountStrategy for NoFieldStrategy { self.cnt } - fn create_agg_result( - &mut self, - left: i64, - datas: &[AggRecordTimeInfo], - cnt: i64, - key: &str, - rule: &RuleNode, - ) -> AggResult { + fn create_agg_result(&mut self, datas: &[AggRecordTimeInfo], cnt: i64, key: &str) -> AggResult { let ret = AggResult::new( cnt, key.to_string(), vec![], - datas[left as usize].record_time, - get_str_agg_eq(rule), + datas.first().unwrap().time, + datas.to_vec(), ); self.cnt = 0; //cntを初期化 ret @@ -484,11 +466,11 @@ fn _create_counter(rule: &RuleNode) -> Box { } fn _get_timestamp(idx: i64, datas: &[AggRecordTimeInfo]) -> i64 { - datas[idx as usize].record_time.timestamp() + datas[idx as usize].time.timestamp() } fn _get_timestamp_subsec_nano(idx: i64, datas: &[AggRecordTimeInfo]) -> u32 { - datas[idx as usize].record_time.timestamp_subsec_nanos() + datas[idx as usize].time.timestamp_subsec_nanos() } // data[left]からdata[right-1]までのデータがtimeframeに収まっているか判定する @@ -518,11 +500,11 @@ pub fn judge_timeframe( // AggRecordTimeInfoを時間順がソートされている前提で処理を進める let mut datas = time_datas.to_owned(); - datas.sort_by(|a, b| a.record_time.cmp(&b.record_time)); + datas.sort_by(|a, b| a.time.cmp(&b.time)); // timeframeの設定がルールにない時は最初と最後の要素の時間差をtimeframeに設定する。 - let def_frame = datas.last().unwrap().record_time.timestamp() - - datas.first().unwrap().record_time.timestamp(); + let def_frame = + datas.last().unwrap().time.timestamp() - datas.first().unwrap().time.timestamp(); let frame = get_sec_timeframe(rule, stored_static).unwrap_or(def_frame); // left <= i < rightの範囲にあるdata[i]がtimeframe内にあるデータであると考える @@ -541,7 +523,7 @@ pub fn judge_timeframe( let cnt = counter.count(); if select_aggcon(cnt, rule) { // 条件を満たすtimeframeが見つかった - ret.push(counter.create_agg_result(left, &datas, cnt, key, rule)); + ret.push(counter.create_agg_result(&datas[left as usize..right as usize], cnt, key)); left = right; } else { // 条件を満たさなかったので、rightとleftを+1ずらす @@ -710,7 +692,7 @@ mod tests { "_".to_string(), vec![], Utc.with_ymd_and_hms(1977, 1, 1, 0, 0, 0).unwrap(), - ">= 1".to_string(), + vec![], )]; check_count( rule_str, @@ -763,14 +745,14 @@ mod tests { "_".to_string(), vec![], Utc.with_ymd_and_hms(1977, 1, 1, 0, 0, 0).unwrap(), - ">= 1".to_string(), + vec![], ), AggResult::new( 1, "_".to_string(), vec![], Utc.with_ymd_and_hms(1996, 2, 27, 1, 5, 1).unwrap(), - ">= 1".to_string(), + vec![], ), ]; check_count( @@ -803,7 +785,7 @@ mod tests { "_".to_string(), vec!["System".to_owned()], Utc.with_ymd_and_hms(1977, 1, 1, 0, 0, 0).unwrap(), - ">= 1".to_string(), + vec![], ); check_count( rule_str, @@ -853,14 +835,14 @@ mod tests { "System".to_owned(), vec!["7040".to_owned()], Utc.with_ymd_and_hms(1977, 1, 1, 0, 0, 0).unwrap(), - ">= 1".to_string(), + vec![], ), AggResult::new( 1, "Test".to_owned(), vec!["9999".to_owned()], Utc.with_ymd_and_hms(1996, 2, 27, 1, 5, 1).unwrap(), - ">= 1".to_string(), + vec![], ), ]; check_count( @@ -911,14 +893,14 @@ mod tests { "Windows Event Log".to_owned(), vec!["7040".to_owned()], Utc.with_ymd_and_hms(1977, 1, 1, 0, 0, 0).unwrap(), - ">= 1".to_string(), + vec![], ), AggResult::new( 1, "Test".to_owned(), vec!["9999".to_owned()], Utc.with_ymd_and_hms(1977, 1, 1, 0, 5, 0).unwrap(), - ">= 1".to_string(), + vec![], ), ]; check_count( @@ -1031,7 +1013,7 @@ mod tests { "System".to_owned(), vec!["7040".to_owned(), "9999".to_owned()], Utc.with_ymd_and_hms(1977, 1, 1, 0, 0, 0).unwrap(), - ">= 2".to_string(), + vec![], )]; check_count( rule_str, @@ -1081,7 +1063,7 @@ mod tests { "System".to_owned(), vec!["7040".to_owned(), "9999".to_owned()], default_time, - ">= 1".to_string(), + vec![], )]; check_count( rule_str, @@ -1111,7 +1093,7 @@ mod tests { "_".to_owned(), vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], default_time, - ">= 3".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); } @@ -1145,7 +1127,7 @@ mod tests { "_".to_owned(), vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], default_time, - ">= 3".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); } @@ -1179,7 +1161,7 @@ mod tests { "_".to_owned(), vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], default_time, - ">= 3".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); } @@ -1195,7 +1177,7 @@ mod tests { "_".to_owned(), vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], default_time, - ">= 3".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); } @@ -1219,7 +1201,7 @@ mod tests { "_".to_owned(), vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], default_time, - ">= 3".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); } @@ -1253,7 +1235,7 @@ mod tests { "_".to_owned(), vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], default_time, - ">= 3".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); } @@ -1287,7 +1269,7 @@ mod tests { "_".to_owned(), vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], default_time, - ">= 3".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); } @@ -1327,7 +1309,7 @@ mod tests { "_".to_owned(), vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], default_time, - ">= 3".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); } @@ -1367,7 +1349,7 @@ mod tests { "_".to_owned(), vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], default_time, - ">= 3".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); } @@ -1415,7 +1397,7 @@ mod tests { "_".to_owned(), vec!["1".to_owned()], default_time, - ">= 1".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); } @@ -1437,7 +1419,7 @@ mod tests { "Windows Event Log".to_owned(), vec!["1".to_owned()], default_time, - ">= 1".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); } @@ -1475,7 +1457,7 @@ mod tests { "_".to_owned(), vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], Utc.with_ymd_and_hms(1977, 1, 9, 1, 30, 0).unwrap(), - ">= 3".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); } @@ -1526,7 +1508,7 @@ mod tests { "4".to_owned(), ], Utc.with_ymd_and_hms(1977, 1, 9, 1, 30, 0).unwrap(), - ">= 4".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); @@ -1554,7 +1536,7 @@ mod tests { "_".to_owned(), vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], Utc.with_ymd_and_hms(1977, 1, 9, 1, 30, 0).unwrap(), - ">= 3".to_string(), + vec![], )]; check_count(&rule_str, &recs, expected_count, expected_agg_result); @@ -1604,7 +1586,7 @@ mod tests { "4".to_owned(), ], Utc.with_ymd_and_hms(1977, 1, 9, 1, 30, 0).unwrap(), - ">= 4".to_string(), + vec![], ), AggResult::new( 4, @@ -1616,7 +1598,7 @@ mod tests { "4".to_owned(), ], Utc.with_ymd_and_hms(1977, 1, 9, 5, 30, 0).unwrap(), - ">= 4".to_string(), + vec![], ), AggResult::new( 4, @@ -1628,7 +1610,7 @@ mod tests { "4".to_owned(), ], Utc.with_ymd_and_hms(1977, 1, 9, 9, 30, 0).unwrap(), - ">= 4".to_string(), + vec![], ), ]; @@ -1666,7 +1648,7 @@ mod tests { "_".to_owned(), vec!["2".to_owned(), "3".to_owned(), "4".to_owned()], Utc.with_ymd_and_hms(1977, 1, 9, 3, 30, 0).unwrap(), - ">= 3".to_string(), + vec![], ), AggResult::new( 4, @@ -1678,7 +1660,7 @@ mod tests { "5".to_owned(), ], Utc.with_ymd_and_hms(1977, 1, 9, 20, 00, 0).unwrap(), - ">= 3".to_string(), + vec![], ), ]; check_count(&rule_str, &recs, expected_count, expected_agg_result); @@ -1773,7 +1755,6 @@ mod tests { let mut expect_key = vec![]; let mut expect_field_values = vec![]; let mut expect_start_timedate = vec![]; - let mut expect_condition_op_num = vec![]; for expect_agg in expect_agg_results { let expect_count = expected_counts.get(&expect_agg.key).unwrap_or(&-1); //countupの関数が機能しているかを確認 @@ -1785,7 +1766,6 @@ mod tests { expect_key.push(expect_agg.key); expect_field_values.push(expect_agg.field_values); expect_start_timedate.push(expect_agg.start_timedate); - expect_condition_op_num.push(expect_agg.condition_op_num); } for agg_result in agg_results { println!("{}", &agg_result.start_timedate); @@ -1801,7 +1781,6 @@ mod tests { // field`要素の順番については以降の処理で関連しない assert!(agg_result.field_values.contains(expect_field_value)); } - assert_eq!(agg_result.condition_op_num, expect_condition_op_num[index]); } } } diff --git a/src/detections/rule/mod.rs b/src/detections/rule/mod.rs index a154488dc..f8cfea3ff 100644 --- a/src/detections/rule/mod.rs +++ b/src/detections/rule/mod.rs @@ -17,7 +17,7 @@ use self::selectionnodes::{LeafSelectionNode, SelectionNode}; mod aggregation_parser; mod condition_parser; pub mod correlation_parser; -mod count; +pub(crate) mod count; mod matchers; mod selectionnodes; @@ -94,7 +94,7 @@ impl RuleNode { if result && self.has_agg_condition() { count::count( self, - &event_record.record, + event_record, verbose_flag, quiet_errors_flag, json_input_flag, @@ -370,7 +370,7 @@ impl DetectionNode { } } -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] /// countなどのaggregationの結果を出力する構造体 pub struct AggResult { /// countなどの値 @@ -381,8 +381,8 @@ pub struct AggResult { pub field_values: Vec, ///検知したブロックの最初のレコードの時間 pub start_timedate: DateTime, - ///条件式の情報 - pub condition_op_num: String, + ///検知したブロックのレコードの全時間とEventID + pub agg_record_time_info: Vec, } impl AggResult { @@ -391,14 +391,14 @@ impl AggResult { key_name: String, field_value: Vec, event_start_timedate: DateTime, - condition_op_number: String, + agg_record_time_info: Vec, ) -> AggResult { AggResult { data: count_data, key: key_name, field_values: field_value, start_timedate: event_start_timedate, - condition_op_num: condition_op_number, + agg_record_time_info, } } }