From ff0ea5f97e308ff9ca817ca1b8b4f0e203160bae Mon Sep 17 00:00:00 2001 From: hajime Date: Sat, 3 Feb 2024 21:16:05 +0900 Subject: [PATCH 01/10] remove ununsed function --- src/detections/rule/condition_parser.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/detections/rule/condition_parser.rs b/src/detections/rule/condition_parser.rs index 4f6894b96..c0cafa1ef 100644 --- a/src/detections/rule/condition_parser.rs +++ b/src/detections/rule/condition_parser.rs @@ -40,23 +40,6 @@ pub enum ConditionToken { OperandContainer(IntoIter), // ANDやORやNOT等の演算子に対して、非演算子を表す } -// ここを参考にしました。https://qiita.com/yasuo-ozu/items/7ce2f8ff846ba00dd244 -impl IntoIterator for ConditionToken { - type Item = ConditionToken; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - match self { - ConditionToken::ParenthesisContainer(sub_tokens) => sub_tokens, - ConditionToken::AndContainer(sub_tokens) => sub_tokens, - ConditionToken::OrContainer(sub_tokens) => sub_tokens, - ConditionToken::NotContainer(sub_tokens) => sub_tokens, - ConditionToken::OperandContainer(sub_tokens) => sub_tokens, - _ => vec![].into_iter(), - } - } -} - impl ConditionToken { fn replace_subtoken(self, sub_tokens: Vec) -> ConditionToken { match self { From 1dadd1dd9d1d01507c86616d7f6e07cd80ac41cb Mon Sep 17 00:00:00 2001 From: DustInDark <2350416+hitenkoku@users.noreply.github.com> Date: Fri, 2 Feb 2024 00:56:41 +0900 Subject: [PATCH 02/10] fix(configs): added required option filter in search command #1257 --- src/detections/configs.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/detections/configs.rs b/src/detections/configs.rs index d7c2b3666..c5da43c92 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -951,6 +951,7 @@ pub struct DefaultProfileOption { } #[derive(Args, Clone, Debug)] +#[clap(group(ArgGroup::new("search_input_filtering").args(["keywords", "regex"]).required(true)))] pub struct SearchOption { #[clap(flatten)] pub common_options: CommonOptions, From b1d49988414e14ad1f11c5921f14a0296e4d101c Mon Sep 17 00:00:00 2001 From: DustInDark <2350416+hitenkoku@users.noreply.github.com> Date: Fri, 2 Feb 2024 01:06:06 +0900 Subject: [PATCH 03/10] fix(configs): added conflict command filter in multiple row and json/jsonl output #1257 --- src/detections/configs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/detections/configs.rs b/src/detections/configs.rs index c5da43c92..93422cd07 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -1065,11 +1065,11 @@ pub struct SearchOption { pub clobber: bool, /// Save the search results in JSON format (ex: -J -o results.json) - #[arg(help_heading = Some("Output"), short = 'J', long = "JSON-output", conflicts_with = "jsonl_output", requires = "output", display_order = 100)] + #[arg(help_heading = Some("Output"), short = 'J', long = "JSON-output", conflicts_with_all = ["jsonl_output", "multiline"], requires = "output", display_order = 100)] pub json_output: bool, /// Save the search results in JSONL format (ex: -L -o results.jsonl) - #[arg(help_heading = Some("Output"), short = 'L', long = "JSONL-output", conflicts_with = "jsonl_output", requires = "output", display_order = 100)] + #[arg(help_heading = Some("Output"), short = 'L', long = "JSONL-output", conflicts_with_all = ["jsonl_output", "multiline"], requires = "output", display_order = 100)] pub jsonl_output: bool, /// Output timestamp in European time format (ex: 22-02-2022 22:00:00.123 +02:00) From 0641f34924636c4098cbb37f3e57bf0ef6f32f69 Mon Sep 17 00:00:00 2001 From: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Date: Fri, 2 Feb 2024 08:13:38 +0900 Subject: [PATCH 04/10] update help msg --- README.md | 2 +- src/detections/configs.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 131e036a3..098676aa5 100644 --- a/README.md +++ b/README.md @@ -774,7 +774,7 @@ Filtering: Output: -J, --JSON-output Save the search results in JSON format (ex: -J -o results.json) -L, --JSONL-output Save the search results in JSONL format (ex: -L -o results.jsonl) - -M, --multiline Output event field information in multiple rows + -M, --multiline Output event field information in multiple rows for CSV output -o, --output Save the search results in CSV format (ex: search.csv) General Options: diff --git a/src/detections/configs.rs b/src/detections/configs.rs index 93422cd07..92a71b8ae 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -1056,7 +1056,7 @@ pub struct SearchOption { #[arg(help_heading = Some("Display Settings"), short = 'v', long, display_order = 480)] pub verbose: bool, - /// Output event field information in multiple rows + /// Output event field information in multiple rows for CSV output #[arg(help_heading = Some("Output"), short = 'M', long="multiline", display_order = 390)] pub multiline: bool, From 68c51c7b1b7d7f27e143491e14303d6eed04c134 Mon Sep 17 00:00:00 2001 From: DustInDark <2350416+hitenkoku@users.noreply.github.com> Date: Sat, 3 Feb 2024 23:07:44 +0900 Subject: [PATCH 05/10] feat(main): skipped loading detection rules when running to command which is no need to load rule #1263 --- src/main.rs | 70 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/src/main.rs b/src/main.rs index 652860394..7064a47fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1282,9 +1282,6 @@ impl App { } else { stored_static.include_status.insert("*".into()); } - println!(); - println!("Loading detection rules. Please wait."); - println!(); if stored_static.html_report_flag { let mut output_data = Nested::::new(); @@ -1317,28 +1314,53 @@ impl App { .min_level .to_uppercase(); - let rule_files = detection::Detection::parse_rule_files( - &level, - &target_level, - &stored_static.output_option.as_ref().unwrap().rules, - &filter::exclude_ids(stored_static), - stored_static, - ); - CHECKPOINT - .lock() - .as_mut() - .unwrap() - .rap_check_point("Rule Parse Processing Time"); - let unused_rules_option = stored_static.logon_summary_flag + println!(); + if !(stored_static.logon_summary_flag || stored_static.search_flag - || stored_static.computer_metrics_flag - || stored_static.metrics_flag; - if !unused_rules_option && rule_files.is_empty() { - AlertMessage::alert( - "No rules were loaded. Please download the latest rules with the update-rules command.\r\n", - ) - .ok(); - return; + || stored_static.metrics_flag + || stored_static.computer_metrics_flag) + { + println!("Loading detection rules. Please wait."); + } else if stored_static.logon_summary_flag { + println!("Currently analyzing Logon Summary. Please wait."); + } else if stored_static.search_flag { + println!("Currently searching. Please wait."); + } else if stored_static.metrics_flag { + println!("Currently analyzing Event ID Metrics. Please wait."); + } else if stored_static.computer_metrics_flag { + println!("Currently analyzing Compute Metrics. Please wait."); + } + println!(); + + let mut rule_files = vec![]; + if !(stored_static.logon_summary_flag + || stored_static.search_flag + || stored_static.metrics_flag + || stored_static.computer_metrics_flag) + { + rule_files = detection::Detection::parse_rule_files( + &level, + &target_level, + &stored_static.output_option.as_ref().unwrap().rules, + &filter::exclude_ids(stored_static), + stored_static, + ); + CHECKPOINT + .lock() + .as_mut() + .unwrap() + .rap_check_point("Rule Parse Processing Time"); + let unused_rules_option = stored_static.logon_summary_flag + || stored_static.search_flag + || stored_static.computer_metrics_flag + || stored_static.metrics_flag; + if !unused_rules_option && rule_files.is_empty() { + AlertMessage::alert( + "No rules were loaded. Please download the latest rules with the update-rules command.\r\n", + ) + .ok(); + return; + } } let template = From 57c9509426faf4ccee5e5b759c9495e9af1b11a4 Mon Sep 17 00:00:00 2001 From: DustInDark <2350416+hitenkoku@users.noreply.github.com> Date: Sat, 3 Feb 2024 23:10:32 +0900 Subject: [PATCH 06/10] docs(CHANGELOG): added #1263 --- CHANGELOG-Japanese.md | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG-Japanese.md b/CHANGELOG-Japanese.md index c006dea9d..e2ff1617d 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -13,6 +13,7 @@ - `json-timeline`コマンドの標準出力でJSONフォーマットを出力するように修正した。 (#1197) (@hitenkoku) - JSON入力でデータが配列内にある場合に解析できるようにした。 (#1248) (@hitenkoku) - 古いターミナルでも正しく表示されるように、また読みやすくするために、`‖`区切り文字を`·`区切り文字に変更した。(#1258) (@YamatoSecurity) +- ルールをロードする必要のないコマンドを実行した場合、検出ルールのロードをスキップするようにした。 (#1263) (@hitenkoku) **バグ修正:** diff --git a/CHANGELOG.md b/CHANGELOG.md index d98cda746..1d174c8e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - The `json-timeline` command now outputs in JSON format when outputting to the terminal. (#1197) (@hitenkoku) - Added support for parsing JSON input when the data is inside an array. (#1248) (@hitenkoku) - Changed the `‖` separator into a `·` separator to make it easier to read and render properly on older terminals. (#1258) (@YamatoSecurity) +- Skiped loading detection rules when running to command which is no need to load rule. (#1263) (@hitenkoku) **Bug Fixes:** From cab323b709a2b460ed0eded34e8776997d628f30 Mon Sep 17 00:00:00 2001 From: hajime Date: Sun, 4 Feb 2024 00:19:32 +0900 Subject: [PATCH 07/10] remove unnecessary functions --- src/detections/rule/condition_parser.rs | 202 +++++++----------------- 1 file changed, 54 insertions(+), 148 deletions(-) diff --git a/src/detections/rule/condition_parser.rs b/src/detections/rule/condition_parser.rs index c0cafa1ef..bfb9f3b89 100644 --- a/src/detections/rule/condition_parser.rs +++ b/src/detections/rule/condition_parser.rs @@ -33,59 +33,10 @@ pub enum ConditionToken { SelectionReference(String), // パースの時に上手く処理するために作った疑似的なトークン - ParenthesisContainer(IntoIter), // 括弧を表すトークン + ParenthesisContainer(Box), // 括弧を表すトークン AndContainer(IntoIter), // ANDでつながった条件をまとめるためのトークン OrContainer(IntoIter), // ORでつながった条件をまとめるためのトークン - NotContainer(IntoIter), // 「NOT」と「NOTで否定される式」をまとめるためのトークン この配列には要素が一つしか入らないが、他のContainerと同じように扱えるようにするためにVecにしている。あんまり良くない。 - OperandContainer(IntoIter), // ANDやORやNOT等の演算子に対して、非演算子を表す -} - -impl ConditionToken { - fn replace_subtoken(self, sub_tokens: Vec) -> ConditionToken { - match self { - ConditionToken::ParenthesisContainer(_) => { - ConditionToken::ParenthesisContainer(sub_tokens.into_iter()) - } - ConditionToken::AndContainer(_) => ConditionToken::AndContainer(sub_tokens.into_iter()), - ConditionToken::OrContainer(_) => ConditionToken::OrContainer(sub_tokens.into_iter()), - ConditionToken::NotContainer(_) => ConditionToken::NotContainer(sub_tokens.into_iter()), - ConditionToken::OperandContainer(_) => { - ConditionToken::OperandContainer(sub_tokens.into_iter()) - } - ConditionToken::LeftParenthesis => self, - ConditionToken::RightParenthesis => self, - ConditionToken::Space => self, - ConditionToken::Not => self, - ConditionToken::And => self, - ConditionToken::Or => self, - ConditionToken::SelectionReference(_) => self, - } - } - - pub fn sub_tokens(&self) -> Vec { - // TODO ここでcloneを使わずに実装できるようにしたい。 - match self { - ConditionToken::ParenthesisContainer(sub_tokens) => sub_tokens.as_slice().to_vec(), - ConditionToken::AndContainer(sub_tokens) => sub_tokens.as_slice().to_vec(), - ConditionToken::OrContainer(sub_tokens) => sub_tokens.as_slice().to_vec(), - ConditionToken::NotContainer(sub_tokens) => sub_tokens.as_slice().to_vec(), - ConditionToken::OperandContainer(sub_tokens) => sub_tokens.as_slice().to_vec(), - ConditionToken::LeftParenthesis => vec![], - ConditionToken::RightParenthesis => vec![], - ConditionToken::Space => vec![], - ConditionToken::Not => vec![], - ConditionToken::And => vec![], - ConditionToken::Or => vec![], - ConditionToken::SelectionReference(_) => vec![], - } - } - - pub fn sub_tokens_without_parenthesis(&self) -> Vec { - match self { - ConditionToken::ParenthesisContainer(_) => vec![], - _ => self.sub_tokens(), - } - } + NotContainer(Box), // 「NOT」と「NOTで否定される式」をまとめるためのトークン この配列には要素が一つしか入らないが、他のContainerと同じように扱えるようにするためにVecにしている。あんまり良くない。 } #[derive(Debug)] @@ -161,38 +112,10 @@ impl ConditionCompiler { /// 構文解析を実行する。 fn parse(&self, tokens: IntoIter) -> Result { // 括弧で囲まれた部分を解析します。 - // (括弧で囲まれた部分は後で解析するため、ここでは一時的にConditionToken::ParenthesisContainerに変換しておく) - // 括弧の中身を解析するのはparse_rest_parenthesis()で行う。 let tokens = self.parse_parenthesis(tokens)?; // AndとOrをパースする。 - let tokens = self.parse_and_or_operator(tokens)?; - - // OperandContainerトークンの中身をパースする。(現状、Notを解析するためだけにある。将来的に修飾するキーワードが増えたらここを変える。) - let token = Self::parse_operand_container(tokens)?; - - // 括弧で囲まれている部分を探して、もしあればその部分を再帰的に構文解析します。 - self.parse_rest_parenthesis(token) - } - - /// 括弧で囲まれている部分を探して、もしあればその部分を再帰的に構文解析します。 - fn parse_rest_parenthesis(&self, token: ConditionToken) -> Result { - if let ConditionToken::ParenthesisContainer(sub_token) = token { - let new_token = self.parse(sub_token)?; - return Result::Ok(new_token); - } - - let sub_tokens = token.sub_tokens(); - if sub_tokens.is_empty() { - return Result::Ok(token); - } - - let mut new_sub_tokens = vec![]; - for sub_token in sub_tokens { - let new_token = self.parse_rest_parenthesis(sub_token)?; - new_sub_tokens.push(new_token); - } - Result::Ok(token.replace_subtoken(new_sub_tokens)) + return self.parse_and_or_operator(tokens); } /// 字句解析を行う @@ -278,7 +201,9 @@ impl ConditionCompiler { } // ここで再帰的に呼び出す。 - ret.push(ConditionToken::ParenthesisContainer(sub_tokens.into_iter())); + let parsed_sub_token = self.parse(sub_tokens.into_iter())?; + let parenthesis_token = ConditionToken::ParenthesisContainer(Box::new(parsed_sub_token)); + ret.push(parenthesis_token); } // この時点で右括弧が残っている場合は右括弧の数が左括弧よりも多いことを表している。 @@ -342,79 +267,63 @@ impl ConditionCompiler { } // 次にOrでつながっている部分をまとめる - let or_contaienr = ConditionToken::OrContainer(operands.into_iter()); - Result::Ok(or_contaienr) + return Result::Ok(ConditionToken::OrContainer(operands.into_iter())); } /// OperandContainerの中身をパースする。現状はNotをパースするためだけに存在している。 - fn parse_operand_container(parent_token: ConditionToken) -> Result { - if let ConditionToken::OperandContainer(sub_tokens) = parent_token { - // 現状ではNOTの場合は、「not」と「notで修飾されるselectionノードの名前」の2つ入っているはず - // NOTが無い場合、「selectionノードの名前」の一つしか入っていないはず。 + fn parse_operand_container(sub_tokens: Vec) -> Result { + // 現状ではNOTの場合は、「not」と「notで修飾されるselectionノードの名前」の2つ入っているはず + // NOTが無い場合、「selectionノードの名前」の一つしか入っていないはず。 + + // 上記の通り、3つ以上入っていることはないはず。 + if sub_tokens.len() >= 3 { + return Result::Err( + "Unknown error. Maybe it is because there are multiple names of selection nodes." + .to_string(), + ); + } - // 上記の通り、3つ以上入っていることはないはず。 - if sub_tokens.len() >= 3 { - return Result::Err( - "Unknown error. Maybe it is because there are multiple names of selection nodes." - .to_string(), - ); - } + // 0はありえないはず + if sub_tokens.len() == 0 { + return Result::Err("Unknown error.".to_string()); + } - // 0はありえないはず - if sub_tokens.len() == 0 { - return Result::Err("Unknown error.".to_string()); + // 1つだけ入っている場合、NOTはありえない。 + if sub_tokens.len() == 1 { + let operand_subtoken = sub_tokens.into_iter().next().unwrap(); + if let ConditionToken::Not = operand_subtoken { + return Result::Err("An illegal not was found.".to_string()); } - // 1つだけ入っている場合、NOTはありえない。 - if sub_tokens.len() == 1 { - let operand_subtoken = sub_tokens.into_iter().next().unwrap(); - if let ConditionToken::Not = operand_subtoken { - return Result::Err("An illegal not was found.".to_string()); - } - - return Result::Ok(operand_subtoken); - } + return Result::Ok(operand_subtoken); + } - // 2つ入っている場合、先頭がNotで次はNotじゃない何かのはず - let mut sub_tokens_ite = sub_tokens; - let first_token = sub_tokens_ite.next().unwrap(); - let second_token = sub_tokens_ite.next().unwrap(); - if let ConditionToken::Not = first_token { - if let ConditionToken::Not = second_token { - Result::Err("Not is continuous.".to_string()) - } else { - let not_container = - ConditionToken::NotContainer(vec![second_token].into_iter()); - Result::Ok(not_container) - } + // 2つ入っている場合、先頭がNotで次はNotじゃない何かのはず + let mut sub_ite = sub_tokens.into_iter(); + let first_token = sub_ite.next().unwrap(); + let second_token = sub_ite.next().unwrap(); + if let ConditionToken::Not = first_token { + if let ConditionToken::Not = second_token { + Result::Err("Not is continuous.".to_string()) } else { - Result::Err( - "Unknown error. Maybe it is because there are multiple names of selection nodes." - .to_string(), - ) + let not_container = + ConditionToken::NotContainer(Box::new(second_token)); + Result::Ok(not_container) } } else { - let sub_tokens = parent_token.sub_tokens_without_parenthesis(); - if sub_tokens.is_empty() { - return Result::Ok(parent_token); - } - - let mut new_sub_tokens = vec![]; - for sub_token in sub_tokens { - let new_sub_token = Self::parse_operand_container(sub_token)?; - new_sub_tokens.push(new_sub_token); - } - - Result::Ok(parent_token.replace_subtoken(new_sub_tokens)) + Result::Err( + "Unknown error. Maybe it is because there are multiple names of selection nodes." + .to_string(), + ) } } - /// ConditionTokenからSelectionNodeトレイトを実装した構造体に変換します。 + /// convert from ConditionToken into SelectionNode fn to_selectnode( token: ConditionToken, name_2_node: &HashMap>>, ) -> Result, String> { - // RefSelectionNodeに変換 + // convert to RefSelectionNode if let ConditionToken::SelectionReference(selection_name) = token { let selection_node = name_2_node.get(&selection_name); if let Some(select_node) = selection_node { @@ -449,17 +358,17 @@ impl ConditionCompiler { } // NotSelectionNodeに変換 - if let ConditionToken::NotContainer(sub_tokens) = token { - if sub_tokens.len() > 1 { - return Result::Err("Unknown error".to_string()); - } - + if let ConditionToken::NotContainer(sub_token) = token { let select_sub_node = - Self::to_selectnode(sub_tokens.into_iter().next().unwrap(), name_2_node)?; + Self::to_selectnode(*sub_token, name_2_node)?; let select_not_node = NotSelectionNode::new(select_sub_node); return Result::Ok(Box::new(select_not_node)); } + if let ConditionToken::ParenthesisContainer(sub_token) = token { + return Result::Ok(Self::to_selectnode(*sub_token, name_2_node)?); + } + Result::Err("Unknown error".to_string()) } @@ -482,9 +391,8 @@ impl ConditionCompiler { ret.push(token); continue; } - ret.push(ConditionToken::OperandContainer( - grouped_operands.into_iter(), - )); + + ret.push(ConditionCompiler::parse_operand_container(grouped_operands)?); ret.push(token); grouped_operands = vec![]; continue; @@ -493,9 +401,7 @@ impl ConditionCompiler { grouped_operands.push(token); } if !grouped_operands.is_empty() { - ret.push(ConditionToken::OperandContainer( - grouped_operands.into_iter(), - )); + ret.push(ConditionCompiler::parse_operand_container(grouped_operands)?); } Result::Ok(ret) From 21e61cece872a4b0b6276ebf4df207a7be9ef5ba Mon Sep 17 00:00:00 2001 From: hajime Date: Sun, 4 Feb 2024 00:31:21 +0900 Subject: [PATCH 08/10] cargo fmt --- src/detections/rule/condition_parser.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/detections/rule/condition_parser.rs b/src/detections/rule/condition_parser.rs index bfb9f3b89..ddb7ce132 100644 --- a/src/detections/rule/condition_parser.rs +++ b/src/detections/rule/condition_parser.rs @@ -34,8 +34,8 @@ pub enum ConditionToken { // パースの時に上手く処理するために作った疑似的なトークン ParenthesisContainer(Box), // 括弧を表すトークン - AndContainer(IntoIter), // ANDでつながった条件をまとめるためのトークン - OrContainer(IntoIter), // ORでつながった条件をまとめるためのトークン + AndContainer(IntoIter), // ANDでつながった条件をまとめるためのトークン + OrContainer(IntoIter), // ORでつながった条件をまとめるためのトークン NotContainer(Box), // 「NOT」と「NOTで否定される式」をまとめるためのトークン この配列には要素が一つしか入らないが、他のContainerと同じように扱えるようにするためにVecにしている。あんまり良くない。 } @@ -202,7 +202,8 @@ impl ConditionCompiler { // ここで再帰的に呼び出す。 let parsed_sub_token = self.parse(sub_tokens.into_iter())?; - let parenthesis_token = ConditionToken::ParenthesisContainer(Box::new(parsed_sub_token)); + let parenthesis_token = + ConditionToken::ParenthesisContainer(Box::new(parsed_sub_token)); ret.push(parenthesis_token); } @@ -306,8 +307,7 @@ impl ConditionCompiler { if let ConditionToken::Not = second_token { Result::Err("Not is continuous.".to_string()) } else { - let not_container = - ConditionToken::NotContainer(Box::new(second_token)); + let not_container = ConditionToken::NotContainer(Box::new(second_token)); Result::Ok(not_container) } } else { @@ -359,8 +359,7 @@ impl ConditionCompiler { // NotSelectionNodeに変換 if let ConditionToken::NotContainer(sub_token) = token { - let select_sub_node = - Self::to_selectnode(*sub_token, name_2_node)?; + let select_sub_node = Self::to_selectnode(*sub_token, name_2_node)?; let select_not_node = NotSelectionNode::new(select_sub_node); return Result::Ok(Box::new(select_not_node)); } @@ -392,7 +391,9 @@ impl ConditionCompiler { continue; } - ret.push(ConditionCompiler::parse_operand_container(grouped_operands)?); + ret.push(ConditionCompiler::parse_operand_container( + grouped_operands, + )?); ret.push(token); grouped_operands = vec![]; continue; @@ -401,7 +402,9 @@ impl ConditionCompiler { grouped_operands.push(token); } if !grouped_operands.is_empty() { - ret.push(ConditionCompiler::parse_operand_container(grouped_operands)?); + ret.push(ConditionCompiler::parse_operand_container( + grouped_operands, + )?); } Result::Ok(ret) From a1557fbb32f03851a522ce698ec77ad0e2882c96 Mon Sep 17 00:00:00 2001 From: hajime Date: Sun, 4 Feb 2024 00:49:44 +0900 Subject: [PATCH 09/10] refactoring --- src/detections/rule/condition_parser.rs | 147 ++++++++++++------------ 1 file changed, 73 insertions(+), 74 deletions(-) diff --git a/src/detections/rule/condition_parser.rs b/src/detections/rule/condition_parser.rs index ddb7ce132..cb70926a5 100644 --- a/src/detections/rule/condition_parser.rs +++ b/src/detections/rule/condition_parser.rs @@ -39,6 +39,77 @@ pub enum ConditionToken { NotContainer(Box), // 「NOT」と「NOTで否定される式」をまとめるためのトークン この配列には要素が一つしか入らないが、他のContainerと同じように扱えるようにするためにVecにしている。あんまり良くない。 } +impl ConditionToken { + /// convert from ConditionToken into SelectionNode + pub fn to_selection_node( + self, + name_2_node: &HashMap>>, + ) -> Result, String> { + return match self { + ConditionToken::SelectionReference(selection_name) => { + let selection_node = name_2_node.get(&selection_name); + if let Some(select_node) = selection_node { + let selection_node = select_node; + let selection_node = Arc::clone(selection_node); + let ref_node = RefSelectionNode::new(selection_node); + return Result::Ok(Box::new(ref_node)); + } else { + let err_msg = format!("{selection_name} is not defined."); + return Result::Err(err_msg); + } + } + ConditionToken::ParenthesisContainer(sub_token) => { + Result::Ok((*sub_token).to_selection_node(name_2_node)?) + } + ConditionToken::AndContainer(sub_tokens) => { + let mut select_and_node = AndSelectionNode::new(); + for sub_token in sub_tokens { + let sub_node = sub_token.to_selection_node(name_2_node)?; + select_and_node.child_nodes.push(sub_node); + } + return Result::Ok(Box::new(select_and_node)); + } + ConditionToken::OrContainer(sub_tokens) => { + let mut select_or_node = OrSelectionNode::new(); + for sub_token in sub_tokens { + let sub_node = sub_token.to_selection_node(name_2_node)?; + select_or_node.child_nodes.push(sub_node); + } + return Result::Ok(Box::new(select_or_node)); + } + ConditionToken::NotContainer(sub_token) => { + let select_sub_node = sub_token.to_selection_node(name_2_node)?; + let select_not_node = NotSelectionNode::new(select_sub_node); + return Result::Ok(Box::new(select_not_node)); + } + ConditionToken::LeftParenthesis => Result::Err("Unknown error".to_string()), + ConditionToken::RightParenthesis => Result::Err("Unknown error".to_string()), + ConditionToken::Space => Result::Err("Unknown error".to_string()), + ConditionToken::Not => Result::Err("Unknown error".to_string()), + ConditionToken::And => Result::Err("Unknown error".to_string()), + ConditionToken::Or => Result::Err("Unknown error".to_string()), + }; + } + + pub fn to_condition_token(token: String) -> ConditionToken { + if token == "(" { + ConditionToken::LeftParenthesis + } else if token == ")" { + ConditionToken::RightParenthesis + } else if token == " " { + ConditionToken::Space + } else if token == "not" { + ConditionToken::Not + } else if token == "and" { + ConditionToken::And + } else if token == "or" { + ConditionToken::Or + } else { + ConditionToken::SelectionReference(token) + } + } +} + #[derive(Debug)] pub struct ConditionCompiler {} @@ -106,7 +177,7 @@ impl ConditionCompiler { let parsed = self.parse(tokens.into_iter())?; - Self::to_selectnode(parsed, name_2_node) + parsed.to_selection_node(name_2_node) } /// 構文解析を実行する。 @@ -133,7 +204,7 @@ impl ConditionCompiler { } let mached_str = captured.unwrap().get(0).unwrap().as_str(); - let token = self.to_enum(mached_str.to_string()); + let token = ConditionToken::to_condition_token(mached_str.to_string()); if let ConditionToken::Space = token { // 空白は特に意味ないので、読み飛ばす。 cur_condition_str = cur_condition_str.replacen(mached_str, "", 1); @@ -147,25 +218,6 @@ impl ConditionCompiler { Result::Ok(tokens) } - /// 文字列をConditionTokenに変換する。 - fn to_enum(&self, token: String) -> ConditionToken { - if token == "(" { - ConditionToken::LeftParenthesis - } else if token == ")" { - ConditionToken::RightParenthesis - } else if token == " " { - ConditionToken::Space - } else if token == "not" { - ConditionToken::Not - } else if token == "and" { - ConditionToken::And - } else if token == "or" { - ConditionToken::Or - } else { - ConditionToken::SelectionReference(token) - } - } - /// 右括弧と左括弧をだけをパースする。戻り値の配列にはLeftParenthesisとRightParenthesisが含まれず、代わりにTokenContainerに変換される。TokenContainerが括弧で囲まれた部分を表現している。 fn parse_parenthesis( &self, @@ -318,59 +370,6 @@ impl ConditionCompiler { } } - /// convert from ConditionToken into SelectionNode - fn to_selectnode( - token: ConditionToken, - name_2_node: &HashMap>>, - ) -> Result, String> { - // convert to RefSelectionNode - if let ConditionToken::SelectionReference(selection_name) = token { - let selection_node = name_2_node.get(&selection_name); - if let Some(select_node) = selection_node { - let selection_node = select_node; - let selection_node = Arc::clone(selection_node); - let ref_node = RefSelectionNode::new(selection_node); - return Result::Ok(Box::new(ref_node)); - } else { - let err_msg = format!("{selection_name} is not defined."); - return Result::Err(err_msg); - } - } - - // AndSelectionNodeに変換 - if let ConditionToken::AndContainer(sub_tokens) = token { - let mut select_and_node = AndSelectionNode::new(); - for sub_token in sub_tokens { - let sub_node = Self::to_selectnode(sub_token, name_2_node)?; - select_and_node.child_nodes.push(sub_node); - } - return Result::Ok(Box::new(select_and_node)); - } - - // OrSelectionNodeに変換 - if let ConditionToken::OrContainer(sub_tokens) = token { - let mut select_or_node = OrSelectionNode::new(); - for sub_token in sub_tokens { - let sub_node = Self::to_selectnode(sub_token, name_2_node)?; - select_or_node.child_nodes.push(sub_node); - } - return Result::Ok(Box::new(select_or_node)); - } - - // NotSelectionNodeに変換 - if let ConditionToken::NotContainer(sub_token) = token { - let select_sub_node = Self::to_selectnode(*sub_token, name_2_node)?; - let select_not_node = NotSelectionNode::new(select_sub_node); - return Result::Ok(Box::new(select_not_node)); - } - - if let ConditionToken::ParenthesisContainer(sub_token) = token { - return Result::Ok(Self::to_selectnode(*sub_token, name_2_node)?); - } - - Result::Err("Unknown error".to_string()) - } - /// ConditionTokenがAndまたはOrTokenならばTrue fn is_logical(&self, token: &ConditionToken) -> bool { matches!(token, ConditionToken::And | ConditionToken::Or) From ff960690d0db862286123927ea3ef82c4ceba046 Mon Sep 17 00:00:00 2001 From: hajime Date: Sun, 4 Feb 2024 00:59:10 +0900 Subject: [PATCH 10/10] fmt and clippy --- src/detections/rule/condition_parser.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/detections/rule/condition_parser.rs b/src/detections/rule/condition_parser.rs index cb70926a5..968f5a5bf 100644 --- a/src/detections/rule/condition_parser.rs +++ b/src/detections/rule/condition_parser.rs @@ -41,7 +41,7 @@ pub enum ConditionToken { impl ConditionToken { /// convert from ConditionToken into SelectionNode - pub fn to_selection_node( + pub fn into_selection_node( self, name_2_node: &HashMap>>, ) -> Result, String> { @@ -59,12 +59,12 @@ impl ConditionToken { } } ConditionToken::ParenthesisContainer(sub_token) => { - Result::Ok((*sub_token).to_selection_node(name_2_node)?) + Result::Ok((*sub_token).into_selection_node(name_2_node)?) } ConditionToken::AndContainer(sub_tokens) => { let mut select_and_node = AndSelectionNode::new(); for sub_token in sub_tokens { - let sub_node = sub_token.to_selection_node(name_2_node)?; + let sub_node = sub_token.into_selection_node(name_2_node)?; select_and_node.child_nodes.push(sub_node); } return Result::Ok(Box::new(select_and_node)); @@ -72,13 +72,13 @@ impl ConditionToken { ConditionToken::OrContainer(sub_tokens) => { let mut select_or_node = OrSelectionNode::new(); for sub_token in sub_tokens { - let sub_node = sub_token.to_selection_node(name_2_node)?; + let sub_node = sub_token.into_selection_node(name_2_node)?; select_or_node.child_nodes.push(sub_node); } return Result::Ok(Box::new(select_or_node)); } ConditionToken::NotContainer(sub_token) => { - let select_sub_node = sub_token.to_selection_node(name_2_node)?; + let select_sub_node = sub_token.into_selection_node(name_2_node)?; let select_not_node = NotSelectionNode::new(select_sub_node); return Result::Ok(Box::new(select_not_node)); } @@ -177,7 +177,7 @@ impl ConditionCompiler { let parsed = self.parse(tokens.into_iter())?; - parsed.to_selection_node(name_2_node) + parsed.into_selection_node(name_2_node) } /// 構文解析を実行する。 @@ -186,7 +186,7 @@ impl ConditionCompiler { let tokens = self.parse_parenthesis(tokens)?; // AndとOrをパースする。 - return self.parse_and_or_operator(tokens); + self.parse_and_or_operator(tokens) } /// 字句解析を行う @@ -320,7 +320,7 @@ impl ConditionCompiler { } // 次にOrでつながっている部分をまとめる - return Result::Ok(ConditionToken::OrContainer(operands.into_iter())); + Result::Ok(ConditionToken::OrContainer(operands.into_iter())) } /// OperandContainerの中身をパースする。現状はNotをパースするためだけに存在している。 @@ -337,7 +337,7 @@ impl ConditionCompiler { } // 0はありえないはず - if sub_tokens.len() == 0 { + if sub_tokens.is_empty() { return Result::Err("Unknown error.".to_string()); }