diff --git a/CHANGELOG-Japanese.md b/CHANGELOG-Japanese.md index 39e783801..6a4edfc21 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -18,6 +18,9 @@ - `count`で複数のグループを指定できるようにした。例: `count() by IpAddress,SubStatus,LogonType >= 2`。また、出力される結果を更新した。例: `[condition] count(TargetUserName) by IpAddress > 3 in timeframe [result] count: 4 TargetUserName:tanaka/Administrator/adsyncadmin/suzuki IpAddress:- timeframe:5m` -> `Count: 4 ¦ TargetUserName: tanaka/Administrator/adsyncadmin/suzuki ¦ IpAddress: -` (#1339) (@fukusuket) - リリースモードでのオーバーフローチェックを有効にした。(#1348) (@YamatoSecurity) - フィールドデータマッピングファイル(`rules/config/data_mapping/*.yaml`)で任意の`Provider_Name`フィールドを指定できるようにし、`Data[x]`表記に対応した。(#1350) (@fukusuket) +- カウントルールのJSON出力で、フィールド情報が分離されるようになった。 (#1342) (@fukusuket) + - 以前: `"Details": "[condition] count() by IpAddress >= 5 in timeframe [result] count:3558 IpAddress:192.168.198.149 timeframe:5m"` + - 現在: `"Details": { "Count": 3558, "IpAddress": "192.168.198.149" }` ## 2.15.0 [2024/04/20] "Sonic Release" diff --git a/CHANGELOG.md b/CHANGELOG.md index ef1c1c7ce..6effd323d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ - You can now specify multiple groups with `count`. Ex: `count() by IpAddress,SubStatus,LogonType >= 2` Also, the output has been updated. Ex: `[condition] count(TargetUserName) by IpAddress > 3 in timeframe [result] count: 4 TargetUserName:tanaka/Administrator/adsyncadmin/suzuki IpAddress:- timeframe:5m` -> `Count: 4 ¦ TargetUserName: tanaka/Administrator/adsyncadmin/suzuki ¦ IpAddress: -` (#1339) (@fukusuket) - Enabled overflow checks in release mode. (#1348) (@YamatoSecurity) - Added support for specifying an optional `Provider_Name` field in field data mapping files (`rules/config/data_mapping/*.yaml`) as well as support for `Data[x]` notation. (#1350) (@fukusuket) +- JSON output in count rules now separates field information. (#1342) (@fukusuket) + - Before: `"Details": "[condition] count() by IpAddress >= 5 in timeframe [result] count:3558 IpAddress:192.168.198.149 timeframe:5m"` + - After: `"Details": { "Count": 3558, "IpAddress": "192.168.198.149" }` ## 2.15.0 [2024/04/20] "Sonic Release" diff --git a/src/afterfact.rs b/src/afterfact.rs index ccfed9b88..37e6613bd 100644 --- a/src/afterfact.rs +++ b/src/afterfact.rs @@ -1886,48 +1886,43 @@ pub fn output_json_str( continue; } let details_target_stock = details_target_stocks.unwrap(); - // aggregation conditionの場合は分解せずにそのまま出力する - if detect_info.is_condition { - let details_val = - if details_target_stock.is_empty() || details_target_stock[0] == "-" { - "-".into() - } else { - details_target_stock[0].clone() - }; - output_stock.push(_create_json_output_format( - key, - &details_val, - key.starts_with('\"'), - details_val.starts_with('\"'), - 4, - )); - if jsonl_output_flag { - target.push(output_stock.join("")); - } else { - target.push(output_stock.join("\n")); - } - continue; - } else { - output_stock.push(format!(" \"{key}\": {{")); - }; let mut children_output_stock: HashMap> = HashMap::new(); let mut children_output_order = vec![]; - for contents in details_target_stock.iter() { - let (key, value) = contents.split_once(':').unwrap_or_default(); - let output_key = _convert_valid_json_str(&[key.trim()], false); - let fmted_val = _convert_valid_json_str(&[value.trim()], false); - if let RawEntryMut::Vacant(_) = children_output_stock - .raw_entry_mut() - .from_key(output_key.as_str()) - { - children_output_order.push(output_key.clone()); + if detect_info.is_condition { + if details_target_stock[0] == "-" { + output_stock.push(_create_json_output_format( + key, + details_target_stock[0].as_str(), + key.starts_with('\"'), + details_target_stock[0].starts_with('\"'), + 4, + )); + if jsonl_output_flag { + target.push(output_stock.join("")); + } else { + target.push(output_stock.join("\n")); + } + continue; } - children_output_stock - .entry(output_key.into()) - .or_insert(vec![]) - .push(fmted_val.into()); + let splitted_agg_details = details_target_stock[0] + .split(" ¦ ") + .map(|x| x.into()) + .collect_vec(); + process_target_stock( + &splitted_agg_details, + &mut children_output_stock, + &mut children_output_order, + ); + } else { + process_target_stock( + details_target_stock, + &mut children_output_stock, + &mut children_output_order, + ); } + output_stock.push(format!(" \"{key}\": {{")); + // ルール内での表示順に合わせた表示順を戻した配列 let mut sorted_children_output_stock: Vec<( &CompactString, @@ -2062,6 +2057,27 @@ pub fn output_json_str( } } +fn process_target_stock( + details_target_stock: &[CompactString], + children_output_stock: &mut HashMap>, + children_output_order: &mut Vec, +) { + for contents in details_target_stock.iter() { + let (key, value) = contents.split_once(':').unwrap_or_default(); + let output_key = _convert_valid_json_str(&[key.trim()], false); + let fmted_val = _convert_valid_json_str(&[value.trim()], false); + if let RawEntryMut::Vacant(_) = children_output_stock + .raw_entry_mut() + .from_key(output_key.as_str()) + { + children_output_order.push(output_key.clone().into()); + } + children_output_stock + .entry(output_key.into()) + .or_insert(vec![]) + .push(fmted_val.into()); + } +} /// output detected rule author name function. fn output_detected_rule_authors( rule_author_counter: &HashMap,