Feature/str exact match#79 (#100)
* Add: LeafMatch struct 'StartsWithMatcher' * Add: LeafMatch struct 'EndsWithMatcher' * Add: LeafMatch struct 'ContainsMatcher' * WIP: StrFeature * Add: get strFeature's from rule file * refact * Revert "refact" This reverts commit 5439b4d6d52dff4a90307206404e38ff20fe792e. Revert "Add: get strFeature's from rule file" This reverts commit 152ad10a03f67f23e6de3db93be9b4e7eaf6a7a8. Revert "WIP: StrFeature" This reverts commit bf7271348d30b24f6063cfe8ce5b27e956143d60. * Add: matcher selector for string options * Add: rm |xxx text from rule file * fix: leafNodes's |xxx command overwritten * test: startswith, endswith, contains LeafNodeTest * test: use string MethodNode in OrSelectionNode * cargo fmt --all * Update: when undefined rule option occur, raise err
This commit is contained in:
@@ -372,6 +372,7 @@ impl LeafSelectionNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LeafMatcherの一覧を取得する。
|
// LeafMatcherの一覧を取得する。
|
||||||
|
// 上から順番に調べて、一番始めに一致したMatcherが適用される
|
||||||
fn get_matchers(&self) -> Vec<Box<dyn LeafMatcher>> {
|
fn get_matchers(&self) -> Vec<Box<dyn LeafMatcher>> {
|
||||||
return vec![
|
return vec![
|
||||||
Box::new(RegexMatcher::new()),
|
Box::new(RegexMatcher::new()),
|
||||||
@@ -416,6 +417,7 @@ impl SelectionNode for LeafSelectionNode {
|
|||||||
|
|
||||||
// 配列じゃなくて、文字列や数値等の場合は普通通りに比較する。
|
// 配列じゃなくて、文字列や数値等の場合は普通通りに比較する。
|
||||||
let eventdata_data = values.unwrap();
|
let eventdata_data = values.unwrap();
|
||||||
|
|
||||||
if eventdata_data.is_boolean() || eventdata_data.is_i64() || eventdata_data.is_string()
|
if eventdata_data.is_boolean() || eventdata_data.is_i64() || eventdata_data.is_string()
|
||||||
{
|
{
|
||||||
return self
|
return self
|
||||||
@@ -424,7 +426,6 @@ impl SelectionNode for LeafSelectionNode {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.is_match(Option::Some(eventdata_data));
|
.is_match(Option::Some(eventdata_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 配列の場合は配列の要素のどれか一つでもルールに合致すれば条件に一致したことにする。
|
// 配列の場合は配列の要素のどれか一つでもルールに合致すれば条件に一致したことにする。
|
||||||
if eventdata_data.is_array() {
|
if eventdata_data.is_array() {
|
||||||
return eventdata_data
|
return eventdata_data
|
||||||
@@ -448,12 +449,36 @@ impl SelectionNode for LeafSelectionNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self) -> Result<(), Vec<String>> {
|
fn init(&mut self) -> Result<(), Vec<String>> {
|
||||||
let matchers = self.get_matchers();
|
let mut fixed_key_list = Vec::new(); // |xx を排除したkey_listを作成する
|
||||||
|
for key in &self.key_list {
|
||||||
|
if key.contains('|') {
|
||||||
|
let v: Vec<&str> = key.split('|').collect();
|
||||||
|
self.matcher = match v[1] {
|
||||||
|
"startswith" => Some(Box::new(StartsWithMatcher::new())),
|
||||||
|
"endswith" => Some(Box::new(EndsWithMatcher::new())),
|
||||||
|
"contains" => Some(Box::new(ContainsMatcher::new())),
|
||||||
|
_ => {
|
||||||
|
return Result::Err(vec![format!(
|
||||||
|
"Found unknown key option. option: {}",
|
||||||
|
v[1]
|
||||||
|
)])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fixed_key_list.push(v[0].to_string());
|
||||||
|
} else {
|
||||||
|
fixed_key_list.push(key.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.key_list = fixed_key_list;
|
||||||
let mut match_key_list = self.key_list.clone();
|
let mut match_key_list = self.key_list.clone();
|
||||||
match_key_list.remove(0);
|
match_key_list.remove(0);
|
||||||
self.matcher = matchers
|
if self.matcher.is_none() {
|
||||||
.into_iter()
|
let matchers = self.get_matchers();
|
||||||
.find(|matcher| matcher.is_target_key(&match_key_list));
|
self.matcher = matchers
|
||||||
|
.into_iter()
|
||||||
|
.find(|matcher| matcher.is_target_key(&match_key_list));
|
||||||
|
}
|
||||||
|
|
||||||
// 一致するmatcherが見つからないエラー
|
// 一致するmatcherが見つからないエラー
|
||||||
if self.matcher.is_none() {
|
if self.matcher.is_none() {
|
||||||
return Result::Err(vec![format!(
|
return Result::Err(vec![format!(
|
||||||
@@ -758,6 +783,168 @@ impl LeafMatcher for WhitelistFileMatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 指定された文字列で始まるか調べるクラス
|
||||||
|
struct StartsWithMatcher {
|
||||||
|
start_text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StartsWithMatcher {
|
||||||
|
fn new() -> StartsWithMatcher {
|
||||||
|
return StartsWithMatcher {
|
||||||
|
start_text: String::from(""),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LeafMatcher for StartsWithMatcher {
|
||||||
|
fn is_target_key(&self, _: &Vec<String>) -> bool {
|
||||||
|
// ContextInfo|startswith のような場合にLeafをStartsWithMatcherにする。
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>> {
|
||||||
|
if select_value.is_null() {
|
||||||
|
return Result::Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringに変換
|
||||||
|
let yaml_value = match select_value {
|
||||||
|
Yaml::Boolean(b) => Option::Some(b.to_string()),
|
||||||
|
Yaml::Integer(i) => Option::Some(i.to_string()),
|
||||||
|
Yaml::Real(r) => Option::Some(r.to_string()),
|
||||||
|
Yaml::String(s) => Option::Some(s.to_owned()),
|
||||||
|
_ => Option::None,
|
||||||
|
};
|
||||||
|
if yaml_value.is_none() {
|
||||||
|
let errmsg = format!(
|
||||||
|
"unknown error occured. [key:{}]",
|
||||||
|
concat_selection_key(key_list)
|
||||||
|
);
|
||||||
|
return Result::Err(vec![errmsg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.start_text = yaml_value.unwrap();
|
||||||
|
return Result::Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_match(&self, event_value: Option<&Value>) -> bool {
|
||||||
|
// 調査する文字列がself.start_textで始まるならtrueを返す
|
||||||
|
return match event_value.unwrap_or(&Value::Null) {
|
||||||
|
Value::String(s) => s.starts_with(&self.start_text),
|
||||||
|
Value::Number(n) => n.to_string().starts_with(&self.start_text),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 指定された文字列で終わるか調べるクラス
|
||||||
|
struct EndsWithMatcher {
|
||||||
|
end_text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EndsWithMatcher {
|
||||||
|
fn new() -> EndsWithMatcher {
|
||||||
|
return EndsWithMatcher {
|
||||||
|
end_text: String::from(""),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LeafMatcher for EndsWithMatcher {
|
||||||
|
fn is_target_key(&self, _: &Vec<String>) -> bool {
|
||||||
|
// ContextInfo|endswith のような場合にLeafをEndsWithMatcherにする。
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>> {
|
||||||
|
if select_value.is_null() {
|
||||||
|
return Result::Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringに変換
|
||||||
|
let yaml_value = match select_value {
|
||||||
|
Yaml::Boolean(b) => Option::Some(b.to_string()),
|
||||||
|
Yaml::Integer(i) => Option::Some(i.to_string()),
|
||||||
|
Yaml::Real(r) => Option::Some(r.to_string()),
|
||||||
|
Yaml::String(s) => Option::Some(s.to_owned()),
|
||||||
|
_ => Option::None,
|
||||||
|
};
|
||||||
|
if yaml_value.is_none() {
|
||||||
|
let errmsg = format!(
|
||||||
|
"unknown error occured. [key:{}]",
|
||||||
|
concat_selection_key(key_list)
|
||||||
|
);
|
||||||
|
return Result::Err(vec![errmsg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.end_text = yaml_value.unwrap();
|
||||||
|
return Result::Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_match(&self, event_value: Option<&Value>) -> bool {
|
||||||
|
// 調査する文字列がself.end_textで終わるならtrueを返す
|
||||||
|
return match event_value.unwrap_or(&Value::Null) {
|
||||||
|
Value::String(s) => s.ends_with(&self.end_text),
|
||||||
|
Value::Number(n) => n.to_string().ends_with(&self.end_text),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 指定された文字列が含まれるか調べるクラス
|
||||||
|
struct ContainsMatcher {
|
||||||
|
pattern: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContainsMatcher {
|
||||||
|
fn new() -> ContainsMatcher {
|
||||||
|
return ContainsMatcher {
|
||||||
|
pattern: String::from(""),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LeafMatcher for ContainsMatcher {
|
||||||
|
fn is_target_key(&self, _: &Vec<String>) -> bool {
|
||||||
|
// ContextInfo|contains のような場合にLeafをContainsMatcherにする。
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>> {
|
||||||
|
if select_value.is_null() {
|
||||||
|
return Result::Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringに変換
|
||||||
|
let yaml_value = match select_value {
|
||||||
|
Yaml::Boolean(b) => Option::Some(b.to_string()),
|
||||||
|
Yaml::Integer(i) => Option::Some(i.to_string()),
|
||||||
|
Yaml::Real(r) => Option::Some(r.to_string()),
|
||||||
|
Yaml::String(s) => Option::Some(s.to_owned()),
|
||||||
|
_ => Option::None,
|
||||||
|
};
|
||||||
|
if yaml_value.is_none() {
|
||||||
|
let errmsg = format!(
|
||||||
|
"unknown error occured. [key:{}]",
|
||||||
|
concat_selection_key(key_list)
|
||||||
|
);
|
||||||
|
return Result::Err(vec![errmsg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pattern = yaml_value.unwrap();
|
||||||
|
return Result::Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_match(&self, event_value: Option<&Value>) -> bool {
|
||||||
|
// 調査する文字列にself.patternが含まれるならtrueを返す
|
||||||
|
return match event_value.unwrap_or(&Value::Null) {
|
||||||
|
Value::String(s) => s.contains(&self.pattern),
|
||||||
|
Value::Number(n) => n.to_string().contains(&self.pattern),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use yaml_rust::YamlLoader;
|
use yaml_rust::YamlLoader;
|
||||||
@@ -2208,4 +2395,322 @@ mod tests {
|
|||||||
assert_eq!(rule_node.init().is_ok(), true);
|
assert_eq!(rule_node.init().is_ok(), true);
|
||||||
return rule_node;
|
return rule_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_detect_startswith1() {
|
||||||
|
// startswithが正しく検知できることを確認
|
||||||
|
let rule_str = r#"
|
||||||
|
enabled: true
|
||||||
|
detection:
|
||||||
|
selection:
|
||||||
|
Channel: Security
|
||||||
|
EventID: 4732
|
||||||
|
TargetUserName|startswith: "Administrators"
|
||||||
|
output: 'user added to local Administrators UserName: %MemberName% SID: %MemberSid%'
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let record_json_str = r#"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"System": {
|
||||||
|
"EventID": 4732,
|
||||||
|
"Channel": "Security"
|
||||||
|
},
|
||||||
|
"EventData": {
|
||||||
|
"TargetUserName": "AdministratorsTest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Event_attributes": {
|
||||||
|
"xmlns": "http://schemas.microsoft.com/win/2004/08/events/event"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let rule_node = parse_rule_from_str(rule_str);
|
||||||
|
let selection_node = rule_node.detection.unwrap().selection.unwrap();
|
||||||
|
|
||||||
|
match serde_json::from_str(record_json_str) {
|
||||||
|
Ok(record) => {
|
||||||
|
assert_eq!(selection_node.select(&record), true);
|
||||||
|
}
|
||||||
|
Err(rec) => {
|
||||||
|
assert!(false, "failed to parse json record.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_detect_startswith2() {
|
||||||
|
// startswithが正しく検知できることを確認
|
||||||
|
let rule_str = r#"
|
||||||
|
enabled: true
|
||||||
|
detection:
|
||||||
|
selection:
|
||||||
|
Channel: Security
|
||||||
|
EventID: 4732
|
||||||
|
TargetUserName|startswith: "Administrators"
|
||||||
|
output: 'user added to local Administrators UserName: %MemberName% SID: %MemberSid%'
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let record_json_str = r#"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"System": {
|
||||||
|
"EventID": 4732,
|
||||||
|
"Channel": "Security"
|
||||||
|
},
|
||||||
|
"EventData": {
|
||||||
|
"TargetUserName": "TestAdministrators"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Event_attributes": {
|
||||||
|
"xmlns": "http://schemas.microsoft.com/win/2004/08/events/event"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let rule_node = parse_rule_from_str(rule_str);
|
||||||
|
let selection_node = rule_node.detection.unwrap().selection.unwrap();
|
||||||
|
|
||||||
|
match serde_json::from_str(record_json_str) {
|
||||||
|
Ok(record) => {
|
||||||
|
assert_eq!(selection_node.select(&record), false);
|
||||||
|
}
|
||||||
|
Err(rec) => {
|
||||||
|
assert!(false, "failed to parse json record.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_detect_endswith1() {
|
||||||
|
// endswithが正しく検知できることを確認
|
||||||
|
let rule_str = r#"
|
||||||
|
enabled: true
|
||||||
|
detection:
|
||||||
|
selection:
|
||||||
|
Channel: Security
|
||||||
|
EventID: 4732
|
||||||
|
TargetUserName|endswith: "Administrators"
|
||||||
|
output: 'user added to local Administrators UserName: %MemberName% SID: %MemberSid%'
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let record_json_str = r#"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"System": {
|
||||||
|
"EventID": 4732,
|
||||||
|
"Channel": "Security"
|
||||||
|
},
|
||||||
|
"EventData": {
|
||||||
|
"TargetUserName": "TestAdministrators"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Event_attributes": {
|
||||||
|
"xmlns": "http://schemas.microsoft.com/win/2004/08/events/event"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let rule_node = parse_rule_from_str(rule_str);
|
||||||
|
let selection_node = rule_node.detection.unwrap().selection.unwrap();
|
||||||
|
|
||||||
|
match serde_json::from_str(record_json_str) {
|
||||||
|
Ok(record) => {
|
||||||
|
assert_eq!(selection_node.select(&record), true);
|
||||||
|
}
|
||||||
|
Err(rec) => {
|
||||||
|
assert!(false, "failed to parse json record.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_detect_endswith2() {
|
||||||
|
// endswithが正しく検知できることを確認
|
||||||
|
let rule_str = r#"
|
||||||
|
enabled: true
|
||||||
|
detection:
|
||||||
|
selection:
|
||||||
|
Channel: Security
|
||||||
|
EventID: 4732
|
||||||
|
TargetUserName|endswith: "Administrators"
|
||||||
|
output: 'user added to local Administrators UserName: %MemberName% SID: %MemberSid%'
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let record_json_str = r#"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"System": {
|
||||||
|
"EventID": 4732,
|
||||||
|
"Channel": "Security"
|
||||||
|
},
|
||||||
|
"EventData": {
|
||||||
|
"TargetUserName": "AdministratorsTest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Event_attributes": {
|
||||||
|
"xmlns": "http://schemas.microsoft.com/win/2004/08/events/event"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let rule_node = parse_rule_from_str(rule_str);
|
||||||
|
let selection_node = rule_node.detection.unwrap().selection.unwrap();
|
||||||
|
|
||||||
|
match serde_json::from_str(record_json_str) {
|
||||||
|
Ok(record) => {
|
||||||
|
assert_eq!(selection_node.select(&record), false);
|
||||||
|
}
|
||||||
|
Err(rec) => {
|
||||||
|
assert!(false, "failed to parse json record.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_detect_contains1() {
|
||||||
|
// containsが正しく検知できることを確認
|
||||||
|
let rule_str = r#"
|
||||||
|
enabled: true
|
||||||
|
detection:
|
||||||
|
selection:
|
||||||
|
Channel: Security
|
||||||
|
EventID: 4732
|
||||||
|
TargetUserName|contains: "Administrators"
|
||||||
|
output: 'user added to local Administrators UserName: %MemberName% SID: %MemberSid%'
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let record_json_str = r#"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"System": {
|
||||||
|
"EventID": 4732,
|
||||||
|
"Channel": "Security"
|
||||||
|
},
|
||||||
|
"EventData": {
|
||||||
|
"TargetUserName": "TestAdministratorsTest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Event_attributes": {
|
||||||
|
"xmlns": "http://schemas.microsoft.com/win/2004/08/events/event"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let rule_node = parse_rule_from_str(rule_str);
|
||||||
|
let selection_node = rule_node.detection.unwrap().selection.unwrap();
|
||||||
|
|
||||||
|
match serde_json::from_str(record_json_str) {
|
||||||
|
Ok(record) => {
|
||||||
|
assert_eq!(selection_node.select(&record), true);
|
||||||
|
}
|
||||||
|
Err(rec) => {
|
||||||
|
assert!(false, "failed to parse json record.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_detect_contains2() {
|
||||||
|
// containsが正しく検知できることを確認
|
||||||
|
let rule_str = r#"
|
||||||
|
enabled: true
|
||||||
|
detection:
|
||||||
|
selection:
|
||||||
|
Channel: Security
|
||||||
|
EventID: 4732
|
||||||
|
TargetUserName|contains: "Administrators"
|
||||||
|
output: 'user added to local Administrators UserName: %MemberName% SID: %MemberSid%'
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let record_json_str = r#"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"System": {
|
||||||
|
"EventID": 4732,
|
||||||
|
"Channel": "Security"
|
||||||
|
},
|
||||||
|
"EventData": {
|
||||||
|
"TargetUserName": "Testministrators"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Event_attributes": {
|
||||||
|
"xmlns": "http://schemas.microsoft.com/win/2004/08/events/event"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let rule_node = parse_rule_from_str(rule_str);
|
||||||
|
let selection_node = rule_node.detection.unwrap().selection.unwrap();
|
||||||
|
|
||||||
|
match serde_json::from_str(record_json_str) {
|
||||||
|
Ok(record) => {
|
||||||
|
assert_eq!(selection_node.select(&record), false);
|
||||||
|
}
|
||||||
|
Err(rec) => {
|
||||||
|
assert!(false, "failed to parse json record.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_use_strfeature_in_or_node() {
|
||||||
|
// orNodeの中でもstartswithが使えるかのテスト
|
||||||
|
let rule_str = r#"
|
||||||
|
enabled: true
|
||||||
|
detection:
|
||||||
|
selection:
|
||||||
|
Channel: 'System'
|
||||||
|
EventID: 7040
|
||||||
|
param1: 'Windows Event Log'
|
||||||
|
param2|startswith:
|
||||||
|
- "disa"
|
||||||
|
- "aut"
|
||||||
|
output: 'Service name : %param1%¥nMessage : Event Log Service Stopped¥nResults: Selective event log manipulation may follow this event.'
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let record_json_str = r#"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"System": {
|
||||||
|
"EventID": 7040,
|
||||||
|
"Channel": "System"
|
||||||
|
},
|
||||||
|
"EventData": {
|
||||||
|
"param1": "Windows Event Log",
|
||||||
|
"param2": "auto start"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Event_attributes": {
|
||||||
|
"xmlns": "http://schemas.microsoft.com/win/2004/08/events/event"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let rule_node = parse_rule_from_str(rule_str);
|
||||||
|
let selection_node = rule_node.detection.unwrap().selection.unwrap();
|
||||||
|
|
||||||
|
match serde_json::from_str(record_json_str) {
|
||||||
|
Ok(record) => {
|
||||||
|
assert_eq!(selection_node.select(&record), true);
|
||||||
|
}
|
||||||
|
Err(rec) => {
|
||||||
|
assert!(false, "failed to parse json record.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_detect_undefined_rule_option() {
|
||||||
|
// 不明な文字列オプションがルールに書かれていたら警告するテスト
|
||||||
|
let rule_str = r#"
|
||||||
|
enabled: true
|
||||||
|
detection:
|
||||||
|
selection:
|
||||||
|
Channel|failed: Security
|
||||||
|
EventID: 0
|
||||||
|
output: 'Rule parse test'
|
||||||
|
"#;
|
||||||
|
let mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter();
|
||||||
|
let mut rule_node = parse_rule(rule_yaml.next().unwrap());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
rule_node.init(),
|
||||||
|
Err(vec!["Found unknown key option. option: failed".to_string()])
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user