v1.2 pre-release marge (#495)
* Fix/fix clippy warn (#434) - Fixed following Clippy Warnings(previous warning count: 671 -> after: 4) - clippy::needless_return - clippy::println_empty_string - clippy::redundant_field_names - clippy::single_char_pattern - clippy::len_zero - clippy::iter_nth_zero - clippy::bool_comparison - clippy::question_mark - clippy::needless_collect - clippy::unnecessary_unwrap - clippy::ptr_arg - clippy::needless_collect - clippy::needless_borrow - clippy::new_without_default - clippy::assign_op_pattern - clippy::bool_assert_comparison - clippy::into_iter_on_ref - clippy::deref_addrof - clippy::while_let_on_iterator - clippy::match_like_matches_macro - clippy::or_fun_call - clippy::useless_conversion - clippy::let_and_return - clippy::redundant_clone - clippy::redundant_closure - clippy::cmp_owned - clippy::upper_case_acronyms - clippy::map_identity - clippy::unused_io_amount - clippy::assertions_on_constants - clippy::op_ref - clippy::useless_vec - clippy::vec_init_then_push - clippy::useless_format - clippy::bind_instead_of_map - clippy::bool_comparison - clippy::clone_on_copy - clippy::too_many_arguments - clippy::module_inception - fixed clippy::needless_lifetimes - fixed clippy::borrowed_box (Thanks for helping by hach1yon!) * Merge main and output fix#443#444 (#445) * removed tools/sigmac (#441) * removed tools/sigmac - moved tools/sigmac to hayabusa-rules repo * fixed doc link tools/sigmac * fixed submodule track * fixed submodule track from latest to v1.1.0 tag * fixed link * erased enter #444 * erased enter #444 * reverted logo enter * fixed rules submodule target commit #444 Co-authored-by: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> * readme update screenshots etc (#448) * Opensslを静的にコンパイルするためにCargo.tomlの設定変更 (#437) * cargo update - openssl static * updated cargo * macos2apple * cargo update * cargo update * aliasキーがない場合もEvent.EventDataを自動で走査する (#442) * add no event key * support not-register-alias search * added checking EventData when key do not match in alias #290 - added checking key in Event.EventData, if key is not exist in eventkey_alias.txt. * cargo fmt * fixed panic when filter files does not exists * fixed errorlog format when filter config files does not exist Co-authored-by: DustInDark <nextsasasa@gmail.com> * changed downcast library from mopa to downcast_rs #447 (#450) * Fixed Clippy Warnings (#451) * fixed clippy warn * fixed cargo clippy warnging * fixed clippy warngings in clippy ver 0.1.59 * fixed clippy warnings clippy::unnecessary_to_owned * added temporary blackhat arsenal badge * added rust report card badges #453 * added repository maintenance levels badge #453 * documentation update macOS usage etc * update * added clippy workflow #428 (#429) * added clippy workflow #428 * fixed action yaml to run clippy #428 * fixed indent * fixed workflow * fixed workflow error * fixed indent * changed no annotation #428 * adujusted annotation version * fixed clippy::needless_match * remove if let exception * removed unnecessary permission check #428 * statistics event id update (#457) * Feature/#440 refactoring #395 (#464) * updated submodule * fix degrade for pull req #464 (#468) * fix degrade for pull req #464 * add trim * Fearture/ added output update result#410 (#452) * add git2 crate #391 * added Update option #391 * updated readme #391 * fixed cargo.lock * fixed option if-statement #391 * changed utc short option and rule-update short option #391 * updated readme * updated readme * fixed -u long option & version number update #391 * added fast-forwarding rules repository #391 * updated command line option #391 * moved output logo prev update rule * fixed readme #391 * removed recursive option in readme * changed rules update from clone and pull to submodule update #391 * fixed document * changed unnecessary clone recursively to clone only * English message update. * cargo fmt * English message update. (4657c35e5ccherry-pick) * added create rules folder when rules folder is not exist * fixed gitmodules github-rules url from ssh to https * added output of updated file #420 * fixed error #410 * changed update rule list seq * added test * fixed output #410 * fixed output and fixed output date field when modified field is lacked #410 * fixed compile error * fixed output - added enter after Latest rule update output - added output when no exist new rule - fixed Latest rule update date format - changed output from 'Latest rule update' to 'Latest rules update' * fixed compile error * changed modified date source from rules folder to each yml rule file * formatting use chrono in main.rs * merge develop clippy ci * fixed output when no update rule #410 - removed Latest rule update - no output "Rules update successfully" when No rule changed * Change English Co-authored-by: Tanaka Zakku <71482215+YamatoSecurity@users.noreply.github.com> * Remove unnecessary code from timeline_event_info and rename files for… (#470) * Remove unnecessary code from timeline_event_info and rename files for issue462 * Remove unnecessary code #462 * add equalsfield pipe (#467) * Enhancement: add config config #456 (#471) * added config option #456 * added process of option to speicifed config folder #456 following files adjust config option. * noisy_rules.txt * exclude_rules.txt * fixed usage in readme * updated rules submodule: * fixed process when yml file exist in .git folder * ignore when yml file exist in .git folder * Add: --level-tuning option's outline * Add: read Rule files * Add: input rule_level.txt files & read rules * cargo fmt * Add: level-tuning function * Reface: split to options file * WIP: Text overwrite failed... * Fix: Text overwrite was failed * Add: Error handlings * Add: id, level validation * mv: IDS_REGEX to configs file * fix: level tuning's file name * Cargo fmt * Pivot Keyword List機能の追加 (#412) * add get_pivot_keyword() func * change function name and call it's function * [WIP] support config file * compilete output * cargo fmt * [WIP] add test * add test * support -o option in pivot * add pivot mod * fix miss * pass test in pivot.rs * add comment * pass all test * add fast return * fix output * add test config file * review * rebase * cargo fmt * test pass * fix clippy in my commit * cargo fmt * little refactor * change file input logic and config format * [WIP] change output * [wip] change deta structure * change output & change data structure * pass test * add config * cargo fmt & clippy & rebase * fix cllipy * delete /rules/ in .gitignore * clean comment * clean * clean * fix rebase miss * fix rebase miss * fix clippy * file name output on -o to stdout * add pivot_keywords.txt to ./config * updated english * Documentation update * cargo fmt and clean * updated translate japanese * readme update * readme update Co-authored-by: DustInDark <nextsasasa@gmail.com> Co-authored-by: Tanaka Zakku <71482215+YamatoSecurity@users.noreply.github.com> * Add: test * Add: README.md * Cargo fmt * Use #[cfg(test)] * Fixed output stop when control char exist in windows terminal (#485) * added control character filter in details #382 * fixed document - removed fixed windows teminal caution in readme * fixed level tuning test and added test files #390 * changed level_tuning.txt header from next_level to new_level * fixed convert miss change to low level * added run args rules path to check test easy #390 * fixed comment out processing in level_tuning.txt * fixed config to show level-tuning option * fixed level-tuning option usage from required to option * reduce output mitre attack detail tachnique No. by config file (#483) * reduced mitre attck tag output by config file #477 * prepared 1.2.0 version toml * added test files and mitre attck strategy tag file #477 * fixed cargo.toml version * updated cargo.lock * output tag english update * cargo fmt Co-authored-by: Tanaka Zakku <71482215+YamatoSecurity@users.noreply.github.com> * Fix: test file's path was incorrect * Add: add test_files/config/level_tuning.txt * Add: Flush method. * inserted debug data * reverted config usage * fixed test yaml file path * Feature/#216 output allfields csvnewcolumn (#469) * refactoring * refactoring * under constructing * underconstructing * under construction * underconstructing * fix existing testcase * finish implement * fmt * add option * change name * fix control code bug * fix disp * change format and fix testcase * fix help * Fix: show usage when hayabusa has no args * rm: debug line * Enhance/warning architecture#478 (#482) * added enhance of architecture check #478 * changed check architecture process after output logo #478 * English msg update * fixed detect method of os-bit to windows and linux * removed mac and unix architecture and binary and updated its process of windows * fix clippy * added check on Wow64 env #478 * Update contributors.txt Co-authored-by: Tanaka Zakku <71482215+YamatoSecurity@users.noreply.github.com> * added --level-tuning option to usage * Revert "added --level-tuning option to usage" This reverts commite6a74090a3. * readme update * Update README-Japanese.md * readme, version, cargo update * typo fix * typo fix * rm: duplicated test & fix test name * Add: show logo, and some infos * small english fix * twitter link fix (#486) * added feature of tag output reducing to agg condition #477 (#488) * changed level output from informational to info #491 * updated rules submodule * v1.2 changelog update (#473) * changelog update * Update CHANGELOG.md added contributor in "Fields that are not defined in eventkey_alias.txt will automatically be searched in Event.EventData." ref #442 * Update CHANGELOG-Japanese.md Fields that are not defined in eventkey_alias.txt will automatically be searched in Event.EventData. added contributor in "Fields that are not defined in eventkey_alias.txt will automatically be searched in Event.EventData." ref #442 * Update CHANGELOG.md added bug fixes (#444) and `Performance and. accuracy` add contributor ref(#395) * Update CHANGELOG-Japanese.md * Translated v1.2 change log to Japanese v1.2の内容を日本語に修正 * fixed typo added lacked back quote. * added description added following issue and pr description to readme - #216 / #469 L8 - #390 / #459 L9 - #478 / #482 L19 - #477/ #483 L20 * added description README.md added following issue and pr description to readme - #216 / #469 L8 - #390 / #459 L9 - #478 / #482 L19 - #477/ #483 L20 * changelog update * changelog update * update Co-authored-by: DustInDark <nextsasasa@gmail.com> * updated rules #493 (#494) * Resolve conflict develop (#496) * removed tools/sigmac (#441) * removed tools/sigmac - moved tools/sigmac to hayabusa-rules repo * fixed doc link tools/sigmac * fixed submodule track * fixed submodule track from latest to v1.1.0 tag * fixed link * fixed rules submodule targe #444 * updated submodule * updated rules submodule Co-authored-by: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Co-authored-by: Yamato Security <71482215+YamatoSecurity@users.noreply.github.com> Co-authored-by: kazuminn <warugaki.k.k@gmail.com> Co-authored-by: James / hach1yon <32596618+hach1yon@users.noreply.github.com> Co-authored-by: garigariganzy <tosada31@hotmail.co.jp> Co-authored-by: itiB <is0312vx@ed.ritsumei.ac.jp>
This commit is contained in:
@@ -28,15 +28,15 @@ pub struct AggregationParseInfo {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AggregationConditionToken {
|
||||
COUNT(String), // count
|
||||
SPACE, // 空白
|
||||
Count(String), // count
|
||||
Space, // 空白
|
||||
BY, // by
|
||||
EQ, // ..と等しい
|
||||
LE, // ..以下
|
||||
LT, // ..未満
|
||||
GE, // ..以上
|
||||
GT, // .よりおおきい
|
||||
KEYWORD(String), // BYのフィールド名
|
||||
Keyword(String), // BYのフィールド名
|
||||
}
|
||||
|
||||
/// SIGMAルールでいうAggregationConditionを解析する。
|
||||
@@ -52,12 +52,12 @@ impl AggegationConditionCompiler {
|
||||
pub fn compile(&self, condition_str: String) -> Result<Option<AggregationParseInfo>, String> {
|
||||
let result = self.compile_body(condition_str);
|
||||
if let Result::Err(msg) = result {
|
||||
return Result::Err(format!(
|
||||
Result::Err(format!(
|
||||
"An aggregation condition parse error has occurred. {}",
|
||||
msg
|
||||
));
|
||||
))
|
||||
} else {
|
||||
return result;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,11 +78,11 @@ impl AggegationConditionCompiler {
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.to_string()
|
||||
.replacen("|", "", 1);
|
||||
.replacen('|', "", 1);
|
||||
|
||||
let tokens = self.tokenize(aggregation_str)?;
|
||||
|
||||
return self.parse(tokens);
|
||||
self.parse(tokens)
|
||||
}
|
||||
|
||||
/// 字句解析します。
|
||||
@@ -90,10 +90,10 @@ impl AggegationConditionCompiler {
|
||||
&self,
|
||||
condition_str: String,
|
||||
) -> Result<Vec<AggregationConditionToken>, String> {
|
||||
let mut cur_condition_str = condition_str.clone();
|
||||
let mut cur_condition_str = condition_str;
|
||||
|
||||
let mut tokens = Vec::new();
|
||||
while cur_condition_str.len() != 0 {
|
||||
while !cur_condition_str.is_empty() {
|
||||
let captured = self::AGGREGATION_REGEXMAP.iter().find_map(|regex| {
|
||||
return regex.captures(cur_condition_str.as_str());
|
||||
});
|
||||
@@ -105,7 +105,7 @@ impl AggegationConditionCompiler {
|
||||
let mached_str = captured.unwrap().get(0).unwrap().as_str();
|
||||
let token = self.to_enum(mached_str.to_string());
|
||||
|
||||
if let AggregationConditionToken::SPACE = token {
|
||||
if let AggregationConditionToken::Space = token {
|
||||
// 空白は特に意味ないので、読み飛ばす。
|
||||
cur_condition_str = cur_condition_str.replacen(mached_str, "", 1);
|
||||
continue;
|
||||
@@ -115,19 +115,19 @@ impl AggegationConditionCompiler {
|
||||
cur_condition_str = cur_condition_str.replacen(mached_str, "", 1);
|
||||
}
|
||||
|
||||
return Result::Ok(tokens);
|
||||
Result::Ok(tokens)
|
||||
}
|
||||
|
||||
/// 比較演算子かどうか判定します。
|
||||
fn is_cmp_op(&self, token: &AggregationConditionToken) -> bool {
|
||||
return match token {
|
||||
AggregationConditionToken::EQ => true,
|
||||
AggregationConditionToken::LE => true,
|
||||
AggregationConditionToken::LT => true,
|
||||
AggregationConditionToken::GE => true,
|
||||
AggregationConditionToken::GT => true,
|
||||
_ => false,
|
||||
};
|
||||
matches!(
|
||||
token,
|
||||
AggregationConditionToken::EQ
|
||||
| AggregationConditionToken::LE
|
||||
| AggregationConditionToken::LT
|
||||
| AggregationConditionToken::GE
|
||||
| AggregationConditionToken::GT
|
||||
)
|
||||
}
|
||||
|
||||
/// 構文解析します。
|
||||
@@ -144,7 +144,7 @@ impl AggegationConditionCompiler {
|
||||
let token = token_ite.next().unwrap();
|
||||
|
||||
let mut count_field_name: Option<String> = Option::None;
|
||||
if let AggregationConditionToken::COUNT(field_name) = token {
|
||||
if let AggregationConditionToken::Count(field_name) = token {
|
||||
if !field_name.is_empty() {
|
||||
count_field_name = Option::Some(field_name);
|
||||
}
|
||||
@@ -173,7 +173,7 @@ impl AggegationConditionCompiler {
|
||||
);
|
||||
}
|
||||
|
||||
if let AggregationConditionToken::KEYWORD(keyword) = after_by.unwrap() {
|
||||
if let AggregationConditionToken::Keyword(keyword) = after_by.unwrap() {
|
||||
by_field_name = Option::Some(keyword);
|
||||
token_ite.next()
|
||||
} else {
|
||||
@@ -200,14 +200,14 @@ impl AggegationConditionCompiler {
|
||||
);
|
||||
}
|
||||
|
||||
let token = token_ite.next().unwrap_or(AggregationConditionToken::SPACE);
|
||||
let cmp_number = if let AggregationConditionToken::KEYWORD(number) = token {
|
||||
let token = token_ite.next().unwrap_or(AggregationConditionToken::Space);
|
||||
let cmp_number = if let AggregationConditionToken::Keyword(number) = token {
|
||||
let number: Result<i64, _> = number.parse();
|
||||
if number.is_err() {
|
||||
if let Ok(num) = number {
|
||||
num
|
||||
} else {
|
||||
// 比較演算子の後に数値が無い。
|
||||
return Result::Err("The compare operator needs a number like '> 3'.".to_string());
|
||||
} else {
|
||||
number.unwrap()
|
||||
}
|
||||
} else {
|
||||
// 比較演算子の後に数値が無い。
|
||||
@@ -224,7 +224,7 @@ impl AggegationConditionCompiler {
|
||||
_cmp_op: cmp_token,
|
||||
_cmp_num: cmp_number,
|
||||
};
|
||||
return Result::Ok(Option::Some(info));
|
||||
Result::Ok(Option::Some(info))
|
||||
}
|
||||
|
||||
/// 文字列をConditionTokenに変換する。
|
||||
@@ -232,25 +232,25 @@ impl AggegationConditionCompiler {
|
||||
if token.starts_with("count(") {
|
||||
let count_field = token
|
||||
.replacen("count(", "", 1)
|
||||
.replacen(")", "", 1)
|
||||
.replace(" ", "");
|
||||
return AggregationConditionToken::COUNT(count_field);
|
||||
.replacen(')', "", 1)
|
||||
.replace(' ', "");
|
||||
AggregationConditionToken::Count(count_field)
|
||||
} else if token == " " {
|
||||
return AggregationConditionToken::SPACE;
|
||||
AggregationConditionToken::Space
|
||||
} else if token == "by" {
|
||||
return AggregationConditionToken::BY;
|
||||
AggregationConditionToken::BY
|
||||
} else if token == "==" {
|
||||
return AggregationConditionToken::EQ;
|
||||
AggregationConditionToken::EQ
|
||||
} else if token == "<=" {
|
||||
return AggregationConditionToken::LE;
|
||||
AggregationConditionToken::LE
|
||||
} else if token == ">=" {
|
||||
return AggregationConditionToken::GE;
|
||||
AggregationConditionToken::GE
|
||||
} else if token == "<" {
|
||||
return AggregationConditionToken::LT;
|
||||
AggregationConditionToken::LT
|
||||
} else if token == ">" {
|
||||
return AggregationConditionToken::GT;
|
||||
AggregationConditionToken::GT
|
||||
} else {
|
||||
return AggregationConditionToken::KEYWORD(token);
|
||||
AggregationConditionToken::Keyword(token)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,9 +266,9 @@ mod tests {
|
||||
// countが無いパターン
|
||||
let compiler = AggegationConditionCompiler::new();
|
||||
let result = compiler.compile("select1 and select2".to_string());
|
||||
assert_eq!(true, result.is_ok());
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
assert_eq!(true, result.is_none());
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -276,43 +276,23 @@ mod tests {
|
||||
// 正常系 countの中身にフィールドが無い 各種演算子を試す
|
||||
let token =
|
||||
check_aggregation_condition_ope("select1 and select2|count() > 32".to_string(), 32);
|
||||
let is_gt = match token {
|
||||
AggregationConditionToken::GT => true,
|
||||
_ => false,
|
||||
};
|
||||
assert_eq!(is_gt, true);
|
||||
assert!(matches!(token, AggregationConditionToken::GT));
|
||||
|
||||
let token =
|
||||
check_aggregation_condition_ope("select1 and select2|count() >= 43".to_string(), 43);
|
||||
let is_gt = match token {
|
||||
AggregationConditionToken::GE => true,
|
||||
_ => false,
|
||||
};
|
||||
assert_eq!(is_gt, true);
|
||||
assert!(matches!(token, AggregationConditionToken::GE));
|
||||
|
||||
let token =
|
||||
check_aggregation_condition_ope("select1 and select2|count() < 59".to_string(), 59);
|
||||
let is_gt = match token {
|
||||
AggregationConditionToken::LT => true,
|
||||
_ => false,
|
||||
};
|
||||
assert_eq!(is_gt, true);
|
||||
assert!(matches!(token, AggregationConditionToken::LT));
|
||||
|
||||
let token =
|
||||
check_aggregation_condition_ope("select1 and select2|count() <= 12".to_string(), 12);
|
||||
let is_gt = match token {
|
||||
AggregationConditionToken::LE => true,
|
||||
_ => false,
|
||||
};
|
||||
assert_eq!(is_gt, true);
|
||||
assert!(matches!(token, AggregationConditionToken::LE));
|
||||
|
||||
let token =
|
||||
check_aggregation_condition_ope("select1 and select2|count() == 28".to_string(), 28);
|
||||
let is_gt = match token {
|
||||
AggregationConditionToken::EQ => true,
|
||||
_ => false,
|
||||
};
|
||||
assert_eq!(is_gt, true);
|
||||
assert!(matches!(token, AggregationConditionToken::EQ));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -320,19 +300,15 @@ mod tests {
|
||||
let compiler = AggegationConditionCompiler::new();
|
||||
let result = compiler.compile("select1 or select2 | count() by iiibbb > 27".to_string());
|
||||
|
||||
assert_eq!(true, result.is_ok());
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
assert_eq!(true, result.is_some());
|
||||
assert!(result.is_some());
|
||||
|
||||
let result = result.unwrap();
|
||||
assert_eq!("iiibbb".to_string(), result._by_field_name.unwrap());
|
||||
assert_eq!(true, result._field_name.is_none());
|
||||
assert!(result._field_name.is_none());
|
||||
assert_eq!(27, result._cmp_num);
|
||||
let is_ok = match result._cmp_op {
|
||||
AggregationConditionToken::GT => true,
|
||||
_ => false,
|
||||
};
|
||||
assert_eq!(true, is_ok);
|
||||
assert!(matches!(result._cmp_op, AggregationConditionToken::GT));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -340,19 +316,15 @@ mod tests {
|
||||
let compiler = AggegationConditionCompiler::new();
|
||||
let result = compiler.compile("select1 or select2 | count( hogehoge ) > 3".to_string());
|
||||
|
||||
assert_eq!(true, result.is_ok());
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
assert_eq!(true, result.is_some());
|
||||
assert!(result.is_some());
|
||||
|
||||
let result = result.unwrap();
|
||||
assert_eq!(true, result._by_field_name.is_none());
|
||||
assert!(result._by_field_name.is_none());
|
||||
assert_eq!("hogehoge", result._field_name.unwrap());
|
||||
assert_eq!(3, result._cmp_num);
|
||||
let is_ok = match result._cmp_op {
|
||||
AggregationConditionToken::GT => true,
|
||||
_ => false,
|
||||
};
|
||||
assert_eq!(true, is_ok);
|
||||
assert!(matches!(result._cmp_op, AggregationConditionToken::GT));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -361,19 +333,15 @@ mod tests {
|
||||
let result =
|
||||
compiler.compile("select1 or select2 | count( hogehoge) by snsn > 3".to_string());
|
||||
|
||||
assert_eq!(true, result.is_ok());
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
assert_eq!(true, result.is_some());
|
||||
assert!(result.is_some());
|
||||
|
||||
let result = result.unwrap();
|
||||
assert_eq!("snsn".to_string(), result._by_field_name.unwrap());
|
||||
assert_eq!("hogehoge", result._field_name.unwrap());
|
||||
assert_eq!(3, result._cmp_num);
|
||||
let is_ok = match result._cmp_op {
|
||||
AggregationConditionToken::GT => true,
|
||||
_ => false,
|
||||
};
|
||||
assert_eq!(true, is_ok);
|
||||
assert!(matches!(result._cmp_op, AggregationConditionToken::GT));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -381,7 +349,7 @@ mod tests {
|
||||
let compiler = AggegationConditionCompiler::new();
|
||||
let result = compiler.compile("select1 or select2 |".to_string());
|
||||
|
||||
assert_eq!(true, result.is_err());
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
"An aggregation condition parse error has occurred. There are no strings after the pipe(|)."
|
||||
.to_string(),
|
||||
@@ -395,7 +363,7 @@ mod tests {
|
||||
let result =
|
||||
compiler.compile("select1 or select2 | count( hogeess ) by ii-i > 33".to_string());
|
||||
|
||||
assert_eq!(true, result.is_err());
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
"An aggregation condition parse error has occurred. An unusable character was found."
|
||||
.to_string(),
|
||||
@@ -410,7 +378,7 @@ mod tests {
|
||||
let result =
|
||||
compiler.compile("select1 or select2 | by count( hogehoge) by snsn > 3".to_string());
|
||||
|
||||
assert_eq!(true, result.is_err());
|
||||
assert!(result.is_err());
|
||||
assert_eq!("An aggregation condition parse error has occurred. The aggregation condition can only use count.".to_string(),result.unwrap_err());
|
||||
}
|
||||
|
||||
@@ -420,7 +388,7 @@ mod tests {
|
||||
let compiler = AggegationConditionCompiler::new();
|
||||
let result = compiler.compile("select1 or select2 | count( hogehoge) 3".to_string());
|
||||
|
||||
assert_eq!(true, result.is_err());
|
||||
assert!(result.is_err());
|
||||
assert_eq!("An aggregation condition parse error has occurred. The count keyword needs a compare operator and number like '> 3'".to_string(),result.unwrap_err());
|
||||
}
|
||||
|
||||
@@ -430,7 +398,7 @@ mod tests {
|
||||
let compiler = AggegationConditionCompiler::new();
|
||||
let result = compiler.compile("select1 or select2 | count( hogehoge) by".to_string());
|
||||
|
||||
assert_eq!(true, result.is_err());
|
||||
assert!(result.is_err());
|
||||
assert_eq!("An aggregation condition parse error has occurred. The by keyword needs a field name like 'by EventID'".to_string(),result.unwrap_err());
|
||||
}
|
||||
|
||||
@@ -441,7 +409,7 @@ mod tests {
|
||||
let result =
|
||||
compiler.compile("select1 or select2 | count( hogehoge ) by hoe >".to_string());
|
||||
|
||||
assert_eq!(true, result.is_err());
|
||||
assert!(result.is_err());
|
||||
assert_eq!("An aggregation condition parse error has occurred. The compare operator needs a number like '> 3'.".to_string(),result.unwrap_err());
|
||||
}
|
||||
|
||||
@@ -452,7 +420,7 @@ mod tests {
|
||||
let result =
|
||||
compiler.compile("select1 or select2 | count( hogehoge ) by hoe > 3 33".to_string());
|
||||
|
||||
assert_eq!(true, result.is_err());
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
"An aggregation condition parse error has occurred. An unnecessary word was found."
|
||||
.to_string(),
|
||||
@@ -464,14 +432,14 @@ mod tests {
|
||||
let compiler = AggegationConditionCompiler::new();
|
||||
let result = compiler.compile(expr);
|
||||
|
||||
assert_eq!(true, result.is_ok());
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
assert_eq!(true, result.is_some());
|
||||
assert!(result.is_some());
|
||||
|
||||
let result = result.unwrap();
|
||||
assert_eq!(true, result._by_field_name.is_none());
|
||||
assert_eq!(true, result._field_name.is_none());
|
||||
assert!(result._by_field_name.is_none());
|
||||
assert!(result._field_name.is_none());
|
||||
assert_eq!(cmp_num, result._cmp_num);
|
||||
return result._cmp_op;
|
||||
result._cmp_op
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ impl IntoIterator for ConditionToken {
|
||||
|
||||
impl ConditionToken {
|
||||
fn replace_subtoken(&self, sub_tokens: Vec<ConditionToken>) -> ConditionToken {
|
||||
return match self {
|
||||
match self {
|
||||
ConditionToken::ParenthesisContainer(_) => {
|
||||
ConditionToken::ParenthesisContainer(sub_tokens)
|
||||
}
|
||||
@@ -74,12 +74,12 @@ impl ConditionToken {
|
||||
ConditionToken::SelectionReference(name) => {
|
||||
ConditionToken::SelectionReference(name.clone())
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub_tokens<'a>(&'a self) -> Vec<ConditionToken> {
|
||||
pub fn sub_tokens(&self) -> Vec<ConditionToken> {
|
||||
// TODO ここでcloneを使わずに実装できるようにしたい。
|
||||
return match self {
|
||||
match self {
|
||||
ConditionToken::ParenthesisContainer(sub_tokens) => sub_tokens.clone(),
|
||||
ConditionToken::AndContainer(sub_tokens) => sub_tokens.clone(),
|
||||
ConditionToken::OrContainer(sub_tokens) => sub_tokens.clone(),
|
||||
@@ -92,14 +92,14 @@ impl ConditionToken {
|
||||
ConditionToken::And => vec![],
|
||||
ConditionToken::Or => vec![],
|
||||
ConditionToken::SelectionReference(_) => vec![],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub_tokens_without_parenthesis<'a>(&'a self) -> Vec<ConditionToken> {
|
||||
return match self {
|
||||
pub fn sub_tokens_without_parenthesis(&self) -> Vec<ConditionToken> {
|
||||
match self {
|
||||
ConditionToken::ParenthesisContainer(_) => vec![],
|
||||
_ => self.sub_tokens(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,8 +119,8 @@ impl ConditionCompiler {
|
||||
) -> Result<Box<dyn SelectionNode>, String> {
|
||||
// パイプはここでは処理しない
|
||||
let captured = self::RE_PIPE.captures(&condition_str);
|
||||
let condition_str = if captured.is_some() {
|
||||
let captured = captured.unwrap().get(0).unwrap().as_str().to_string();
|
||||
let condition_str = if let Some(cap) = captured {
|
||||
let captured = cap.get(0).unwrap().as_str().to_string();
|
||||
condition_str.replacen(&captured, "", 1)
|
||||
} else {
|
||||
condition_str
|
||||
@@ -128,9 +128,9 @@ impl ConditionCompiler {
|
||||
|
||||
let result = self.compile_condition_body(condition_str, name_2_node);
|
||||
if let Result::Err(msg) = result {
|
||||
return Result::Err(format!("A condition parse error has occured. {}", msg));
|
||||
Result::Err(format!("A condition parse error has occured. {}", msg))
|
||||
} else {
|
||||
return result;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ impl ConditionCompiler {
|
||||
|
||||
let parsed = self.parse(tokens)?;
|
||||
|
||||
return self.to_selectnode(parsed, name_2_node);
|
||||
self.to_selectnode(parsed, name_2_node)
|
||||
}
|
||||
|
||||
/// 構文解析を実行する。
|
||||
@@ -161,7 +161,7 @@ impl ConditionCompiler {
|
||||
let token = self.parse_operand_container(tokens)?;
|
||||
|
||||
// 括弧で囲まれている部分を探して、もしあればその部分を再帰的に構文解析します。
|
||||
return self.parse_rest_parenthesis(token);
|
||||
self.parse_rest_parenthesis(token)
|
||||
}
|
||||
|
||||
/// 括弧で囲まれている部分を探して、もしあればその部分を再帰的に構文解析します。
|
||||
@@ -172,7 +172,7 @@ impl ConditionCompiler {
|
||||
}
|
||||
|
||||
let sub_tokens = token.sub_tokens();
|
||||
if sub_tokens.len() == 0 {
|
||||
if sub_tokens.is_empty() {
|
||||
return Result::Ok(token);
|
||||
}
|
||||
|
||||
@@ -181,15 +181,15 @@ impl ConditionCompiler {
|
||||
let new_token = self.parse_rest_parenthesis(sub_token)?;
|
||||
new_sub_tokens.push(new_token);
|
||||
}
|
||||
return Result::Ok(token.replace_subtoken(new_sub_tokens));
|
||||
Result::Ok(token.replace_subtoken(new_sub_tokens))
|
||||
}
|
||||
|
||||
/// 字句解析を行う
|
||||
fn tokenize(&self, condition_str: &String) -> Result<Vec<ConditionToken>, String> {
|
||||
let mut cur_condition_str = condition_str.clone();
|
||||
fn tokenize(&self, condition_str: &str) -> Result<Vec<ConditionToken>, String> {
|
||||
let mut cur_condition_str = condition_str.to_string();
|
||||
|
||||
let mut tokens = Vec::new();
|
||||
while cur_condition_str.len() != 0 {
|
||||
while !cur_condition_str.is_empty() {
|
||||
let captured = self::CONDITION_REGEXMAP.iter().find_map(|regex| {
|
||||
return regex.captures(cur_condition_str.as_str());
|
||||
});
|
||||
@@ -210,25 +210,25 @@ impl ConditionCompiler {
|
||||
cur_condition_str = cur_condition_str.replacen(mached_str, "", 1);
|
||||
}
|
||||
|
||||
return Result::Ok(tokens);
|
||||
Result::Ok(tokens)
|
||||
}
|
||||
|
||||
/// 文字列をConditionTokenに変換する。
|
||||
fn to_enum(&self, token: String) -> ConditionToken {
|
||||
if token == "(" {
|
||||
return ConditionToken::LeftParenthesis;
|
||||
ConditionToken::LeftParenthesis
|
||||
} else if token == ")" {
|
||||
return ConditionToken::RightParenthesis;
|
||||
ConditionToken::RightParenthesis
|
||||
} else if token == " " {
|
||||
return ConditionToken::Space;
|
||||
ConditionToken::Space
|
||||
} else if token == "not" {
|
||||
return ConditionToken::Not;
|
||||
ConditionToken::Not
|
||||
} else if token == "and" {
|
||||
return ConditionToken::And;
|
||||
ConditionToken::And
|
||||
} else if token == "or" {
|
||||
return ConditionToken::Or;
|
||||
ConditionToken::Or
|
||||
} else {
|
||||
return ConditionToken::SelectionReference(token.clone());
|
||||
ConditionToken::SelectionReference(token)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,10 +241,7 @@ impl ConditionCompiler {
|
||||
let mut token_ite = tokens.into_iter();
|
||||
while let Some(token) = token_ite.next() {
|
||||
// まず、左括弧を探す。
|
||||
let is_left = match token {
|
||||
ConditionToken::LeftParenthesis => true,
|
||||
_ => false,
|
||||
};
|
||||
let is_left = matches!(token, ConditionToken::LeftParenthesis);
|
||||
if !is_left {
|
||||
ret.push(token);
|
||||
continue;
|
||||
@@ -254,7 +251,7 @@ impl ConditionCompiler {
|
||||
let mut left_cnt = 1;
|
||||
let mut right_cnt = 0;
|
||||
let mut sub_tokens = vec![];
|
||||
while let Some(token) = token_ite.next() {
|
||||
for token in token_ite.by_ref() {
|
||||
if let ConditionToken::LeftParenthesis = token {
|
||||
left_cnt += 1;
|
||||
} else if let ConditionToken::RightParenthesis = token {
|
||||
@@ -275,22 +272,19 @@ impl ConditionCompiler {
|
||||
}
|
||||
|
||||
// この時点で右括弧が残っている場合は右括弧の数が左括弧よりも多いことを表している。
|
||||
let is_right_left = ret.iter().any(|token| {
|
||||
return match token {
|
||||
ConditionToken::RightParenthesis => true,
|
||||
_ => false,
|
||||
};
|
||||
});
|
||||
let is_right_left = ret
|
||||
.iter()
|
||||
.any(|token| matches!(token, ConditionToken::RightParenthesis));
|
||||
if is_right_left {
|
||||
return Result::Err("'(' was expected but not found.".to_string());
|
||||
}
|
||||
|
||||
return Result::Ok(ret);
|
||||
Result::Ok(ret)
|
||||
}
|
||||
|
||||
/// AND, ORをパースする。
|
||||
fn parse_and_or_operator(&self, tokens: Vec<ConditionToken>) -> Result<ConditionToken, String> {
|
||||
if tokens.len() == 0 {
|
||||
if tokens.is_empty() {
|
||||
// 長さ0は呼び出してはいけない
|
||||
return Result::Err("Unknown error.".to_string());
|
||||
}
|
||||
@@ -339,7 +333,7 @@ impl ConditionCompiler {
|
||||
|
||||
// 次にOrでつながっている部分をまとめる
|
||||
let or_contaienr = ConditionToken::OrContainer(operands);
|
||||
return Result::Ok(or_contaienr);
|
||||
Result::Ok(or_contaienr)
|
||||
}
|
||||
|
||||
/// OperandContainerの中身をパースする。現状はNotをパースするためだけに存在している。
|
||||
@@ -360,7 +354,7 @@ impl ConditionCompiler {
|
||||
}
|
||||
|
||||
// 0はありえないはず
|
||||
if sub_tokens.len() == 0 {
|
||||
if sub_tokens.is_empty() {
|
||||
return Result::Err("Unknown error.".to_string());
|
||||
}
|
||||
|
||||
@@ -380,20 +374,20 @@ impl ConditionCompiler {
|
||||
let second_token = sub_tokens_ite.next().unwrap();
|
||||
if let ConditionToken::Not = first_token {
|
||||
if let ConditionToken::Not = second_token {
|
||||
return Result::Err("Not is continuous.".to_string());
|
||||
Result::Err("Not is continuous.".to_string())
|
||||
} else {
|
||||
let not_container = ConditionToken::NotContainer(vec![second_token]);
|
||||
return Result::Ok(not_container);
|
||||
Result::Ok(not_container)
|
||||
}
|
||||
} else {
|
||||
return Result::Err(
|
||||
Result::Err(
|
||||
"Unknown error. Maybe it is because there are multiple names of selection nodes."
|
||||
.to_string(),
|
||||
);
|
||||
)
|
||||
}
|
||||
} else {
|
||||
let sub_tokens = parent_token.sub_tokens_without_parenthesis();
|
||||
if sub_tokens.len() == 0 {
|
||||
if sub_tokens.is_empty() {
|
||||
return Result::Ok(parent_token);
|
||||
}
|
||||
|
||||
@@ -403,7 +397,7 @@ impl ConditionCompiler {
|
||||
new_sub_tokens.push(new_sub_token);
|
||||
}
|
||||
|
||||
return Result::Ok(parent_token.replace_subtoken(new_sub_tokens));
|
||||
Result::Ok(parent_token.replace_subtoken(new_sub_tokens))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,14 +410,14 @@ impl ConditionCompiler {
|
||||
// RefSelectionNodeに変換
|
||||
if let ConditionToken::SelectionReference(selection_name) = token {
|
||||
let selection_node = name_2_node.get(&selection_name);
|
||||
if selection_node.is_none() {
|
||||
let err_msg = format!("{} is not defined.", selection_name);
|
||||
return Result::Err(err_msg);
|
||||
} else {
|
||||
let selection_node = selection_node.unwrap();
|
||||
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!("{} is not defined.", selection_name);
|
||||
return Result::Err(err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,16 +453,12 @@ impl ConditionCompiler {
|
||||
return Result::Ok(Box::new(select_not_node));
|
||||
}
|
||||
|
||||
return Result::Err("Unknown error".to_string());
|
||||
Result::Err("Unknown error".to_string())
|
||||
}
|
||||
|
||||
/// ConditionTokenがAndまたはOrTokenならばTrue
|
||||
fn is_logical(&self, token: &ConditionToken) -> bool {
|
||||
return match token {
|
||||
ConditionToken::And => true,
|
||||
ConditionToken::Or => true,
|
||||
_ => false,
|
||||
};
|
||||
matches!(token, ConditionToken::And | ConditionToken::Or)
|
||||
}
|
||||
|
||||
/// ConditionToken::OperandContainerに変換できる部分があれば変換する。
|
||||
@@ -478,8 +468,7 @@ impl ConditionCompiler {
|
||||
) -> Result<Vec<ConditionToken>, String> {
|
||||
let mut ret = vec![];
|
||||
let mut grouped_operands = vec![]; // ANDとORの間にあるトークンを表す。ANDとORをOperatorとしたときのOperand
|
||||
let mut token_ite = tokens.into_iter();
|
||||
while let Some(token) = token_ite.next() {
|
||||
for token in tokens.into_iter() {
|
||||
if self.is_logical(&token) {
|
||||
// ここに来るのはエラーのはずだが、後でエラー出力するので、ここではエラー出さない。
|
||||
if grouped_operands.is_empty() {
|
||||
@@ -498,7 +487,7 @@ impl ConditionCompiler {
|
||||
ret.push(ConditionToken::OperandContainer(grouped_operands));
|
||||
}
|
||||
|
||||
return Result::Ok(ret);
|
||||
Result::Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,7 +531,7 @@ mod tests {
|
||||
assert_eq!(rule_node.select(&recinfo), expect_select);
|
||||
}
|
||||
Err(_rec) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -582,10 +571,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), true);
|
||||
assert!(rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_rec) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
Err(_) => {
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -626,10 +615,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), false);
|
||||
assert!(!rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_rec) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
Err(_) => {
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+228
-276
File diff suppressed because it is too large
Load Diff
+339
-187
File diff suppressed because it is too large
Load Diff
+89
-91
@@ -21,7 +21,7 @@ use self::count::{AggRecordTimeInfo, TimeFrameInfo};
|
||||
use super::detection::EvtxRecordInfo;
|
||||
|
||||
pub fn create_rule(rulepath: String, yaml: Yaml) -> RuleNode {
|
||||
return RuleNode::new(rulepath, yaml);
|
||||
RuleNode::new(rulepath, yaml)
|
||||
}
|
||||
|
||||
/// Ruleファイルを表すノード
|
||||
@@ -34,7 +34,7 @@ pub struct RuleNode {
|
||||
|
||||
impl Debug for RuleNode {
|
||||
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return Result::Ok(());
|
||||
Result::Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,13 +42,13 @@ unsafe impl Sync for RuleNode {}
|
||||
unsafe impl Send for RuleNode {}
|
||||
|
||||
impl RuleNode {
|
||||
pub fn new(rulepath: String, yaml: Yaml) -> RuleNode {
|
||||
return RuleNode {
|
||||
rulepath: rulepath,
|
||||
yaml: yaml,
|
||||
pub fn new(rule_path: String, yaml_data: Yaml) -> RuleNode {
|
||||
RuleNode {
|
||||
rulepath: rule_path,
|
||||
yaml: yaml_data,
|
||||
detection: DetectionNode::new(),
|
||||
countdata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&mut self) -> Result<(), Vec<String>> {
|
||||
@@ -56,14 +56,14 @@ impl RuleNode {
|
||||
|
||||
// detection node initialization
|
||||
let detection_result = self.detection.init(&self.yaml["detection"]);
|
||||
if detection_result.is_err() {
|
||||
errmsgs.extend(detection_result.unwrap_err());
|
||||
if let Err(err_detail) = detection_result {
|
||||
errmsgs.extend(err_detail);
|
||||
}
|
||||
|
||||
if errmsgs.is_empty() {
|
||||
return Result::Ok(());
|
||||
Result::Ok(())
|
||||
} else {
|
||||
return Result::Err(errmsgs);
|
||||
Result::Err(errmsgs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,11 +72,11 @@ impl RuleNode {
|
||||
if result && self.has_agg_condition() {
|
||||
count::count(self, &event_record.record);
|
||||
}
|
||||
return result;
|
||||
result
|
||||
}
|
||||
/// aggregation conditionが存在するかを返す関数
|
||||
pub fn has_agg_condition(&self) -> bool {
|
||||
return self.detection.aggregation_condition.is_some();
|
||||
self.detection.aggregation_condition.is_some()
|
||||
}
|
||||
/// Aggregation Conditionの結果を配列で返却する関数
|
||||
pub fn judge_satisfy_aggcondition(&self) -> Vec<AggResult> {
|
||||
@@ -84,22 +84,18 @@ impl RuleNode {
|
||||
if !self.has_agg_condition() {
|
||||
return ret;
|
||||
}
|
||||
ret.append(&mut count::aggregation_condition_select(&self));
|
||||
return ret;
|
||||
ret.append(&mut count::aggregation_condition_select(self));
|
||||
ret
|
||||
}
|
||||
pub fn check_exist_countdata(&self) -> bool {
|
||||
self.countdata.len() > 0
|
||||
!self.countdata.is_empty()
|
||||
}
|
||||
/// ルール内のAggregationParseInfo(Aggregation Condition)を取得する関数
|
||||
pub fn get_agg_condition(&self) -> Option<&AggregationParseInfo> {
|
||||
match self.detection.aggregation_condition.as_ref() {
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
Some(agg_parse_info) => {
|
||||
return Some(agg_parse_info);
|
||||
}
|
||||
if self.detection.aggregation_condition.as_ref().is_some() {
|
||||
return self.detection.aggregation_condition.as_ref();
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,22 +106,24 @@ pub fn get_detection_keys(node: &RuleNode) -> Vec<String> {
|
||||
for key in detection.name_to_selection.keys() {
|
||||
let selection = &detection.name_to_selection[key];
|
||||
let desc = selection.get_descendants();
|
||||
let keys = desc.iter().filter_map(|node| {
|
||||
desc.iter().for_each(|node| {
|
||||
if !node.is::<LeafSelectionNode>() {
|
||||
return Option::None;
|
||||
return;
|
||||
}
|
||||
|
||||
let node = node.downcast_ref::<LeafSelectionNode>().unwrap();
|
||||
let key = node.get_key();
|
||||
if key.is_empty() {
|
||||
return Option::None;
|
||||
}
|
||||
return Option::Some(key.to_string());
|
||||
let keys = node.get_keys();
|
||||
let keys = keys.iter().filter_map(|key| {
|
||||
if key.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(key.to_string())
|
||||
});
|
||||
ret.extend(keys);
|
||||
});
|
||||
ret.extend(keys);
|
||||
}
|
||||
|
||||
return ret;
|
||||
ret
|
||||
}
|
||||
|
||||
/// Ruleファイルのdetectionを表すノード
|
||||
@@ -138,12 +136,12 @@ struct DetectionNode {
|
||||
|
||||
impl DetectionNode {
|
||||
fn new() -> DetectionNode {
|
||||
return DetectionNode {
|
||||
DetectionNode {
|
||||
name_to_selection: HashMap::new(),
|
||||
condition: Option::None,
|
||||
aggregation_condition: Option::None,
|
||||
timeframe: Option::None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn init(&mut self, detection_yaml: &Yaml) -> Result<(), Vec<String>> {
|
||||
@@ -169,7 +167,7 @@ impl DetectionNode {
|
||||
]);
|
||||
}
|
||||
|
||||
keys.nth(0).unwrap().to_string()
|
||||
keys.next().unwrap().to_string()
|
||||
};
|
||||
|
||||
// conditionをパースして、SelectionNodeに変換する
|
||||
@@ -193,9 +191,9 @@ impl DetectionNode {
|
||||
}
|
||||
|
||||
if err_msgs.is_empty() {
|
||||
return Result::Ok(());
|
||||
Result::Ok(())
|
||||
} else {
|
||||
return Result::Err(err_msgs);
|
||||
Result::Err(err_msgs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +203,7 @@ impl DetectionNode {
|
||||
}
|
||||
|
||||
let condition = &self.condition.as_ref().unwrap();
|
||||
return condition.select(event_record);
|
||||
condition.select(event_record)
|
||||
}
|
||||
|
||||
/// selectionノードをパースします。
|
||||
@@ -221,7 +219,7 @@ impl DetectionNode {
|
||||
let mut err_msgs = vec![];
|
||||
for key in keys {
|
||||
let name = key.as_str().unwrap_or("");
|
||||
if name.len() == 0 {
|
||||
if name.is_empty() {
|
||||
continue;
|
||||
}
|
||||
// condition等、特殊なキーワードを無視する。
|
||||
@@ -231,11 +229,11 @@ impl DetectionNode {
|
||||
|
||||
// パースして、エラーメッセージがあれば配列にためて、戻り値で返す。
|
||||
let selection_node = self.parse_selection(&detection_hash[key]);
|
||||
if selection_node.is_some() {
|
||||
let mut selection_node = selection_node.unwrap();
|
||||
if let Some(node) = selection_node {
|
||||
let mut selection_node = node;
|
||||
let init_result = selection_node.init();
|
||||
if init_result.is_err() {
|
||||
err_msgs.extend(init_result.unwrap_err());
|
||||
if let Err(err_detail) = init_result {
|
||||
err_msgs.extend(err_detail);
|
||||
} else {
|
||||
let rc_selection = Arc::new(selection_node);
|
||||
self.name_to_selection
|
||||
@@ -248,18 +246,18 @@ impl DetectionNode {
|
||||
}
|
||||
|
||||
// selectionノードが無いのはエラー
|
||||
if self.name_to_selection.len() == 0 {
|
||||
if self.name_to_selection.is_empty() {
|
||||
return Result::Err(vec![
|
||||
"There is no selection node under detection.".to_string()
|
||||
]);
|
||||
}
|
||||
|
||||
return Result::Ok(());
|
||||
Result::Ok(())
|
||||
}
|
||||
|
||||
/// selectionをパースします。
|
||||
fn parse_selection(&self, selection_yaml: &Yaml) -> Option<Box<dyn SelectionNode>> {
|
||||
return Option::Some(self.parse_selection_recursively(vec![], selection_yaml));
|
||||
Option::Some(self.parse_selection_recursively(vec![], selection_yaml))
|
||||
}
|
||||
|
||||
/// selectionをパースします。
|
||||
@@ -280,7 +278,7 @@ impl DetectionNode {
|
||||
let child_node = self.parse_selection_recursively(child_key_list, child_yaml);
|
||||
and_node.child_nodes.push(child_node);
|
||||
});
|
||||
return Box::new(and_node);
|
||||
Box::new(and_node)
|
||||
} else if yaml.as_vec().is_some() {
|
||||
// 配列はOR条件と解釈する。
|
||||
let mut or_node = selectionnodes::OrSelectionNode::new();
|
||||
@@ -289,13 +287,13 @@ impl DetectionNode {
|
||||
or_node.child_nodes.push(child_node);
|
||||
});
|
||||
|
||||
return Box::new(or_node);
|
||||
Box::new(or_node)
|
||||
} else {
|
||||
// 連想配列と配列以外は末端ノード
|
||||
return Box::new(selectionnodes::LeafSelectionNode::new(
|
||||
Box::new(selectionnodes::LeafSelectionNode::new(
|
||||
key_list,
|
||||
yaml.clone(),
|
||||
));
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,19 +315,19 @@ pub struct AggResult {
|
||||
|
||||
impl AggResult {
|
||||
pub fn new(
|
||||
data: i64,
|
||||
key: String,
|
||||
field_values: Vec<String>,
|
||||
start_timedate: DateTime<Utc>,
|
||||
condition_op_num: String,
|
||||
count_data: i64,
|
||||
key_name: String,
|
||||
field_value: Vec<String>,
|
||||
event_start_timedate: DateTime<Utc>,
|
||||
condition_op_number: String,
|
||||
) -> AggResult {
|
||||
return AggResult {
|
||||
data: data,
|
||||
key: key,
|
||||
field_values: field_values,
|
||||
start_timedate: start_timedate,
|
||||
condition_op_num: condition_op_num,
|
||||
};
|
||||
AggResult {
|
||||
data: count_data,
|
||||
key: key_name,
|
||||
field_values: field_value,
|
||||
start_timedate: event_start_timedate,
|
||||
condition_op_num: condition_op_number,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,12 +339,12 @@ mod tests {
|
||||
|
||||
pub fn parse_rule_from_str(rule_str: &str) -> RuleNode {
|
||||
let rule_yaml = YamlLoader::load_from_str(rule_str);
|
||||
assert_eq!(rule_yaml.is_ok(), true);
|
||||
assert!(rule_yaml.is_ok());
|
||||
let rule_yamls = rule_yaml.unwrap();
|
||||
let mut rule_yaml = rule_yamls.into_iter();
|
||||
let mut rule_node = create_rule("testpath".to_string(), rule_yaml.next().unwrap());
|
||||
assert_eq!(rule_node.init().is_ok(), true);
|
||||
return rule_node;
|
||||
assert!(rule_node.init().is_ok());
|
||||
rule_node
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -371,10 +369,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), true);
|
||||
assert!(rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -401,10 +399,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), false);
|
||||
assert!(!rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -431,10 +429,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), false);
|
||||
assert!(!rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -514,10 +512,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), true);
|
||||
assert!(rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -573,10 +571,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), false);
|
||||
assert!(!rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -639,10 +637,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), true);
|
||||
assert!(rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -683,10 +681,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), true);
|
||||
assert!(rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -728,10 +726,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), false);
|
||||
assert!(!rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -792,10 +790,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), true);
|
||||
assert!(rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -856,10 +854,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), false);
|
||||
assert!(!rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -902,10 +900,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), true);
|
||||
assert!(rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_rec) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -961,15 +959,15 @@ mod tests {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
let result = rule_node.select(&recinfo);
|
||||
assert_eq!(rule_node.detection.aggregation_condition.is_some(), true);
|
||||
assert_eq!(result, true);
|
||||
assert!(rule_node.detection.aggregation_condition.is_some());
|
||||
assert!(result);
|
||||
assert_eq!(
|
||||
*&rule_node.countdata.get(key).unwrap().len() as i32,
|
||||
rule_node.countdata.get(key).unwrap().len() as i32,
|
||||
expect_count
|
||||
);
|
||||
}
|
||||
Err(_rec) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use crate::detections::{detection::EvtxRecordInfo, utils};
|
||||
use crate::filter::FILTER_REGEX;
|
||||
use mopa::mopafy;
|
||||
use downcast_rs::Downcast;
|
||||
use std::{sync::Arc, vec};
|
||||
use yaml_rust::Yaml;
|
||||
|
||||
use super::matchers;
|
||||
use super::matchers::{self, DefaultMatcher};
|
||||
|
||||
// Ruleファイルの detection- selection配下のノードはこのtraitを実装する。
|
||||
pub trait SelectionNode: mopa::Any {
|
||||
pub trait SelectionNode: Downcast {
|
||||
// 引数で指定されるイベントログのレコードが、条件に一致するかどうかを判定する
|
||||
// このトレイトを実装する構造体毎に適切な判定処理を書く必要がある。
|
||||
fn select(&self, event_record: &EvtxRecordInfo) -> bool;
|
||||
@@ -19,12 +18,12 @@ pub trait SelectionNode: mopa::Any {
|
||||
fn init(&mut self) -> Result<(), Vec<String>>;
|
||||
|
||||
// 子ノードを取得する(グラフ理論のchildと同じ意味)
|
||||
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>>;
|
||||
fn get_childs(&self) -> Vec<&dyn SelectionNode>;
|
||||
|
||||
// 子孫ノードを取得する(グラフ理論のdescendantと同じ意味)
|
||||
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>>;
|
||||
fn get_descendants(&self) -> Vec<&dyn SelectionNode>;
|
||||
}
|
||||
mopafy!(SelectionNode);
|
||||
downcast_rs::impl_downcast!(SelectionNode);
|
||||
|
||||
/// detection - selection配下でAND条件を表すノード
|
||||
pub struct AndSelectionNode {
|
||||
@@ -33,17 +32,17 @@ pub struct AndSelectionNode {
|
||||
|
||||
impl AndSelectionNode {
|
||||
pub fn new() -> AndSelectionNode {
|
||||
return AndSelectionNode {
|
||||
AndSelectionNode {
|
||||
child_nodes: vec![],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectionNode for AndSelectionNode {
|
||||
fn select(&self, event_record: &EvtxRecordInfo) -> bool {
|
||||
return self.child_nodes.iter().all(|child_node| {
|
||||
return child_node.select(event_record);
|
||||
});
|
||||
self.child_nodes
|
||||
.iter()
|
||||
.all(|child_node| child_node.select(event_record))
|
||||
}
|
||||
|
||||
fn init(&mut self) -> Result<(), Vec<String>> {
|
||||
@@ -52,50 +51,47 @@ impl SelectionNode for AndSelectionNode {
|
||||
.iter_mut()
|
||||
.map(|child_node| {
|
||||
let res = child_node.init();
|
||||
if res.is_err() {
|
||||
return res.unwrap_err();
|
||||
if let Err(err) = res {
|
||||
err
|
||||
} else {
|
||||
return vec![];
|
||||
vec![]
|
||||
}
|
||||
})
|
||||
.fold(
|
||||
vec![],
|
||||
|mut acc: Vec<String>, cur: Vec<String>| -> Vec<String> {
|
||||
acc.extend(cur.into_iter());
|
||||
return acc;
|
||||
acc
|
||||
},
|
||||
);
|
||||
|
||||
if err_msgs.is_empty() {
|
||||
return Result::Ok(());
|
||||
Result::Ok(())
|
||||
} else {
|
||||
return Result::Err(err_msgs);
|
||||
Result::Err(err_msgs)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>> {
|
||||
fn get_childs(&self) -> Vec<&dyn SelectionNode> {
|
||||
let mut ret = vec![];
|
||||
self.child_nodes.iter().for_each(|child_node| {
|
||||
ret.push(child_node);
|
||||
ret.push(child_node.as_ref());
|
||||
});
|
||||
|
||||
return ret;
|
||||
ret
|
||||
}
|
||||
|
||||
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>> {
|
||||
fn get_descendants(&self) -> Vec<&dyn SelectionNode> {
|
||||
let mut ret = self.get_childs();
|
||||
|
||||
self.child_nodes
|
||||
.iter()
|
||||
.map(|child_node| {
|
||||
return child_node.get_descendants();
|
||||
})
|
||||
.flatten()
|
||||
.flat_map(|child_node| child_node.get_descendants())
|
||||
.for_each(|descendant_node| {
|
||||
ret.push(descendant_node);
|
||||
});
|
||||
|
||||
return ret;
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,17 +102,17 @@ pub struct OrSelectionNode {
|
||||
|
||||
impl OrSelectionNode {
|
||||
pub fn new() -> OrSelectionNode {
|
||||
return OrSelectionNode {
|
||||
OrSelectionNode {
|
||||
child_nodes: vec![],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectionNode for OrSelectionNode {
|
||||
fn select(&self, event_record: &EvtxRecordInfo) -> bool {
|
||||
return self.child_nodes.iter().any(|child_node| {
|
||||
return child_node.select(event_record);
|
||||
});
|
||||
self.child_nodes
|
||||
.iter()
|
||||
.any(|child_node| child_node.select(event_record))
|
||||
}
|
||||
|
||||
fn init(&mut self) -> Result<(), Vec<String>> {
|
||||
@@ -125,50 +121,47 @@ impl SelectionNode for OrSelectionNode {
|
||||
.iter_mut()
|
||||
.map(|child_node| {
|
||||
let res = child_node.init();
|
||||
if res.is_err() {
|
||||
return res.unwrap_err();
|
||||
if let Err(err) = res {
|
||||
err
|
||||
} else {
|
||||
return vec![];
|
||||
vec![]
|
||||
}
|
||||
})
|
||||
.fold(
|
||||
vec![],
|
||||
|mut acc: Vec<String>, cur: Vec<String>| -> Vec<String> {
|
||||
acc.extend(cur.into_iter());
|
||||
return acc;
|
||||
acc
|
||||
},
|
||||
);
|
||||
|
||||
if err_msgs.is_empty() {
|
||||
return Result::Ok(());
|
||||
Result::Ok(())
|
||||
} else {
|
||||
return Result::Err(err_msgs);
|
||||
Result::Err(err_msgs)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>> {
|
||||
fn get_childs(&self) -> Vec<&dyn SelectionNode> {
|
||||
let mut ret = vec![];
|
||||
self.child_nodes.iter().for_each(|child_node| {
|
||||
ret.push(child_node);
|
||||
ret.push(child_node.as_ref());
|
||||
});
|
||||
|
||||
return ret;
|
||||
ret
|
||||
}
|
||||
|
||||
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>> {
|
||||
fn get_descendants(&self) -> Vec<&dyn SelectionNode> {
|
||||
let mut ret = self.get_childs();
|
||||
|
||||
self.child_nodes
|
||||
.iter()
|
||||
.map(|child_node| {
|
||||
return child_node.get_descendants();
|
||||
})
|
||||
.flatten()
|
||||
.flat_map(|child_node| child_node.get_descendants())
|
||||
.for_each(|descendant_node| {
|
||||
ret.push(descendant_node);
|
||||
});
|
||||
|
||||
return ret;
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,26 +171,26 @@ pub struct NotSelectionNode {
|
||||
}
|
||||
|
||||
impl NotSelectionNode {
|
||||
pub fn new(node: Box<dyn SelectionNode>) -> NotSelectionNode {
|
||||
return NotSelectionNode { node: node };
|
||||
pub fn new(select_node: Box<dyn SelectionNode>) -> NotSelectionNode {
|
||||
NotSelectionNode { node: select_node }
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectionNode for NotSelectionNode {
|
||||
fn select(&self, event_record: &EvtxRecordInfo) -> bool {
|
||||
return !self.node.select(event_record);
|
||||
!self.node.select(event_record)
|
||||
}
|
||||
|
||||
fn init(&mut self) -> Result<(), Vec<String>> {
|
||||
return Result::Ok(());
|
||||
Result::Ok(())
|
||||
}
|
||||
|
||||
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>> {
|
||||
return vec![];
|
||||
fn get_childs(&self) -> Vec<&dyn SelectionNode> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>> {
|
||||
return self.get_childs();
|
||||
fn get_descendants(&self) -> Vec<&dyn SelectionNode> {
|
||||
self.get_childs()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,28 +203,28 @@ pub struct RefSelectionNode {
|
||||
}
|
||||
|
||||
impl RefSelectionNode {
|
||||
pub fn new(selection_node: Arc<Box<dyn SelectionNode>>) -> RefSelectionNode {
|
||||
return RefSelectionNode {
|
||||
selection_node: selection_node,
|
||||
};
|
||||
pub fn new(select_node: Arc<Box<dyn SelectionNode>>) -> RefSelectionNode {
|
||||
RefSelectionNode {
|
||||
selection_node: select_node,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectionNode for RefSelectionNode {
|
||||
fn select(&self, event_record: &EvtxRecordInfo) -> bool {
|
||||
return self.selection_node.select(event_record);
|
||||
self.selection_node.select(event_record)
|
||||
}
|
||||
|
||||
fn init(&mut self) -> Result<(), Vec<String>> {
|
||||
return Result::Ok(());
|
||||
Result::Ok(())
|
||||
}
|
||||
|
||||
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>> {
|
||||
return vec![&self.selection_node];
|
||||
fn get_childs(&self) -> Vec<&dyn SelectionNode> {
|
||||
vec![self.selection_node.as_ref().as_ref()]
|
||||
}
|
||||
|
||||
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>> {
|
||||
return self.get_childs();
|
||||
fn get_descendants(&self) -> Vec<&dyn SelectionNode> {
|
||||
self.get_childs()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,17 +237,35 @@ pub struct LeafSelectionNode {
|
||||
}
|
||||
|
||||
impl LeafSelectionNode {
|
||||
pub fn new(key_list: Vec<String>, value_yaml: Yaml) -> LeafSelectionNode {
|
||||
return LeafSelectionNode {
|
||||
pub fn new(keys: Vec<String>, value_yaml: Yaml) -> LeafSelectionNode {
|
||||
LeafSelectionNode {
|
||||
key: String::default(),
|
||||
key_list: key_list,
|
||||
key_list: keys,
|
||||
select_value: value_yaml,
|
||||
matcher: Option::None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_key(&self) -> &String {
|
||||
return &self.key;
|
||||
&self.key
|
||||
}
|
||||
|
||||
pub fn get_keys(&self) -> Vec<&String> {
|
||||
let mut keys = vec![];
|
||||
if !self.key.is_empty() {
|
||||
keys.push(&self.key);
|
||||
}
|
||||
|
||||
if let Some(matcher) = &self.matcher {
|
||||
let matcher = matcher.downcast_ref::<DefaultMatcher>();
|
||||
if let Some(matcher) = matcher {
|
||||
if let Some(eq_key) = matcher.get_eqfield_key() {
|
||||
keys.push(eq_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keys
|
||||
}
|
||||
|
||||
fn _create_key(&self) -> String {
|
||||
@@ -263,8 +274,8 @@ impl LeafSelectionNode {
|
||||
}
|
||||
|
||||
let topkey = self.key_list[0].to_string();
|
||||
let values: Vec<&str> = topkey.split("|").collect();
|
||||
return values[0].to_string();
|
||||
let values: Vec<&str> = topkey.split('|').collect();
|
||||
values[0].to_string()
|
||||
}
|
||||
|
||||
/// JSON形式のEventJSONから値を取得する関数 aliasも考慮されている。
|
||||
@@ -274,18 +285,18 @@ impl LeafSelectionNode {
|
||||
return Option::Some(&record.data_string);
|
||||
}
|
||||
|
||||
return record.get_value(self.get_key());
|
||||
record.get_value(self.get_key())
|
||||
}
|
||||
|
||||
/// matchers::LeafMatcherの一覧を取得する。
|
||||
/// 上から順番に調べて、一番始めに一致したMatcherが適用される
|
||||
fn get_matchers(&self) -> Vec<Box<dyn matchers::LeafMatcher>> {
|
||||
return vec![
|
||||
vec![
|
||||
Box::new(matchers::MinlengthMatcher::new()),
|
||||
Box::new(matchers::RegexesFileMatcher::new()),
|
||||
Box::new(matchers::AllowlistFileMatcher::new()),
|
||||
Box::new(matchers::DefaultMatcher::new()),
|
||||
];
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,12 +326,8 @@ impl SelectionNode for LeafSelectionNode {
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
let filter_rule = FILTER_REGEX.get(self.get_key());
|
||||
|
||||
if self.get_key() == "EventData" {
|
||||
let values =
|
||||
utils::get_event_value(&"Event.EventData.Data".to_string(), &event_record.record);
|
||||
let values = utils::get_event_value("Event.EventData.Data", &event_record.record);
|
||||
if values.is_none() {
|
||||
return self
|
||||
.matcher
|
||||
@@ -333,15 +340,12 @@ impl SelectionNode for LeafSelectionNode {
|
||||
let eventdata_data = values.unwrap();
|
||||
if eventdata_data.is_boolean() || eventdata_data.is_i64() || eventdata_data.is_string()
|
||||
{
|
||||
let replaced_str = utils::replace_target_character(
|
||||
event_record.get_value(self.get_key()),
|
||||
filter_rule,
|
||||
);
|
||||
let event_value = event_record.get_value(self.get_key());
|
||||
return self
|
||||
.matcher
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.is_match(replaced_str.as_ref(), event_record);
|
||||
.is_match(event_value, event_record);
|
||||
}
|
||||
// 配列の場合は配列の要素のどれか一つでもルールに合致すれば条件に一致したことにする。
|
||||
if eventdata_data.is_array() {
|
||||
@@ -350,15 +354,12 @@ impl SelectionNode for LeafSelectionNode {
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|ary_element| {
|
||||
let replaced_str = utils::replace_target_character(
|
||||
utils::value_to_string(ary_element).as_ref(),
|
||||
filter_rule,
|
||||
);
|
||||
let event_value = utils::value_to_string(ary_element);
|
||||
return self
|
||||
.matcher
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.is_match(replaced_str.as_ref(), event_record);
|
||||
.is_match(event_value.as_ref(), event_record);
|
||||
});
|
||||
} else {
|
||||
return self
|
||||
@@ -369,14 +370,12 @@ impl SelectionNode for LeafSelectionNode {
|
||||
}
|
||||
}
|
||||
|
||||
let replaced_str =
|
||||
utils::replace_target_character(self.get_event_value(&event_record), filter_rule);
|
||||
|
||||
let event_value = self.get_event_value(event_record);
|
||||
return self
|
||||
.matcher
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.is_match(replaced_str.as_ref(), event_record);
|
||||
.is_match(event_value, event_record);
|
||||
}
|
||||
|
||||
fn init(&mut self) -> Result<(), Vec<String>> {
|
||||
@@ -409,12 +408,12 @@ impl SelectionNode for LeafSelectionNode {
|
||||
.init(&match_key_list, &self.select_value);
|
||||
}
|
||||
|
||||
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>> {
|
||||
return vec![];
|
||||
fn get_childs(&self) -> Vec<&dyn SelectionNode> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>> {
|
||||
return vec![];
|
||||
fn get_descendants(&self) -> Vec<&dyn SelectionNode> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,10 +444,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), true);
|
||||
assert!(rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -478,10 +477,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), false);
|
||||
assert!(!rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -510,10 +509,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), true);
|
||||
assert!(rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -542,10 +541,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), true);
|
||||
assert!(rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -574,10 +573,10 @@ mod tests {
|
||||
Ok(record) => {
|
||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||
assert_eq!(rule_node.select(&recinfo), false);
|
||||
assert!(!rule_node.select(&recinfo));
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(false, "Failed to parse json record.");
|
||||
panic!("Failed to parse json record.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user