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!)
This commit is contained in:
DustInDark
2022-03-07 08:38:05 +09:00
committed by GitHub
parent 631496cf41
commit bb1f5f619d
18 changed files with 1044 additions and 1144 deletions

View File

@@ -49,7 +49,7 @@ pub fn set_output_color() -> Option<HashMap<String, Vec<u8>>> {
// color情報がない場合は通常の白色の出力が出てくるのみで動作への影響を与えない為warnとして処理する // color情報がない場合は通常の白色の出力が出てくるのみで動作への影響を与えない為warnとして処理する
AlertMessage::warn( AlertMessage::warn(
&mut BufWriter::new(std::io::stderr().lock()), &mut BufWriter::new(std::io::stderr().lock()),
&read_result.as_ref().unwrap_err(), read_result.as_ref().unwrap_err(),
) )
.ok(); .ok();
return None; return None;
@@ -71,12 +71,12 @@ pub fn set_output_color() -> Option<HashMap<String, Vec<u8>>> {
return; return;
} }
let color_code = convert_color_result.unwrap(); let color_code = convert_color_result.unwrap();
if level.len() == 0 || color_code.len() < 3 { if level.is_empty() || color_code.len() < 3 {
return; return;
} }
color_map.insert(level.to_string(), color_code); color_map.insert(level.to_string(), color_code);
}); });
return Some(color_map); Some(color_map)
} }
pub fn after_fact() { pub fn after_fact() {
@@ -142,7 +142,7 @@ fn emit_csv<W: std::io::Write>(
if displayflag { if displayflag {
if color_map.is_some() { if color_map.is_some() {
let output_color = let output_color =
_get_output_color(&color_map.as_ref().unwrap(), &detect_info.level); _get_output_color(color_map.as_ref().unwrap(), &detect_info.level);
wtr.serialize(DisplayFormat { wtr.serialize(DisplayFormat {
timestamp: &format!( timestamp: &format!(
"{} ", "{} ",
@@ -227,10 +227,10 @@ fn emit_csv<W: std::io::Write>(
total_detect_counts_by_level[level_suffix] += 1; total_detect_counts_by_level[level_suffix] += 1;
} }
} }
println!(""); println!();
wtr.flush()?; wtr.flush()?;
println!(""); println!();
_print_unique_results( _print_unique_results(
total_detect_counts_by_level, total_detect_counts_by_level,
"Total".to_string(), "Total".to_string(),
@@ -285,7 +285,7 @@ fn _print_unique_results(
output_str = output_raw_str; output_str = output_raw_str;
} else { } else {
let output_color = let output_color =
_get_output_color(&color_map.as_ref().unwrap(), &level_name.to_string()); _get_output_color(color_map.as_ref().unwrap(), &level_name.to_string());
output_str = output_raw_str output_str = output_raw_str
.truecolor(output_color[0], output_color[1], output_color[2]) .truecolor(output_color[0], output_color[1], output_color[2])
.to_string(); .to_string();
@@ -296,14 +296,14 @@ fn _print_unique_results(
} }
/// levelに対応したtruecolorの値の配列を返す関数 /// levelに対応したtruecolorの値の配列を返す関数
fn _get_output_color(color_map: &HashMap<String, Vec<u8>>, level: &String) -> Vec<u8> { fn _get_output_color(color_map: &HashMap<String, Vec<u8>>, level: &str) -> Vec<u8> {
// カラーをつけない場合は255,255,255で出力する // カラーをつけない場合は255,255,255で出力する
let mut output_color: Vec<u8> = vec![255, 255, 255]; let mut output_color: Vec<u8> = vec![255, 255, 255];
let target_color = color_map.get(level); let target_color = color_map.get(level);
if target_color.is_some() { if let Some(color) = target_color {
output_color = target_color.unwrap().to_vec(); output_color = color.to_vec();
} }
return output_color; output_color
} }
fn format_time(time: &DateTime<Utc>) -> String { fn format_time(time: &DateTime<Utc>) -> String {
@@ -319,11 +319,11 @@ where
Tz::Offset: std::fmt::Display, Tz::Offset: std::fmt::Display,
{ {
if configs::CONFIG.read().unwrap().args.is_present("rfc-2822") { if configs::CONFIG.read().unwrap().args.is_present("rfc-2822") {
return time.to_rfc2822(); time.to_rfc2822()
} else if configs::CONFIG.read().unwrap().args.is_present("rfc-3339") { } else if configs::CONFIG.read().unwrap().args.is_present("rfc-3339") {
return time.to_rfc3339(); time.to_rfc3339()
} else { } else {
return time.format("%Y-%m-%d %H:%M:%S%.3f %:z").to_string(); time.format("%Y-%m-%d %H:%M:%S%.3f %:z").to_string()
} }
} }
@@ -331,6 +331,7 @@ where
mod tests { mod tests {
use crate::afterfact::emit_csv; use crate::afterfact::emit_csv;
use crate::detections::print; use crate::detections::print;
use crate::detections::print::DetectInfo;
use chrono::{Local, TimeZone, Utc}; use chrono::{Local, TimeZone, Utc};
use serde_json::Value; use serde_json::Value;
use std::fs::File; use std::fs::File;
@@ -372,15 +373,18 @@ mod tests {
"##; "##;
let event: Value = serde_json::from_str(val).unwrap(); let event: Value = serde_json::from_str(val).unwrap();
messages.insert( messages.insert(
testfilepath.to_string(),
testrulepath.to_string(),
&event, &event,
test_level.to_string(),
test_computername.to_string(),
test_eventid.to_string(),
test_title.to_string(),
output.to_string(), output.to_string(),
test_attack.to_string(), DetectInfo {
filepath: testfilepath.to_string(),
rulepath: testrulepath.to_string(),
level: test_level.to_string(),
computername: test_computername.to_string(),
eventid: test_eventid.to_string(),
alert: test_title.to_string(),
detail: String::default(),
tag_info: test_attack.to_string(),
},
); );
} }
let expect_time = Utc let expect_time = Utc
@@ -452,15 +456,18 @@ mod tests {
"##; "##;
let event: Value = serde_json::from_str(val).unwrap(); let event: Value = serde_json::from_str(val).unwrap();
messages.insert( messages.insert(
testfilepath.to_string(),
testrulepath.to_string(),
&event, &event,
test_level.to_string(),
test_computername.to_string(),
test_eventid.to_string(),
test_title.to_string(),
output.to_string(), output.to_string(),
test_attack.to_string(), DetectInfo {
filepath: testfilepath.to_string(),
rulepath: testrulepath.to_string(),
level: test_level.to_string(),
computername: test_computername.to_string(),
eventid: test_eventid.to_string(),
alert: test_title.to_string(),
detail: String::default(),
tag_info: test_attack.to_string(),
},
); );
messages.debug(); messages.debug();
} }
@@ -520,6 +527,6 @@ mod tests {
let white_color_header = "\u{1b}[38;2;255;255;255m"; let white_color_header = "\u{1b}[38;2;255;255;255m";
let white_color_footer = "\u{1b}[0m"; let white_color_footer = "\u{1b}[0m";
return white_color_header.to_owned() + target + white_color_footer; white_color_header.to_owned() + target + white_color_footer
} }
} }

View File

@@ -16,7 +16,7 @@ lazy_static! {
levelmap.insert("MEDIUM".to_owned(), 3); levelmap.insert("MEDIUM".to_owned(), 3);
levelmap.insert("HIGH".to_owned(), 4); levelmap.insert("HIGH".to_owned(), 4);
levelmap.insert("CRITICAL".to_owned(), 5); levelmap.insert("CRITICAL".to_owned(), 5);
return levelmap; levelmap
}; };
pub static ref EVENTKEY_ALIAS: EventKeyAliasConfig = pub static ref EVENTKEY_ALIAS: EventKeyAliasConfig =
load_eventkey_alias("./rules/config/eventkey_alias.txt"); load_eventkey_alias("./rules/config/eventkey_alias.txt");
@@ -29,6 +29,12 @@ pub struct ConfigReader {
pub target_eventids: TargetEventIds, pub target_eventids: TargetEventIds,
} }
impl Default for ConfigReader {
fn default() -> Self {
Self::new()
}
}
impl ConfigReader { impl ConfigReader {
pub fn new() -> Self { pub fn new() -> Self {
ConfigReader { ConfigReader {
@@ -41,7 +47,7 @@ impl ConfigReader {
fn build_app<'a>() -> ArgMatches<'a> { fn build_app<'a>() -> ArgMatches<'a> {
let program = std::env::args() let program = std::env::args()
.nth(0) .next()
.and_then(|s| { .and_then(|s| {
std::path::PathBuf::from(s) std::path::PathBuf::from(s)
.file_stem() .file_stem()
@@ -91,7 +97,7 @@ fn is_test_mode() -> bool {
} }
} }
return false; false
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -99,19 +105,25 @@ pub struct TargetEventIds {
ids: HashSet<String>, ids: HashSet<String>,
} }
impl Default for TargetEventIds {
fn default() -> Self {
Self::new()
}
}
impl TargetEventIds { impl TargetEventIds {
pub fn new() -> TargetEventIds { pub fn new() -> TargetEventIds {
return TargetEventIds { TargetEventIds {
ids: HashSet::new(), ids: HashSet::new(),
}; }
} }
pub fn is_target(&self, id: &String) -> bool { pub fn is_target(&self, id: &str) -> bool {
// 中身が空の場合は全EventIdを対象とする。 // 中身が空の場合は全EventIdを対象とする。
if self.ids.is_empty() { if self.ids.is_empty() {
return true; return true;
} }
return self.ids.contains(id); self.ids.contains(id)
} }
} }
@@ -121,7 +133,7 @@ fn load_target_ids(path: &str) -> TargetEventIds {
if lines.is_err() { if lines.is_err() {
AlertMessage::alert( AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()), &mut BufWriter::new(std::io::stderr().lock()),
&lines.as_ref().unwrap_err(), lines.as_ref().unwrap_err(),
) )
.ok(); .ok();
return ret; return ret;
@@ -134,7 +146,7 @@ fn load_target_ids(path: &str) -> TargetEventIds {
ret.ids.insert(line); ret.ids.insert(line);
} }
return ret; ret
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -143,6 +155,12 @@ pub struct TargetEventTime {
end_time: Option<DateTime<Utc>>, end_time: Option<DateTime<Utc>>,
} }
impl Default for TargetEventTime {
fn default() -> Self {
Self::new()
}
}
impl TargetEventTime { impl TargetEventTime {
pub fn new() -> Self { pub fn new() -> Self {
let start_time = let start_time =
@@ -180,17 +198,17 @@ impl TargetEventTime {
} else { } else {
None None
}; };
return Self::set(start_time, end_time); Self::set(start_time, end_time)
} }
pub fn set( pub fn set(
start_time: Option<chrono::DateTime<chrono::Utc>>, input_start_time: Option<chrono::DateTime<chrono::Utc>>,
end_time: Option<chrono::DateTime<chrono::Utc>>, input_end_time: Option<chrono::DateTime<chrono::Utc>>,
) -> Self { ) -> Self {
return Self { Self {
start_time: start_time, start_time: input_start_time,
end_time: end_time, end_time: input_end_time,
}; }
} }
pub fn is_target(&self, eventtime: &Option<DateTime<Utc>>) -> bool { pub fn is_target(&self, eventtime: &Option<DateTime<Utc>>) -> bool {
@@ -207,7 +225,7 @@ impl TargetEventTime {
return false; return false;
} }
} }
return true; true
} }
} }
@@ -219,18 +237,24 @@ pub struct EventKeyAliasConfig {
impl EventKeyAliasConfig { impl EventKeyAliasConfig {
pub fn new() -> EventKeyAliasConfig { pub fn new() -> EventKeyAliasConfig {
return EventKeyAliasConfig { EventKeyAliasConfig {
key_to_eventkey: HashMap::new(), key_to_eventkey: HashMap::new(),
key_to_split_eventkey: HashMap::new(), key_to_split_eventkey: HashMap::new(),
}; }
} }
pub fn get_event_key(&self, alias: &String) -> Option<&String> { pub fn get_event_key(&self, alias: &str) -> Option<&String> {
return self.key_to_eventkey.get(alias); self.key_to_eventkey.get(alias)
} }
pub fn get_event_key_split(&self, alias: &String) -> Option<&Vec<usize>> { pub fn get_event_key_split(&self, alias: &str) -> Option<&Vec<usize>> {
return self.key_to_split_eventkey.get(alias); self.key_to_split_eventkey.get(alias)
}
}
impl Default for EventKeyAliasConfig {
fn default() -> Self {
Self::new()
} }
} }
@@ -241,7 +265,7 @@ fn load_eventkey_alias(path: &str) -> EventKeyAliasConfig {
if read_result.is_err() { if read_result.is_err() {
AlertMessage::alert( AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()), &mut BufWriter::new(std::io::stderr().lock()),
&read_result.as_ref().unwrap_err(), read_result.as_ref().unwrap_err(),
) )
.ok(); .ok();
return config; return config;
@@ -255,20 +279,20 @@ fn load_eventkey_alias(path: &str) -> EventKeyAliasConfig {
let empty = &"".to_string(); let empty = &"".to_string();
let alias = line.get(0).unwrap_or(empty); let alias = line.get(0).unwrap_or(empty);
let event_key = line.get(1).unwrap_or(empty); let event_key = line.get(1).unwrap_or(empty);
if alias.len() == 0 || event_key.len() == 0 { if alias.is_empty() || event_key.is_empty() {
return; return;
} }
config config
.key_to_eventkey .key_to_eventkey
.insert(alias.to_owned(), event_key.to_owned()); .insert(alias.to_owned(), event_key.to_owned());
let splits = event_key.split(".").map(|s| s.len()).collect(); let splits = event_key.split('.').map(|s| s.len()).collect();
config config
.key_to_split_eventkey .key_to_split_eventkey
.insert(alias.to_owned(), splits); .insert(alias.to_owned(), splits);
}); });
config.key_to_eventkey.shrink_to_fit(); config.key_to_eventkey.shrink_to_fit();
return config; config
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -278,16 +302,22 @@ pub struct EventInfo {
pub comment: String, pub comment: String,
} }
impl Default for EventInfo {
fn default() -> Self {
Self::new()
}
}
impl EventInfo { impl EventInfo {
pub fn new() -> EventInfo { pub fn new() -> EventInfo {
let evttitle = "Unknown".to_string(); let evttitle = "Unknown".to_string();
let detectflg = "".to_string(); let detectflg = "".to_string();
let comment = "".to_string(); let comment = "".to_string();
return EventInfo { EventInfo {
evttitle, evttitle,
detectflg, detectflg,
comment, comment,
}; }
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -295,14 +325,20 @@ pub struct EventInfoConfig {
eventinfo: HashMap<String, EventInfo>, eventinfo: HashMap<String, EventInfo>,
} }
impl Default for EventInfoConfig {
fn default() -> Self {
Self::new()
}
}
impl EventInfoConfig { impl EventInfoConfig {
pub fn new() -> EventInfoConfig { pub fn new() -> EventInfoConfig {
return EventInfoConfig { EventInfoConfig {
eventinfo: HashMap::new(), eventinfo: HashMap::new(),
};
} }
pub fn get_event_id(&self, eventid: &String) -> Option<&EventInfo> { }
return self.eventinfo.get(eventid); pub fn get_event_id(&self, eventid: &str) -> Option<&EventInfo> {
self.eventinfo.get(eventid)
} }
} }
@@ -313,7 +349,7 @@ fn load_eventcode_info(path: &str) -> EventInfoConfig {
if read_result.is_err() { if read_result.is_err() {
AlertMessage::alert( AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()), &mut BufWriter::new(std::io::stderr().lock()),
&read_result.as_ref().unwrap_err(), read_result.as_ref().unwrap_err(),
) )
.ok(); .ok();
return config; return config;
@@ -339,7 +375,7 @@ fn load_eventcode_info(path: &str) -> EventInfoConfig {
.eventinfo .eventinfo
.insert(eventcode.to_owned(), infodata.to_owned()); .insert(eventcode.to_owned(), infodata.to_owned());
}); });
return config; config
} }
#[cfg(test)] #[cfg(test)]
@@ -375,9 +411,9 @@ mod tests {
let within_range = Some("2019-02-27T01:05:01Z".parse::<DateTime<Utc>>().unwrap()); let within_range = Some("2019-02-27T01:05:01Z".parse::<DateTime<Utc>>().unwrap());
let out_of_range2 = Some("2021-02-27T01:05:01Z".parse::<DateTime<Utc>>().unwrap()); let out_of_range2 = Some("2021-02-27T01:05:01Z".parse::<DateTime<Utc>>().unwrap());
assert_eq!(time_filter.is_target(&out_of_range1), false); assert!(!time_filter.is_target(&out_of_range1));
assert_eq!(time_filter.is_target(&within_range), true); assert!(time_filter.is_target(&within_range));
assert_eq!(time_filter.is_target(&out_of_range2), false); assert!(!time_filter.is_target(&out_of_range2));
} }
#[test] #[test]
@@ -386,7 +422,7 @@ mod tests {
let end_time = Some("2020-03-30T12:00:09Z".parse::<DateTime<Utc>>().unwrap()); let end_time = Some("2020-03-30T12:00:09Z".parse::<DateTime<Utc>>().unwrap());
let time_filter = configs::TargetEventTime::set(start_time, end_time); let time_filter = configs::TargetEventTime::set(start_time, end_time);
assert_eq!(time_filter.is_target(&start_time), true); assert!(time_filter.is_target(&start_time));
assert_eq!(time_filter.is_target(&end_time), true); assert!(time_filter.is_target(&end_time));
} }
} }

View File

@@ -2,6 +2,7 @@ extern crate csv;
use crate::detections::configs; use crate::detections::configs;
use crate::detections::print::AlertMessage; use crate::detections::print::AlertMessage;
use crate::detections::print::DetectInfo;
use crate::detections::print::ERROR_LOG_STACK; use crate::detections::print::ERROR_LOG_STACK;
use crate::detections::print::MESSAGES; use crate::detections::print::MESSAGES;
use crate::detections::print::QUIET_ERRORS_FLAG; use crate::detections::print::QUIET_ERRORS_FLAG;
@@ -31,8 +32,8 @@ pub struct EvtxRecordInfo {
} }
impl EvtxRecordInfo { impl EvtxRecordInfo {
pub fn get_value(&self, key: &String) -> Option<&String> { pub fn get_value(&self, key: &str) -> Option<&String> {
return self.key_2_value.get(key); self.key_2_value.get(key)
} }
} }
@@ -42,12 +43,12 @@ pub struct Detection {
} }
impl Detection { impl Detection {
pub fn new(rules: Vec<RuleNode>) -> Detection { pub fn new(rule_nodes: Vec<RuleNode>) -> Detection {
return Detection { rules: rules }; Detection { rules: rule_nodes }
} }
pub fn start(self, rt: &Runtime, records: Vec<EvtxRecordInfo>) -> Self { pub fn start(self, rt: &Runtime, records: Vec<EvtxRecordInfo>) -> Self {
return rt.block_on(self.execute_rules(records)); rt.block_on(self.execute_rules(records))
} }
// ルールファイルをパースします。 // ルールファイルをパースします。
@@ -104,9 +105,9 @@ impl Detection {
}); });
} }
parseerror_count += 1; parseerror_count += 1;
println!(""); // 一行開けるためのprintln println!(); // 一行開けるためのprintln
}); });
return Option::None; Option::None
}; };
// parse rule files // parse rule files
let ret = rulefile_loader let ret = rulefile_loader
@@ -120,7 +121,7 @@ impl Detection {
&parseerror_count, &parseerror_count,
&rulefile_loader.ignorerule_count, &rulefile_loader.ignorerule_count,
); );
return ret; ret
} }
// 複数のイベントレコードに対して、複数のルールを1個実行します。 // 複数のイベントレコードに対して、複数のルールを1個実行します。
@@ -132,10 +133,7 @@ impl Detection {
.into_iter() .into_iter()
.map(|rule| { .map(|rule| {
let records_cloned = Arc::clone(&records_arc); let records_cloned = Arc::clone(&records_arc);
return spawn(async move { spawn(async move { Detection::execute_rule(rule, records_cloned) })
let moved_rule = Detection::execute_rule(rule, records_cloned);
return moved_rule;
});
}) })
.collect(); .collect();
@@ -151,7 +149,7 @@ impl Detection {
// self.rulesが再度所有権を取り戻せるように、Detection::execute_ruleで引数に渡したruleを戻り値として返すようにしている。 // self.rulesが再度所有権を取り戻せるように、Detection::execute_ruleで引数に渡したruleを戻り値として返すようにしている。
self.rules = rules; self.rules = rules;
return self; self
} }
pub fn add_aggcondition_msges(self, rt: &Runtime) { pub fn add_aggcondition_msges(self, rt: &Runtime) {
@@ -175,17 +173,17 @@ impl Detection {
fn execute_rule(mut rule: RuleNode, records: Arc<Vec<EvtxRecordInfo>>) -> RuleNode { fn execute_rule(mut rule: RuleNode, records: Arc<Vec<EvtxRecordInfo>>) -> RuleNode {
let agg_condition = rule.has_agg_condition(); let agg_condition = rule.has_agg_condition();
for record_info in records.as_ref() { for record_info in records.as_ref() {
let result = rule.select(&record_info); let result = rule.select(record_info);
if !result { if !result {
continue; continue;
} }
// aggregation conditionが存在しない場合はそのまま出力対応を行う // aggregation conditionが存在しない場合はそのまま出力対応を行う
if !agg_condition { if !agg_condition {
Detection::insert_message(&rule, &record_info); Detection::insert_message(&rule, record_info);
} }
} }
return rule; rule
} }
/// 条件に合致したレコードを表示するための関数 /// 条件に合致したレコードを表示するための関数
@@ -193,23 +191,27 @@ impl Detection {
let tag_info: Vec<String> = rule.yaml["tags"] let tag_info: Vec<String> = rule.yaml["tags"]
.as_vec() .as_vec()
.unwrap_or(&Vec::default()) .unwrap_or(&Vec::default())
.into_iter() .iter()
.map(|info| info.as_str().unwrap_or("").replace("attack.", "")) .map(|info| info.as_str().unwrap_or("").replace("attack.", ""))
.collect(); .collect();
MESSAGES.lock().unwrap().insert( MESSAGES.lock().unwrap().insert(
record_info.evtx_filepath.to_string(),
rule.rulepath.to_string(),
&record_info.record, &record_info.record,
rule.yaml["level"].as_str().unwrap_or("-").to_string(), rule.yaml["details"].as_str().unwrap_or("").to_string(),
record_info.record["Event"]["System"]["Computer"] DetectInfo {
filepath: record_info.evtx_filepath.to_string(),
rulepath: rule.rulepath.to_string(),
level: rule.yaml["level"].as_str().unwrap_or("-").to_string(),
computername: record_info.record["Event"]["System"]["Computer"]
.to_string() .to_string()
.replace("\"", ""), .replace("\"", ""),
get_serde_number_to_string(&record_info.record["Event"]["System"]["EventID"]) eventid: get_serde_number_to_string(
.unwrap_or("-".to_owned()) &record_info.record["Event"]["System"]["EventID"],
.to_string(), )
rule.yaml["title"].as_str().unwrap_or("").to_string(), .unwrap_or_else(|| "-".to_owned()),
rule.yaml["details"].as_str().unwrap_or("").to_string(), alert: rule.yaml["title"].as_str().unwrap_or("").to_string(),
tag_info.join(" : "), detail: String::default(),
tag_info: tag_info.join(" : "),
},
); );
} }
@@ -218,20 +220,22 @@ impl Detection {
let tag_info: Vec<String> = rule.yaml["tags"] let tag_info: Vec<String> = rule.yaml["tags"]
.as_vec() .as_vec()
.unwrap_or(&Vec::default()) .unwrap_or(&Vec::default())
.into_iter() .iter()
.map(|info| info.as_str().unwrap_or("").replace("attack.", "")) .map(|info| info.as_str().unwrap_or("").replace("attack.", ""))
.collect(); .collect();
let output = Detection::create_count_output(rule, &agg_result); let output = Detection::create_count_output(rule, &agg_result);
MESSAGES.lock().unwrap().insert_message( MESSAGES.lock().unwrap().insert_message(
"-".to_owned(), DetectInfo {
rule.rulepath.to_owned(), filepath: "-".to_owned(),
rulepath: rule.rulepath.to_owned(),
level: rule.yaml["level"].as_str().unwrap_or("").to_owned(),
computername: "-".to_owned(),
eventid: "-".to_owned(),
alert: rule.yaml["title"].as_str().unwrap_or("").to_owned(),
detail: output,
tag_info: tag_info.join(" : "),
},
agg_result.start_timedate, agg_result.start_timedate,
rule.yaml["level"].as_str().unwrap_or("").to_owned(),
"-".to_owned(),
"-".to_owned(),
rule.yaml["title"].as_str().unwrap_or("").to_owned(),
output.to_owned(),
tag_info.join(" : "),
) )
} }
@@ -242,15 +246,11 @@ impl Detection {
let agg_condition_raw_str: Vec<&str> = rule.yaml["detection"]["condition"] let agg_condition_raw_str: Vec<&str> = rule.yaml["detection"]["condition"]
.as_str() .as_str()
.unwrap() .unwrap()
.split("|") .split('|')
.collect(); .collect();
// この関数が呼び出されている段階で既にaggregation conditionは存在する前提なのでunwrap前の確認は行わない // この関数が呼び出されている段階で既にaggregation conditionは存在する前提なのでunwrap前の確認は行わない
let agg_condition = rule.get_agg_condition().unwrap(); let agg_condition = rule.get_agg_condition().unwrap();
let exist_timeframe = rule.yaml["detection"]["timeframe"] let exist_timeframe = rule.yaml["detection"]["timeframe"].as_str().unwrap_or("") != "";
.as_str()
.unwrap_or("")
.to_string()
!= "";
// この関数が呼び出されている段階で既にaggregation conditionは存在する前提なのでagg_conditionの配列の長さは2となる // この関数が呼び出されている段階で既にaggregation conditionは存在する前提なのでagg_conditionの配列の長さは2となる
ret.push_str(agg_condition_raw_str[1].trim()); ret.push_str(agg_condition_raw_str[1].trim());
if exist_timeframe { if exist_timeframe {
@@ -281,8 +281,9 @@ impl Detection {
)); ));
} }
return ret; ret
} }
pub fn print_rule_load_info( pub fn print_rule_load_info(
rc: &HashMap<String, u128>, rc: &HashMap<String, u128>,
parseerror_count: &u128, parseerror_count: &u128,
@@ -302,7 +303,7 @@ impl Detection {
"Total enabled detection rules: {}", "Total enabled detection rules: {}",
total - ignore_count - parseerror_count total - ignore_count - parseerror_count
); );
println!(""); println!();
} }
} }

View File

@@ -57,6 +57,12 @@ lazy_static! {
.is_present("statistics"); .is_present("statistics");
} }
impl Default for Message {
fn default() -> Self {
Self::new()
}
}
impl Message { impl Message {
pub fn new() -> Self { pub fn new() -> Self {
let messages: BTreeMap<DateTime<Utc>, Vec<DetectInfo>> = BTreeMap::new(); let messages: BTreeMap<DateTime<Utc>, Vec<DetectInfo>> = BTreeMap::new();
@@ -64,67 +70,21 @@ impl Message {
} }
/// メッセージの設定を行う関数。aggcondition対応のためrecordではなく出力をする対象時間がDatetime形式での入力としている /// メッセージの設定を行う関数。aggcondition対応のためrecordではなく出力をする対象時間がDatetime形式での入力としている
pub fn insert_message( pub fn insert_message(&mut self, detect_info: DetectInfo, event_time: DateTime<Utc>) {
&mut self, if let Some(v) = self.map.get_mut(&event_time) {
target_file: String,
rule_path: String,
event_time: DateTime<Utc>,
level: String,
computername: String,
eventid: String,
event_title: String,
event_detail: String,
tag_info: String,
) {
let detect_info = DetectInfo {
filepath: target_file,
rulepath: rule_path,
level: level,
computername: computername,
eventid: eventid,
alert: event_title,
detail: event_detail,
tag_info: tag_info,
};
match self.map.get_mut(&event_time) {
Some(v) => {
v.push(detect_info); v.push(detect_info);
} } else {
None => {
let m = vec![detect_info; 1]; let m = vec![detect_info; 1];
self.map.insert(event_time, m); self.map.insert(event_time, m);
} }
} }
}
/// メッセージを設定 /// メッセージを設定
pub fn insert( pub fn insert(&mut self, event_record: &Value, output: String, mut detect_info: DetectInfo) {
&mut self, detect_info.detail = self.parse_message(event_record, output);
target_file: String,
rule_path: String,
event_record: &Value,
level: String,
computername: String,
eventid: String,
event_title: String,
output: String,
tag_info: String,
) {
let message = &self.parse_message(event_record, output);
let default_time = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); let default_time = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0);
let time = Message::get_event_time(event_record).unwrap_or(default_time); let time = Message::get_event_time(event_record).unwrap_or(default_time);
self.insert_message( self.insert_message(detect_info, time)
target_file,
rule_path,
time,
level,
computername,
eventid,
event_title,
message.to_string(),
tag_info,
)
} }
fn parse_message(&mut self, event_record: &Value, output: String) -> String { fn parse_message(&mut self, event_record: &Value, output: String) -> String {
@@ -141,9 +101,9 @@ impl Message {
.collect::<String>(); .collect::<String>();
if let Some(array_str) = configs::EVENTKEY_ALIAS.get_event_key(&target_str) { if let Some(array_str) = configs::EVENTKEY_ALIAS.get_event_key(&target_str) {
let split: Vec<&str> = array_str.split(".").collect(); let split: Vec<&str> = array_str.split('.').collect();
let mut is_exist_event_key = false; let mut is_exist_event_key = false;
let mut tmp_event_record: &Value = event_record.into(); let mut tmp_event_record: &Value = event_record;
for s in &split { for s in &split {
if let Some(record) = tmp_event_record.get(s) { if let Some(record) = tmp_event_record.get(s) {
is_exist_event_key = true; is_exist_event_key = true;
@@ -193,7 +153,7 @@ impl Message {
} }
detect_count += detect_infos.len(); detect_count += detect_infos.len();
} }
println!(""); println!();
println!("Total events:{:?}", detect_count); println!("Total events:{:?}", detect_count);
} }
@@ -224,20 +184,14 @@ impl AlertMessage {
} }
let mut error_log_writer = BufWriter::new(File::create(path).unwrap()); let mut error_log_writer = BufWriter::new(File::create(path).unwrap());
error_log_writer error_log_writer
.write( .write_all(
format!( format!(
"user input: {:?}\n", "user input: {:?}\n",
format_args!( format_args!("{}", env::args().collect::<Vec<String>>().join(" "))
"{}",
env::args()
.map(|arg| arg)
.collect::<Vec<String>>()
.join(" ")
)
) )
.as_bytes(), .as_bytes(),
) )
.unwrap(); .ok();
for error_log in ERROR_LOG_STACK.lock().unwrap().iter() { for error_log in ERROR_LOG_STACK.lock().unwrap().iter() {
writeln!(error_log_writer, "{}", error_log).ok(); writeln!(error_log_writer, "{}", error_log).ok();
} }
@@ -245,22 +199,23 @@ impl AlertMessage {
"Errors were generated. Please check {} for details.", "Errors were generated. Please check {} for details.",
ERROR_LOG_PATH.to_string() ERROR_LOG_PATH.to_string()
); );
println!(""); println!();
} }
/// ERRORメッセージを表示する関数 /// ERRORメッセージを表示する関数
pub fn alert<W: Write>(w: &mut W, contents: &String) -> io::Result<()> { pub fn alert<W: Write>(w: &mut W, contents: &str) -> io::Result<()> {
writeln!(w, "[ERROR] {}", contents) writeln!(w, "[ERROR] {}", contents)
} }
/// WARNメッセージを表示する関数 /// WARNメッセージを表示する関数
pub fn warn<W: Write>(w: &mut W, contents: &String) -> io::Result<()> { pub fn warn<W: Write>(w: &mut W, contents: &str) -> io::Result<()> {
writeln!(w, "[WARN] {}", contents) writeln!(w, "[WARN] {}", contents)
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::detections::print::DetectInfo;
use crate::detections::print::{AlertMessage, Message}; use crate::detections::print::{AlertMessage, Message};
use serde_json::Value; use serde_json::Value;
use std::io::BufWriter; use std::io::BufWriter;
@@ -284,15 +239,18 @@ mod tests {
"##; "##;
let event_record_1: Value = serde_json::from_str(json_str_1).unwrap(); let event_record_1: Value = serde_json::from_str(json_str_1).unwrap();
message.insert( message.insert(
"a".to_string(),
"test_rule".to_string(),
&event_record_1, &event_record_1,
"high".to_string(),
"testcomputer1".to_string(),
"1".to_string(),
"test1".to_string(),
"CommandLine1: %CommandLine%".to_string(), "CommandLine1: %CommandLine%".to_string(),
"txxx.001".to_string(), DetectInfo {
filepath: "a".to_string(),
rulepath: "test_rule".to_string(),
level: "high".to_string(),
computername: "testcomputer1".to_string(),
eventid: "1".to_string(),
alert: "test1".to_string(),
detail: String::default(),
tag_info: "txxx.001".to_string(),
},
); );
let json_str_2 = r##" let json_str_2 = r##"
@@ -311,15 +269,18 @@ mod tests {
"##; "##;
let event_record_2: Value = serde_json::from_str(json_str_2).unwrap(); let event_record_2: Value = serde_json::from_str(json_str_2).unwrap();
message.insert( message.insert(
"a".to_string(),
"test_rule2".to_string(),
&event_record_2, &event_record_2,
"high".to_string(),
"testcomputer2".to_string(),
"2".to_string(),
"test2".to_string(),
"CommandLine2: %CommandLine%".to_string(), "CommandLine2: %CommandLine%".to_string(),
"txxx.002".to_string(), DetectInfo {
filepath: "a".to_string(),
rulepath: "test_rule2".to_string(),
level: "high".to_string(),
computername: "testcomputer2".to_string(),
eventid: "2".to_string(),
alert: "test2".to_string(),
detail: String::default(),
tag_info: "txxx.002".to_string(),
},
); );
let json_str_3 = r##" let json_str_3 = r##"
@@ -338,15 +299,18 @@ mod tests {
"##; "##;
let event_record_3: Value = serde_json::from_str(json_str_3).unwrap(); let event_record_3: Value = serde_json::from_str(json_str_3).unwrap();
message.insert( message.insert(
"a".to_string(),
"test_rule3".to_string(),
&event_record_3, &event_record_3,
"high".to_string(),
"testcomputer3".to_string(),
"3".to_string(),
"test3".to_string(),
"CommandLine3: %CommandLine%".to_string(), "CommandLine3: %CommandLine%".to_string(),
"txxx.003".to_string(), DetectInfo {
filepath: "a".to_string(),
rulepath: "test_rule3".to_string(),
level: "high".to_string(),
computername: "testcomputer3".to_string(),
eventid: "3".to_string(),
alert: "test3".to_string(),
detail: String::default(),
tag_info: "txxx.003".to_string(),
},
); );
let json_str_4 = r##" let json_str_4 = r##"
@@ -360,15 +324,18 @@ mod tests {
"##; "##;
let event_record_4: Value = serde_json::from_str(json_str_4).unwrap(); let event_record_4: Value = serde_json::from_str(json_str_4).unwrap();
message.insert( message.insert(
"a".to_string(),
"test_rule4".to_string(),
&event_record_4, &event_record_4,
"medium".to_string(),
"testcomputer4".to_string(),
"4".to_string(),
"test4".to_string(),
"CommandLine4: %CommandLine%".to_string(), "CommandLine4: %CommandLine%".to_string(),
"txxx.004".to_string(), DetectInfo {
filepath: "a".to_string(),
rulepath: "test_rule4".to_string(),
level: "medium".to_string(),
computername: "testcomputer4".to_string(),
eventid: "4".to_string(),
alert: "test4".to_string(),
detail: String::default(),
tag_info: "txxx.004".to_string(),
},
); );
let display = format!("{}", format_args!("{:?}", message)); let display = format!("{}", format_args!("{:?}", message));

View File

@@ -28,15 +28,15 @@ pub struct AggregationParseInfo {
#[derive(Debug)] #[derive(Debug)]
pub enum AggregationConditionToken { pub enum AggregationConditionToken {
COUNT(String), // count Count(String), // count
SPACE, // 空白 Space, // 空白
BY, // by BY, // by
EQ, // ..と等しい EQ, // ..と等しい
LE, // ..以下 LE, // ..以下
LT, // ..未満 LT, // ..未満
GE, // ..以上 GE, // ..以上
GT, // .よりおおきい GT, // .よりおおきい
KEYWORD(String), // BYのフィールド名 Keyword(String), // BYのフィールド名
} }
/// SIGMAルールでいうAggregationConditionを解析する。 /// SIGMAルールでいうAggregationConditionを解析する。
@@ -52,12 +52,12 @@ impl AggegationConditionCompiler {
pub fn compile(&self, condition_str: String) -> Result<Option<AggregationParseInfo>, String> { pub fn compile(&self, condition_str: String) -> Result<Option<AggregationParseInfo>, String> {
let result = self.compile_body(condition_str); let result = self.compile_body(condition_str);
if let Result::Err(msg) = result { if let Result::Err(msg) = result {
return Result::Err(format!( Result::Err(format!(
"An aggregation condition parse error has occurred. {}", "An aggregation condition parse error has occurred. {}",
msg msg
)); ))
} else { } else {
return result; result
} }
} }
@@ -82,7 +82,7 @@ impl AggegationConditionCompiler {
let tokens = self.tokenize(aggregation_str)?; let tokens = self.tokenize(aggregation_str)?;
return self.parse(tokens); self.parse(tokens)
} }
/// 字句解析します。 /// 字句解析します。
@@ -90,10 +90,10 @@ impl AggegationConditionCompiler {
&self, &self,
condition_str: String, condition_str: String,
) -> Result<Vec<AggregationConditionToken>, 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(); 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| { let captured = self::AGGREGATION_REGEXMAP.iter().find_map(|regex| {
return regex.captures(cur_condition_str.as_str()); 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 mached_str = captured.unwrap().get(0).unwrap().as_str();
let token = self.to_enum(mached_str.to_string()); 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); cur_condition_str = cur_condition_str.replacen(mached_str, "", 1);
continue; continue;
@@ -115,19 +115,19 @@ impl AggegationConditionCompiler {
cur_condition_str = cur_condition_str.replacen(mached_str, "", 1); 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 { fn is_cmp_op(&self, token: &AggregationConditionToken) -> bool {
return match token { matches!(
AggregationConditionToken::EQ => true, token,
AggregationConditionToken::LE => true, AggregationConditionToken::EQ
AggregationConditionToken::LT => true, | AggregationConditionToken::LE
AggregationConditionToken::GE => true, | AggregationConditionToken::LT
AggregationConditionToken::GT => true, | AggregationConditionToken::GE
_ => false, | AggregationConditionToken::GT
}; )
} }
/// 構文解析します。 /// 構文解析します。
@@ -144,7 +144,7 @@ impl AggegationConditionCompiler {
let token = token_ite.next().unwrap(); let token = token_ite.next().unwrap();
let mut count_field_name: Option<String> = Option::None; 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() { if !field_name.is_empty() {
count_field_name = Option::Some(field_name); 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); by_field_name = Option::Some(keyword);
token_ite.next() token_ite.next()
} else { } else {
@@ -200,14 +200,14 @@ impl AggegationConditionCompiler {
); );
} }
let token = token_ite.next().unwrap_or(AggregationConditionToken::SPACE); let token = token_ite.next().unwrap_or(AggregationConditionToken::Space);
let cmp_number = if let AggregationConditionToken::KEYWORD(number) = token { let cmp_number = if let AggregationConditionToken::Keyword(number) = token {
let number: Result<i64, _> = number.parse(); 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()); return Result::Err("The compare operator needs a number like '> 3'.".to_string());
} else {
number.unwrap()
} }
} else { } else {
// 比較演算子の後に数値が無い。 // 比較演算子の後に数値が無い。
@@ -224,7 +224,7 @@ impl AggegationConditionCompiler {
_cmp_op: cmp_token, _cmp_op: cmp_token,
_cmp_num: cmp_number, _cmp_num: cmp_number,
}; };
return Result::Ok(Option::Some(info)); Result::Ok(Option::Some(info))
} }
/// 文字列をConditionTokenに変換する。 /// 文字列をConditionTokenに変換する。
@@ -234,23 +234,23 @@ impl AggegationConditionCompiler {
.replacen("count(", "", 1) .replacen("count(", "", 1)
.replacen(")", "", 1) .replacen(")", "", 1)
.replace(" ", ""); .replace(" ", "");
return AggregationConditionToken::COUNT(count_field); AggregationConditionToken::Count(count_field)
} else if token == " " { } else if token == " " {
return AggregationConditionToken::SPACE; AggregationConditionToken::Space
} else if token == "by" { } else if token == "by" {
return AggregationConditionToken::BY; AggregationConditionToken::BY
} else if token == "==" { } else if token == "==" {
return AggregationConditionToken::EQ; AggregationConditionToken::EQ
} else if token == "<=" { } else if token == "<=" {
return AggregationConditionToken::LE; AggregationConditionToken::LE
} else if token == ">=" { } else if token == ">=" {
return AggregationConditionToken::GE; AggregationConditionToken::GE
} else if token == "<" { } else if token == "<" {
return AggregationConditionToken::LT; AggregationConditionToken::LT
} else if token == ">" { } else if token == ">" {
return AggregationConditionToken::GT; AggregationConditionToken::GT
} else { } else {
return AggregationConditionToken::KEYWORD(token); AggregationConditionToken::Keyword(token)
} }
} }
} }
@@ -266,9 +266,9 @@ mod tests {
// countが無いパターン // countが無いパターン
let compiler = AggegationConditionCompiler::new(); let compiler = AggegationConditionCompiler::new();
let result = compiler.compile("select1 and select2".to_string()); let result = compiler.compile("select1 and select2".to_string());
assert_eq!(true, result.is_ok()); assert!(result.is_ok());
let result = result.unwrap(); let result = result.unwrap();
assert_eq!(true, result.is_none()); assert!(result.is_none());
} }
#[test] #[test]
@@ -276,43 +276,23 @@ mod tests {
// 正常系 countの中身にフィールドが無い 各種演算子を試す // 正常系 countの中身にフィールドが無い 各種演算子を試す
let token = let token =
check_aggregation_condition_ope("select1 and select2|count() > 32".to_string(), 32); check_aggregation_condition_ope("select1 and select2|count() > 32".to_string(), 32);
let is_gt = match token { assert!(matches!(token, AggregationConditionToken::GT));
AggregationConditionToken::GT => true,
_ => false,
};
assert_eq!(is_gt, true);
let token = let token =
check_aggregation_condition_ope("select1 and select2|count() >= 43".to_string(), 43); check_aggregation_condition_ope("select1 and select2|count() >= 43".to_string(), 43);
let is_gt = match token { assert!(matches!(token, AggregationConditionToken::GE));
AggregationConditionToken::GE => true,
_ => false,
};
assert_eq!(is_gt, true);
let token = let token =
check_aggregation_condition_ope("select1 and select2|count() < 59".to_string(), 59); check_aggregation_condition_ope("select1 and select2|count() < 59".to_string(), 59);
let is_gt = match token { assert!(matches!(token, AggregationConditionToken::LT));
AggregationConditionToken::LT => true,
_ => false,
};
assert_eq!(is_gt, true);
let token = let token =
check_aggregation_condition_ope("select1 and select2|count() <= 12".to_string(), 12); check_aggregation_condition_ope("select1 and select2|count() <= 12".to_string(), 12);
let is_gt = match token { assert!(matches!(token, AggregationConditionToken::LE));
AggregationConditionToken::LE => true,
_ => false,
};
assert_eq!(is_gt, true);
let token = let token =
check_aggregation_condition_ope("select1 and select2|count() == 28".to_string(), 28); check_aggregation_condition_ope("select1 and select2|count() == 28".to_string(), 28);
let is_gt = match token { assert!(matches!(token, AggregationConditionToken::EQ));
AggregationConditionToken::EQ => true,
_ => false,
};
assert_eq!(is_gt, true);
} }
#[test] #[test]
@@ -320,19 +300,15 @@ mod tests {
let compiler = AggegationConditionCompiler::new(); let compiler = AggegationConditionCompiler::new();
let result = compiler.compile("select1 or select2 | count() by iiibbb > 27".to_string()); 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(); let result = result.unwrap();
assert_eq!(true, result.is_some()); assert!(result.is_some());
let result = result.unwrap(); let result = result.unwrap();
assert_eq!("iiibbb".to_string(), result._by_field_name.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); assert_eq!(27, result._cmp_num);
let is_ok = match result._cmp_op { assert!(matches!(result._cmp_op, AggregationConditionToken::GT));
AggregationConditionToken::GT => true,
_ => false,
};
assert_eq!(true, is_ok);
} }
#[test] #[test]
@@ -340,19 +316,15 @@ mod tests {
let compiler = AggegationConditionCompiler::new(); let compiler = AggegationConditionCompiler::new();
let result = compiler.compile("select1 or select2 | count( hogehoge ) > 3".to_string()); 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(); let result = result.unwrap();
assert_eq!(true, result.is_some()); assert!(result.is_some());
let result = result.unwrap(); 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!("hogehoge", result._field_name.unwrap());
assert_eq!(3, result._cmp_num); assert_eq!(3, result._cmp_num);
let is_ok = match result._cmp_op { assert!(matches!(result._cmp_op, AggregationConditionToken::GT));
AggregationConditionToken::GT => true,
_ => false,
};
assert_eq!(true, is_ok);
} }
#[test] #[test]
@@ -361,19 +333,15 @@ mod tests {
let result = let result =
compiler.compile("select1 or select2 | count( hogehoge) by snsn > 3".to_string()); 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(); let result = result.unwrap();
assert_eq!(true, result.is_some()); assert!(result.is_some());
let result = result.unwrap(); let result = result.unwrap();
assert_eq!("snsn".to_string(), result._by_field_name.unwrap()); assert_eq!("snsn".to_string(), result._by_field_name.unwrap());
assert_eq!("hogehoge", result._field_name.unwrap()); assert_eq!("hogehoge", result._field_name.unwrap());
assert_eq!(3, result._cmp_num); assert_eq!(3, result._cmp_num);
let is_ok = match result._cmp_op { assert!(matches!(result._cmp_op, AggregationConditionToken::GT));
AggregationConditionToken::GT => true,
_ => false,
};
assert_eq!(true, is_ok);
} }
#[test] #[test]
@@ -381,7 +349,7 @@ mod tests {
let compiler = AggegationConditionCompiler::new(); let compiler = AggegationConditionCompiler::new();
let result = compiler.compile("select1 or select2 |".to_string()); let result = compiler.compile("select1 or select2 |".to_string());
assert_eq!(true, result.is_err()); assert!(result.is_err());
assert_eq!( assert_eq!(
"An aggregation condition parse error has occurred. There are no strings after the pipe(|)." "An aggregation condition parse error has occurred. There are no strings after the pipe(|)."
.to_string(), .to_string(),
@@ -395,7 +363,7 @@ mod tests {
let result = let result =
compiler.compile("select1 or select2 | count( hogeess ) by ii-i > 33".to_string()); 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!( assert_eq!(
"An aggregation condition parse error has occurred. An unusable character was found." "An aggregation condition parse error has occurred. An unusable character was found."
.to_string(), .to_string(),
@@ -410,7 +378,7 @@ mod tests {
let result = let result =
compiler.compile("select1 or select2 | by count( hogehoge) by snsn > 3".to_string()); 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()); 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 compiler = AggegationConditionCompiler::new();
let result = compiler.compile("select1 or select2 | count( hogehoge) 3".to_string()); 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()); 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 compiler = AggegationConditionCompiler::new();
let result = compiler.compile("select1 or select2 | count( hogehoge) by".to_string()); 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()); 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 = let result =
compiler.compile("select1 or select2 | count( hogehoge ) by hoe >".to_string()); 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()); 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 = let result =
compiler.compile("select1 or select2 | count( hogehoge ) by hoe > 3 33".to_string()); 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!( assert_eq!(
"An aggregation condition parse error has occurred. An unnecessary word was found." "An aggregation condition parse error has occurred. An unnecessary word was found."
.to_string(), .to_string(),
@@ -464,14 +432,14 @@ mod tests {
let compiler = AggegationConditionCompiler::new(); let compiler = AggegationConditionCompiler::new();
let result = compiler.compile(expr); let result = compiler.compile(expr);
assert_eq!(true, result.is_ok()); assert!(result.is_ok());
let result = result.unwrap(); let result = result.unwrap();
assert_eq!(true, result.is_some()); assert!(result.is_some());
let result = result.unwrap(); let result = result.unwrap();
assert_eq!(true, result._by_field_name.is_none()); assert!(result._by_field_name.is_none());
assert_eq!(true, result._field_name.is_none()); assert!(result._field_name.is_none());
assert_eq!(cmp_num, result._cmp_num); assert_eq!(cmp_num, result._cmp_num);
return result._cmp_op; result._cmp_op
} }
} }

View File

@@ -57,7 +57,7 @@ impl IntoIterator for ConditionToken {
impl ConditionToken { impl ConditionToken {
fn replace_subtoken(&self, sub_tokens: Vec<ConditionToken>) -> ConditionToken { fn replace_subtoken(&self, sub_tokens: Vec<ConditionToken>) -> ConditionToken {
return match self { match self {
ConditionToken::ParenthesisContainer(_) => { ConditionToken::ParenthesisContainer(_) => {
ConditionToken::ParenthesisContainer(sub_tokens) ConditionToken::ParenthesisContainer(sub_tokens)
} }
@@ -74,12 +74,12 @@ impl ConditionToken {
ConditionToken::SelectionReference(name) => { ConditionToken::SelectionReference(name) => {
ConditionToken::SelectionReference(name.clone()) ConditionToken::SelectionReference(name.clone())
} }
}; }
} }
pub fn sub_tokens<'a>(&'a self) -> Vec<ConditionToken> { pub fn sub_tokens(&self) -> Vec<ConditionToken> {
// TODO ここでcloneを使わずに実装できるようにしたい。 // TODO ここでcloneを使わずに実装できるようにしたい。
return match self { match self {
ConditionToken::ParenthesisContainer(sub_tokens) => sub_tokens.clone(), ConditionToken::ParenthesisContainer(sub_tokens) => sub_tokens.clone(),
ConditionToken::AndContainer(sub_tokens) => sub_tokens.clone(), ConditionToken::AndContainer(sub_tokens) => sub_tokens.clone(),
ConditionToken::OrContainer(sub_tokens) => sub_tokens.clone(), ConditionToken::OrContainer(sub_tokens) => sub_tokens.clone(),
@@ -92,14 +92,14 @@ impl ConditionToken {
ConditionToken::And => vec![], ConditionToken::And => vec![],
ConditionToken::Or => vec![], ConditionToken::Or => vec![],
ConditionToken::SelectionReference(_) => vec![], ConditionToken::SelectionReference(_) => vec![],
}; }
} }
pub fn sub_tokens_without_parenthesis<'a>(&'a self) -> Vec<ConditionToken> { pub fn sub_tokens_without_parenthesis(&self) -> Vec<ConditionToken> {
return match self { match self {
ConditionToken::ParenthesisContainer(_) => vec![], ConditionToken::ParenthesisContainer(_) => vec![],
_ => self.sub_tokens(), _ => self.sub_tokens(),
}; }
} }
} }
@@ -119,8 +119,8 @@ impl ConditionCompiler {
) -> Result<Box<dyn SelectionNode>, String> { ) -> Result<Box<dyn SelectionNode>, String> {
// パイプはここでは処理しない // パイプはここでは処理しない
let captured = self::RE_PIPE.captures(&condition_str); let captured = self::RE_PIPE.captures(&condition_str);
let condition_str = if captured.is_some() { let condition_str = if let Some(cap) = captured {
let captured = captured.unwrap().get(0).unwrap().as_str().to_string(); let captured = cap.get(0).unwrap().as_str().to_string();
condition_str.replacen(&captured, "", 1) condition_str.replacen(&captured, "", 1)
} else { } else {
condition_str condition_str
@@ -128,9 +128,9 @@ impl ConditionCompiler {
let result = self.compile_condition_body(condition_str, name_2_node); let result = self.compile_condition_body(condition_str, name_2_node);
if let Result::Err(msg) = result { 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 { } else {
return result; result
} }
} }
@@ -144,7 +144,7 @@ impl ConditionCompiler {
let parsed = self.parse(tokens)?; 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)?; 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(); let sub_tokens = token.sub_tokens();
if sub_tokens.len() == 0 { if sub_tokens.is_empty() {
return Result::Ok(token); return Result::Ok(token);
} }
@@ -181,15 +181,15 @@ impl ConditionCompiler {
let new_token = self.parse_rest_parenthesis(sub_token)?; let new_token = self.parse_rest_parenthesis(sub_token)?;
new_sub_tokens.push(new_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> { fn tokenize(&self, condition_str: &str) -> Result<Vec<ConditionToken>, String> {
let mut cur_condition_str = condition_str.clone(); let mut cur_condition_str = condition_str.to_string();
let mut tokens = Vec::new(); 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| { let captured = self::CONDITION_REGEXMAP.iter().find_map(|regex| {
return regex.captures(cur_condition_str.as_str()); return regex.captures(cur_condition_str.as_str());
}); });
@@ -210,25 +210,25 @@ impl ConditionCompiler {
cur_condition_str = cur_condition_str.replacen(mached_str, "", 1); cur_condition_str = cur_condition_str.replacen(mached_str, "", 1);
} }
return Result::Ok(tokens); Result::Ok(tokens)
} }
/// 文字列をConditionTokenに変換する。 /// 文字列をConditionTokenに変換する。
fn to_enum(&self, token: String) -> ConditionToken { fn to_enum(&self, token: String) -> ConditionToken {
if token == "(" { if token == "(" {
return ConditionToken::LeftParenthesis; ConditionToken::LeftParenthesis
} else if token == ")" { } else if token == ")" {
return ConditionToken::RightParenthesis; ConditionToken::RightParenthesis
} else if token == " " { } else if token == " " {
return ConditionToken::Space; ConditionToken::Space
} else if token == "not" { } else if token == "not" {
return ConditionToken::Not; ConditionToken::Not
} else if token == "and" { } else if token == "and" {
return ConditionToken::And; ConditionToken::And
} else if token == "or" { } else if token == "or" {
return ConditionToken::Or; ConditionToken::Or
} else { } else {
return ConditionToken::SelectionReference(token.clone()); ConditionToken::SelectionReference(token)
} }
} }
@@ -241,10 +241,7 @@ impl ConditionCompiler {
let mut token_ite = tokens.into_iter(); let mut token_ite = tokens.into_iter();
while let Some(token) = token_ite.next() { while let Some(token) = token_ite.next() {
// まず、左括弧を探す。 // まず、左括弧を探す。
let is_left = match token { let is_left = matches!(token, ConditionToken::LeftParenthesis);
ConditionToken::LeftParenthesis => true,
_ => false,
};
if !is_left { if !is_left {
ret.push(token); ret.push(token);
continue; continue;
@@ -254,7 +251,7 @@ impl ConditionCompiler {
let mut left_cnt = 1; let mut left_cnt = 1;
let mut right_cnt = 0; let mut right_cnt = 0;
let mut sub_tokens = vec![]; let mut sub_tokens = vec![];
while let Some(token) = token_ite.next() { for token in token_ite.by_ref() {
if let ConditionToken::LeftParenthesis = token { if let ConditionToken::LeftParenthesis = token {
left_cnt += 1; left_cnt += 1;
} else if let ConditionToken::RightParenthesis = token { } else if let ConditionToken::RightParenthesis = token {
@@ -275,22 +272,19 @@ impl ConditionCompiler {
} }
// この時点で右括弧が残っている場合は右括弧の数が左括弧よりも多いことを表している。 // この時点で右括弧が残っている場合は右括弧の数が左括弧よりも多いことを表している。
let is_right_left = ret.iter().any(|token| { let is_right_left = ret
return match token { .iter()
ConditionToken::RightParenthesis => true, .any(|token| matches!(token, ConditionToken::RightParenthesis));
_ => false,
};
});
if is_right_left { if is_right_left {
return Result::Err("'(' was expected but not found.".to_string()); return Result::Err("'(' was expected but not found.".to_string());
} }
return Result::Ok(ret); Result::Ok(ret)
} }
/// AND, ORをパースする。 /// AND, ORをパースする。
fn parse_and_or_operator(&self, tokens: Vec<ConditionToken>) -> Result<ConditionToken, String> { fn parse_and_or_operator(&self, tokens: Vec<ConditionToken>) -> Result<ConditionToken, String> {
if tokens.len() == 0 { if tokens.is_empty() {
// 長さ0は呼び出してはいけない // 長さ0は呼び出してはいけない
return Result::Err("Unknown error.".to_string()); return Result::Err("Unknown error.".to_string());
} }
@@ -339,7 +333,7 @@ impl ConditionCompiler {
// 次にOrでつながっている部分をまとめる // 次にOrでつながっている部分をまとめる
let or_contaienr = ConditionToken::OrContainer(operands); let or_contaienr = ConditionToken::OrContainer(operands);
return Result::Ok(or_contaienr); Result::Ok(or_contaienr)
} }
/// OperandContainerの中身をパースする。現状はNotをパースするためだけに存在している。 /// OperandContainerの中身をパースする。現状はNotをパースするためだけに存在している。
@@ -360,7 +354,7 @@ impl ConditionCompiler {
} }
// 0はありえないはず // 0はありえないはず
if sub_tokens.len() == 0 { if sub_tokens.is_empty() {
return Result::Err("Unknown error.".to_string()); return Result::Err("Unknown error.".to_string());
} }
@@ -380,20 +374,20 @@ impl ConditionCompiler {
let second_token = sub_tokens_ite.next().unwrap(); let second_token = sub_tokens_ite.next().unwrap();
if let ConditionToken::Not = first_token { if let ConditionToken::Not = first_token {
if let ConditionToken::Not = second_token { if let ConditionToken::Not = second_token {
return Result::Err("Not is continuous.".to_string()); Result::Err("Not is continuous.".to_string())
} else { } else {
let not_container = ConditionToken::NotContainer(vec![second_token]); let not_container = ConditionToken::NotContainer(vec![second_token]);
return Result::Ok(not_container); Result::Ok(not_container)
} }
} else { } else {
return Result::Err( Result::Err(
"Unknown error. Maybe it is because there are multiple names of selection nodes." "Unknown error. Maybe it is because there are multiple names of selection nodes."
.to_string(), .to_string(),
); )
} }
} else { } else {
let sub_tokens = parent_token.sub_tokens_without_parenthesis(); let sub_tokens = parent_token.sub_tokens_without_parenthesis();
if sub_tokens.len() == 0 { if sub_tokens.is_empty() {
return Result::Ok(parent_token); return Result::Ok(parent_token);
} }
@@ -403,7 +397,7 @@ impl ConditionCompiler {
new_sub_tokens.push(new_sub_token); 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に変換 // RefSelectionNodeに変換
if let ConditionToken::SelectionReference(selection_name) = token { if let ConditionToken::SelectionReference(selection_name) = token {
let selection_node = name_2_node.get(&selection_name); let selection_node = name_2_node.get(&selection_name);
if selection_node.is_none() { if let Some(select_node) = selection_node {
let err_msg = format!("{} is not defined.", selection_name); let selection_node = select_node;
return Result::Err(err_msg);
} else {
let selection_node = selection_node.unwrap();
let selection_node = Arc::clone(selection_node); let selection_node = Arc::clone(selection_node);
let ref_node = RefSelectionNode::new(selection_node); let ref_node = RefSelectionNode::new(selection_node);
return Result::Ok(Box::new(ref_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::Ok(Box::new(select_not_node));
} }
return Result::Err("Unknown error".to_string()); Result::Err("Unknown error".to_string())
} }
/// ConditionTokenがAndまたはOrTokenならばTrue /// ConditionTokenがAndまたはOrTokenならばTrue
fn is_logical(&self, token: &ConditionToken) -> bool { fn is_logical(&self, token: &ConditionToken) -> bool {
return match token { matches!(token, ConditionToken::And | ConditionToken::Or)
ConditionToken::And => true,
ConditionToken::Or => true,
_ => false,
};
} }
/// ConditionToken::OperandContainerに変換できる部分があれば変換する。 /// ConditionToken::OperandContainerに変換できる部分があれば変換する。
@@ -478,8 +468,7 @@ impl ConditionCompiler {
) -> Result<Vec<ConditionToken>, String> { ) -> Result<Vec<ConditionToken>, String> {
let mut ret = vec![]; let mut ret = vec![];
let mut grouped_operands = vec![]; // ANDとORの間にあるトークンを表す。ANDとORをOperatorとしたときのOperand let mut grouped_operands = vec![]; // ANDとORの間にあるトークンを表す。ANDとORをOperatorとしたときのOperand
let mut token_ite = tokens.into_iter(); for token in tokens.into_iter() {
while let Some(token) = token_ite.next() {
if self.is_logical(&token) { if self.is_logical(&token) {
// ここに来るのはエラーのはずだが、後でエラー出力するので、ここではエラー出さない。 // ここに来るのはエラーのはずだが、後でエラー出力するので、ここではエラー出さない。
if grouped_operands.is_empty() { if grouped_operands.is_empty() {
@@ -498,7 +487,7 @@ impl ConditionCompiler {
ret.push(ConditionToken::OperandContainer(grouped_operands)); 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); assert_eq!(rule_node.select(&recinfo), expect_select);
} }
Err(_rec) => { Err(_rec) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -582,10 +571,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); 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) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -626,10 +615,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); 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) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }

View File

@@ -18,7 +18,7 @@ use crate::detections::utils;
/// 検知された際にカウント情報を投入する関数 /// 検知された際にカウント情報を投入する関数
pub fn count(rule: &mut RuleNode, record: &Value) { pub fn count(rule: &mut RuleNode, record: &Value) {
let key = create_count_key(&rule, record); let key = create_count_key(rule, record);
let field_name: String = match rule.get_agg_condition() { let field_name: String = match rule.get_agg_condition() {
None => String::default(), None => String::default(),
Some(aggcondition) => aggcondition Some(aggcondition) => aggcondition
@@ -28,7 +28,7 @@ pub fn count(rule: &mut RuleNode, record: &Value) {
.to_owned(), .to_owned(),
}; };
let field_value = let field_value =
get_alias_value_in_record(rule, &field_name, record, false).unwrap_or(String::default()); get_alias_value_in_record(rule, &field_name, record, false).unwrap_or_default();
let default_time = Utc.ymd(1977, 1, 1).and_hms(0, 0, 0); let default_time = Utc.ymd(1977, 1, 1).and_hms(0, 0, 0);
countup( countup(
rule, rule,
@@ -57,17 +57,15 @@ pub fn countup(
/// is_by_aliasはこの関数を呼び出す際はcountのbyの値もしくはfieldの値のどちらかであるためboolとした /// is_by_aliasはこの関数を呼び出す際はcountのbyの値もしくはfieldの値のどちらかであるためboolとした
fn get_alias_value_in_record( fn get_alias_value_in_record(
rule: &RuleNode, rule: &RuleNode,
alias: &String, alias: &str,
record: &Value, record: &Value,
is_by_alias: bool, is_by_alias: bool,
) -> Option<String> { ) -> Option<String> {
if alias == "" { if alias.is_empty() {
return None; return None;
} }
match utils::get_event_value(alias, record) { match utils::get_event_value(alias, record) {
Some(value) => { Some(value) => Some(value.to_string().replace("\"", "")),
return Some(value.to_string().replace("\"", ""));
}
None => { None => {
let errmsg = match is_by_alias { let errmsg = match is_by_alias {
true => format!( true => format!(
@@ -98,9 +96,9 @@ fn get_alias_value_in_record(
.unwrap() .unwrap()
.push(format!("[ERROR] {}", errmsg)); .push(format!("[ERROR] {}", errmsg));
} }
return None; None
}
} }
};
} }
/// countでgroupbyなどの情報を区分するためのハッシュマップのキーを作成する関数。 /// countでgroupbyなどの情報を区分するためのハッシュマップのキーを作成する関数。
@@ -110,10 +108,10 @@ pub fn create_count_key(rule: &RuleNode, record: &Value) -> String {
let agg_condition = rule.get_agg_condition().unwrap(); let agg_condition = rule.get_agg_condition().unwrap();
if agg_condition._by_field_name.is_some() { if agg_condition._by_field_name.is_some() {
let by_field_key = agg_condition._by_field_name.as_ref().unwrap(); let by_field_key = agg_condition._by_field_name.as_ref().unwrap();
return get_alias_value_in_record(rule, by_field_key, record, true) get_alias_value_in_record(rule, by_field_key, record, true)
.unwrap_or("_".to_string()); .unwrap_or_else(|| "_".to_string())
} else { } else {
return "_".to_string(); "_".to_string()
} }
} }
@@ -123,9 +121,9 @@ pub fn aggregation_condition_select(rule: &RuleNode) -> Vec<AggResult> {
let value_map = &rule.countdata; let value_map = &rule.countdata;
let mut ret = Vec::new(); let mut ret = Vec::new();
for (key, value) in value_map { for (key, value) in value_map {
ret.append(&mut judge_timeframe(&rule, &value, &key.to_string())); ret.append(&mut judge_timeframe(rule, value, &key.to_string()));
} }
return ret; ret
} }
/// aggregation condition内での条件式を文字として返す関数 /// aggregation condition内での条件式を文字として返す関数
@@ -155,7 +153,7 @@ pub fn get_str_agg_eq(rule: &RuleNode) -> String {
} }
} }
ret.push_str(&agg_condition._cmp_num.to_string()); ret.push_str(&agg_condition._cmp_num.to_string());
return ret; ret
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@@ -177,16 +175,16 @@ impl TimeFrameInfo {
pub fn parse_tframe(value: String) -> TimeFrameInfo { pub fn parse_tframe(value: String) -> TimeFrameInfo {
let mut ttype: String = "".to_string(); let mut ttype: String = "".to_string();
let mut tnum = value.clone(); let mut tnum = value.clone();
if value.contains("s") { if value.contains('s') {
ttype = "s".to_owned(); ttype = "s".to_owned();
tnum.retain(|c| c != 's'); tnum.retain(|c| c != 's');
} else if value.contains("m") { } else if value.contains('m') {
ttype = "m".to_owned(); ttype = "m".to_owned();
tnum.retain(|c| c != 'm'); tnum.retain(|c| c != 'm');
} else if value.contains("h") { } else if value.contains('h') {
ttype = "h".to_owned(); ttype = "h".to_owned();
tnum.retain(|c| c != 'h'); tnum.retain(|c| c != 'h');
} else if value.contains("d") { } else if value.contains('d') {
ttype = "d".to_owned(); ttype = "d".to_owned();
tnum.retain(|c| c != 'd'); tnum.retain(|c| c != 'd');
} else { } else {
@@ -201,30 +199,28 @@ impl TimeFrameInfo {
.push(format!("[ERROR] {}", errmsg)); .push(format!("[ERROR] {}", errmsg));
} }
} }
return TimeFrameInfo { TimeFrameInfo {
timetype: ttype, timetype: ttype,
timenum: tnum.parse::<i64>(), timenum: tnum.parse::<i64>(),
}; }
} }
} }
/// TimeFrameInfoで格納されたtimeframeの値を秒数に変換した結果を返す関数 /// TimeFrameInfoで格納されたtimeframeの値を秒数に変換した結果を返す関数
pub fn get_sec_timeframe(rule: &RuleNode) -> Option<i64> { pub fn get_sec_timeframe(rule: &RuleNode) -> Option<i64> {
let timeframe = rule.detection.timeframe.as_ref(); let timeframe = rule.detection.timeframe.as_ref();
if timeframe.is_none() { timeframe?;
return Option::None;
}
let tfi = timeframe.as_ref().unwrap(); let tfi = timeframe.as_ref().unwrap();
match &tfi.timenum { match &tfi.timenum {
Ok(n) => { Ok(n) => {
if tfi.timetype == "d" { if tfi.timetype == "d" {
return Some(n * 86400); Some(n * 86400)
} else if tfi.timetype == "h" { } else if tfi.timetype == "h" {
return Some(n * 3600); Some(n * 3600)
} else if tfi.timetype == "m" { } else if tfi.timetype == "m" {
return Some(n * 60); Some(n * 60)
} else { } else {
return Some(*n); Some(*n)
} }
} }
Err(err) => { Err(err) => {
@@ -236,9 +232,9 @@ pub fn get_sec_timeframe(rule: &RuleNode) -> Option<i64> {
ERROR_LOG_STACK ERROR_LOG_STACK
.lock() .lock()
.unwrap() .unwrap()
.push(format!("[ERROR] {}", errmsg.to_string())); .push(format!("[ERROR] {}", errmsg));
} }
return Option::None; Option::None
} }
} }
} }
@@ -251,44 +247,12 @@ pub fn select_aggcon(cnt: i64, rule: &RuleNode) -> bool {
let agg_condition = agg_condition.unwrap(); let agg_condition = agg_condition.unwrap();
match agg_condition._cmp_op { match agg_condition._cmp_op {
AggregationConditionToken::EQ => { AggregationConditionToken::EQ => cnt == agg_condition._cmp_num,
if cnt == agg_condition._cmp_num { AggregationConditionToken::GE => cnt >= agg_condition._cmp_num,
return true; AggregationConditionToken::GT => cnt > agg_condition._cmp_num,
} else { AggregationConditionToken::LE => cnt <= agg_condition._cmp_num,
return false; AggregationConditionToken::LT => cnt < agg_condition._cmp_num,
} _ => false,
}
AggregationConditionToken::GE => {
if cnt >= agg_condition._cmp_num {
return true;
} else {
return false;
}
}
AggregationConditionToken::GT => {
if cnt > agg_condition._cmp_num {
return true;
} else {
return false;
}
}
AggregationConditionToken::LE => {
if cnt <= agg_condition._cmp_num {
return true;
} else {
return false;
}
}
AggregationConditionToken::LT => {
if cnt < agg_condition._cmp_num {
return true;
} else {
return false;
}
}
_ => {
return false;
}
} }
} }
@@ -312,11 +276,11 @@ trait CountStrategy {
/** /**
* datas[idx]のデータをtimeframeに追加します * datas[idx]のデータをtimeframeに追加します
*/ */
fn add_data(&mut self, idx: i64, datas: &Vec<AggRecordTimeInfo>, rule: &RuleNode); fn add_data(&mut self, idx: i64, datas: &[AggRecordTimeInfo], rule: &RuleNode);
/** /**
* datas[idx]のデータをtimeframeから削除します。 * datas[idx]のデータをtimeframeから削除します。
*/ */
fn remove_data(&mut self, idx: i64, datas: &Vec<AggRecordTimeInfo>, rule: &RuleNode); fn remove_data(&mut self, idx: i64, datas: &[AggRecordTimeInfo], rule: &RuleNode);
/** /**
* count()の値を返します。 * count()の値を返します。
*/ */
@@ -327,9 +291,9 @@ trait CountStrategy {
fn create_agg_result( fn create_agg_result(
&mut self, &mut self,
left: i64, left: i64,
datas: &Vec<AggRecordTimeInfo>, datas: &[AggRecordTimeInfo],
cnt: i64, cnt: i64,
key: &String, key: &str,
rule: &RuleNode, rule: &RuleNode,
) -> AggResult; ) -> AggResult;
} }
@@ -342,22 +306,22 @@ struct FieldStrategy {
} }
impl CountStrategy for FieldStrategy { impl CountStrategy for FieldStrategy {
fn add_data(&mut self, idx: i64, datas: &Vec<AggRecordTimeInfo>, _rule: &RuleNode) { fn add_data(&mut self, idx: i64, datas: &[AggRecordTimeInfo], _rule: &RuleNode) {
if idx >= datas.len() as i64 || idx < 0 { if idx >= datas.len() as i64 || idx < 0 {
return; return;
} }
let value = &datas[idx as usize].field_record_value; let value = &datas[idx as usize].field_record_value;
let key_val = self.value_2_cnt.get_key_value_mut(value); let key_val = self.value_2_cnt.get_key_value_mut(value);
if key_val.is_none() { if let Some(kv) = key_val {
self.value_2_cnt.insert(value.to_string(), 1); let (_, val) = kv;
} else {
let (_, val) = key_val.unwrap();
*val += 1; *val += 1;
} else {
self.value_2_cnt.insert(value.to_string(), 1);
} }
} }
fn remove_data(&mut self, idx: i64, datas: &Vec<AggRecordTimeInfo>, _rule: &RuleNode) { fn remove_data(&mut self, idx: i64, datas: &[AggRecordTimeInfo], _rule: &RuleNode) {
if idx >= datas.len() as i64 || idx < 0 { if idx >= datas.len() as i64 || idx < 0 {
return; return;
} }
@@ -384,19 +348,19 @@ impl CountStrategy for FieldStrategy {
fn create_agg_result( fn create_agg_result(
&mut self, &mut self,
left: i64, left: i64,
datas: &Vec<AggRecordTimeInfo>, datas: &[AggRecordTimeInfo],
_cnt: i64, _cnt: i64,
key: &String, key: &str,
rule: &RuleNode, rule: &RuleNode,
) -> AggResult { ) -> AggResult {
let values: Vec<String> = self.value_2_cnt.drain().map(|(key, _)| key).collect(); // drainで初期化 let values: Vec<String> = self.value_2_cnt.drain().map(|(key, _)| key).collect(); // drainで初期化
return AggResult::new( AggResult::new(
values.len() as i64, values.len() as i64,
key.to_string(), key.to_string(),
values, values,
datas[left as usize].record_time, datas[left as usize].record_time,
get_str_agg_eq(rule), get_str_agg_eq(rule),
); )
} }
} }
@@ -408,7 +372,7 @@ struct NoFieldStrategy {
} }
impl CountStrategy for NoFieldStrategy { impl CountStrategy for NoFieldStrategy {
fn add_data(&mut self, idx: i64, datas: &Vec<AggRecordTimeInfo>, _rule: &RuleNode) { fn add_data(&mut self, idx: i64, datas: &[AggRecordTimeInfo], _rule: &RuleNode) {
if idx >= datas.len() as i64 || idx < 0 { if idx >= datas.len() as i64 || idx < 0 {
return; return;
} }
@@ -416,7 +380,7 @@ impl CountStrategy for NoFieldStrategy {
self.cnt += 1; self.cnt += 1;
} }
fn remove_data(&mut self, idx: i64, datas: &Vec<AggRecordTimeInfo>, _rule: &RuleNode) { fn remove_data(&mut self, idx: i64, datas: &[AggRecordTimeInfo], _rule: &RuleNode) {
if idx >= datas.len() as i64 || idx < 0 { if idx >= datas.len() as i64 || idx < 0 {
return; return;
} }
@@ -425,15 +389,15 @@ impl CountStrategy for NoFieldStrategy {
} }
fn count(&mut self) -> i64 { fn count(&mut self) -> i64 {
return self.cnt; self.cnt
} }
fn create_agg_result( fn create_agg_result(
&mut self, &mut self,
left: i64, left: i64,
datas: &Vec<AggRecordTimeInfo>, datas: &[AggRecordTimeInfo],
cnt: i64, cnt: i64,
key: &String, key: &str,
rule: &RuleNode, rule: &RuleNode,
) -> AggResult { ) -> AggResult {
let ret = AggResult::new( let ret = AggResult::new(
@@ -444,31 +408,31 @@ impl CountStrategy for NoFieldStrategy {
get_str_agg_eq(rule), get_str_agg_eq(rule),
); );
self.cnt = 0; //cntを初期化 self.cnt = 0; //cntを初期化
return ret; ret
} }
} }
fn _create_counter(rule: &RuleNode) -> Box<dyn CountStrategy> { fn _create_counter(rule: &RuleNode) -> Box<dyn CountStrategy> {
let agg_cond = rule.get_agg_condition().unwrap(); let agg_cond = rule.get_agg_condition().unwrap();
if agg_cond._field_name.is_some() { if agg_cond._field_name.is_some() {
return Box::new(FieldStrategy { Box::new(FieldStrategy {
value_2_cnt: HashMap::new(), value_2_cnt: HashMap::new(),
}); })
} else { } else {
return Box::new(NoFieldStrategy { cnt: 0 }); Box::new(NoFieldStrategy { cnt: 0 })
} }
} }
fn _get_timestamp(idx: i64, datas: &Vec<AggRecordTimeInfo>) -> i64 { fn _get_timestamp(idx: i64, datas: &[AggRecordTimeInfo]) -> i64 {
return datas[idx as usize].record_time.timestamp(); datas[idx as usize].record_time.timestamp()
} }
fn _get_timestamp_subsec_nano(idx: i64, datas: &Vec<AggRecordTimeInfo>) -> u32 { fn _get_timestamp_subsec_nano(idx: i64, datas: &[AggRecordTimeInfo]) -> u32 {
return datas[idx as usize].record_time.timestamp_subsec_nanos(); datas[idx as usize].record_time.timestamp_subsec_nanos()
} }
// data[left]からdata[right-1]までのデータがtimeframeに収まっているか判定する // data[left]からdata[right-1]までのデータがtimeframeに収まっているか判定する
fn _is_in_timeframe(left: i64, right: i64, frame: i64, datas: &Vec<AggRecordTimeInfo>) -> bool { fn _is_in_timeframe(left: i64, right: i64, frame: i64, datas: &[AggRecordTimeInfo]) -> bool {
let left_time = _get_timestamp(left, datas); let left_time = _get_timestamp(left, datas);
let left_time_nano = _get_timestamp_subsec_nano(left, datas); let left_time_nano = _get_timestamp_subsec_nano(left, datas);
// evtxのSystemTimeは小数点7桁秒まで記録されているので、それを考慮する // evtxのSystemTimeは小数点7桁秒まで記録されているので、それを考慮する
@@ -477,14 +441,14 @@ fn _is_in_timeframe(left: i64, right: i64, frame: i64, datas: &Vec<AggRecordTime
if right_time_nano > left_time_nano { if right_time_nano > left_time_nano {
right_time += 1; right_time += 1;
} }
return right_time - left_time <= frame; right_time - left_time <= frame
} }
/// count済みデータ内でタイムフレーム内に存在するselectの条件を満たすレコードが、timeframe単位でcountの条件を満たしているAggResultを配列として返却する関数 /// count済みデータ内でタイムフレーム内に存在するselectの条件を満たすレコードが、timeframe単位でcountの条件を満たしているAggResultを配列として返却する関数
pub fn judge_timeframe( pub fn judge_timeframe(
rule: &RuleNode, rule: &RuleNode,
time_datas: &Vec<AggRecordTimeInfo>, time_datas: &[AggRecordTimeInfo],
key: &String, key: &str,
) -> Vec<AggResult> { ) -> Vec<AggResult> {
let mut ret: Vec<AggResult> = Vec::new(); let mut ret: Vec<AggResult> = Vec::new();
if time_datas.is_empty() { if time_datas.is_empty() {
@@ -492,12 +456,12 @@ pub fn judge_timeframe(
} }
// AggRecordTimeInfoを時間順がソートされている前提で処理を進める // AggRecordTimeInfoを時間順がソートされている前提で処理を進める
let mut datas = time_datas.clone(); let mut datas = time_datas.to_owned();
datas.sort_by(|a, b| a.record_time.cmp(&b.record_time)); datas.sort_by(|a, b| a.record_time.cmp(&b.record_time));
// timeframeの設定がルールにない時は最初と最後の要素の時間差をtimeframeに設定する。 // timeframeの設定がルールにない時は最初と最後の要素の時間差をtimeframeに設定する。
let def_frame = &datas.last().unwrap().record_time.timestamp() let def_frame = datas.last().unwrap().record_time.timestamp()
- &datas.first().unwrap().record_time.timestamp(); - datas.first().unwrap().record_time.timestamp();
let frame = get_sec_timeframe(rule).unwrap_or(def_frame); let frame = get_sec_timeframe(rule).unwrap_or(def_frame);
// left <= i < rightの範囲にあるdata[i]がtimeframe内にあるデータであると考える // left <= i < rightの範囲にあるdata[i]がtimeframe内にあるデータであると考える
@@ -510,7 +474,7 @@ pub fn judge_timeframe(
// timeframeの範囲にある限りrightをincrement // timeframeの範囲にある限りrightをincrement
while right < data_len && _is_in_timeframe(left, right, frame, &datas) { while right < data_len && _is_in_timeframe(left, right, frame, &datas) {
counter.add_data(right, &datas, rule); counter.add_data(right, &datas, rule);
right = right + 1; right += 1;
} }
let cnt = counter.count(); let cnt = counter.count();
@@ -527,7 +491,7 @@ pub fn judge_timeframe(
} }
} }
return ret; ret
} }
#[cfg(test)] #[cfg(test)]
@@ -603,7 +567,7 @@ mod tests {
)]; )];
check_count( check_count(
rule_str, rule_str,
&vec![SIMPLE_RECORD_STR.to_string(), record_str.to_string()], &[SIMPLE_RECORD_STR.to_string(), record_str.to_string()],
expected_count, expected_count,
expected_agg_result, expected_agg_result,
); );
@@ -646,24 +610,25 @@ mod tests {
"#; "#;
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 2); expected_count.insert("_".to_owned(), 2);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![
expected_agg_result.push(AggResult::new( AggResult::new(
1, 1,
"_".to_string(), "_".to_string(),
vec![], vec![],
Utc.ymd(1977, 1, 1).and_hms(0, 0, 0), Utc.ymd(1977, 1, 1).and_hms(0, 0, 0),
">= 1".to_string(), ">= 1".to_string(),
)); ),
expected_agg_result.push(AggResult::new( AggResult::new(
1, 1,
"_".to_string(), "_".to_string(),
vec![], vec![],
Utc.ymd(1996, 2, 27).and_hms(1, 5, 1), Utc.ymd(1996, 2, 27).and_hms(1, 5, 1),
">= 1".to_string(), ">= 1".to_string(),
)); ),
];
check_count( check_count(
rule_str, rule_str,
&vec![SIMPLE_RECORD_STR.to_string(), record_str.to_string()], &[SIMPLE_RECORD_STR.to_string(), record_str.to_string()],
expected_count, expected_count,
expected_agg_result, expected_agg_result,
); );
@@ -695,7 +660,7 @@ mod tests {
); );
check_count( check_count(
rule_str, rule_str,
&vec![SIMPLE_RECORD_STR.to_string()], &[SIMPLE_RECORD_STR.to_string()],
expected_count, expected_count,
vec![expected_agg_result], vec![expected_agg_result],
); );
@@ -735,24 +700,25 @@ mod tests {
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("System".to_owned(), 1); expected_count.insert("System".to_owned(), 1);
expected_count.insert("Test".to_owned(), 1); expected_count.insert("Test".to_owned(), 1);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![
expected_agg_result.push(AggResult::new( AggResult::new(
1, 1,
"System".to_owned(), "System".to_owned(),
vec!["7040".to_owned()], vec!["7040".to_owned()],
Utc.ymd(1977, 1, 1).and_hms(0, 0, 0), Utc.ymd(1977, 1, 1).and_hms(0, 0, 0),
">= 1".to_string(), ">= 1".to_string(),
)); ),
expected_agg_result.push(AggResult::new( AggResult::new(
1, 1,
"Test".to_owned(), "Test".to_owned(),
vec!["9999".to_owned()], vec!["9999".to_owned()],
Utc.ymd(1996, 2, 27).and_hms(1, 5, 1), Utc.ymd(1996, 2, 27).and_hms(1, 5, 1),
">= 1".to_string(), ">= 1".to_string(),
)); ),
];
check_count( check_count(
rule_str, rule_str,
&vec![SIMPLE_RECORD_STR.to_string(), record_str.to_string()], &[SIMPLE_RECORD_STR.to_string(), record_str.to_string()],
expected_count, expected_count,
expected_agg_result, expected_agg_result,
); );
@@ -792,24 +758,25 @@ mod tests {
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("Windows Event Log".to_owned(), 1); expected_count.insert("Windows Event Log".to_owned(), 1);
expected_count.insert("Test".to_owned(), 1); expected_count.insert("Test".to_owned(), 1);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![
expected_agg_result.push(AggResult::new( AggResult::new(
1, 1,
"Windows Event Log".to_owned(), "Windows Event Log".to_owned(),
vec!["7040".to_owned()], vec!["7040".to_owned()],
Utc.ymd(1977, 1, 1).and_hms(0, 0, 0), Utc.ymd(1977, 1, 1).and_hms(0, 0, 0),
">= 1".to_string(), ">= 1".to_string(),
)); ),
expected_agg_result.push(AggResult::new( AggResult::new(
1, 1,
"Test".to_owned(), "Test".to_owned(),
vec!["9999".to_owned()], vec!["9999".to_owned()],
Utc.ymd(1977, 1, 1).and_hms(0, 5, 0), Utc.ymd(1977, 1, 1).and_hms(0, 5, 0),
">= 1".to_string(), ">= 1".to_string(),
)); ),
];
check_count( check_count(
rule_str, rule_str,
&vec![SIMPLE_RECORD_STR.to_string(), record_str.to_string()], &[SIMPLE_RECORD_STR.to_string(), record_str.to_string()],
expected_count, expected_count,
expected_agg_result, expected_agg_result,
); );
@@ -855,14 +822,14 @@ mod tests {
let recinfo = utils::create_rec_info(rec, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(rec, "testpath".to_owned(), &keys);
let _result = rule_node.select(&recinfo); let _result = rule_node.select(&recinfo);
} }
Err(_rec) => { Err(_) => {
assert!(false, "failed to parse json record."); panic!("failed to parse json record.");
} }
} }
} }
//countupの関数が機能しているかを確認 //countupの関数が機能しているかを確認
assert_eq!( assert_eq!(
*&rule_node.countdata.get(&"_".to_owned()).unwrap().len() as i32, rule_node.countdata.get(&"_".to_owned()).unwrap().len() as i32,
2 2
); );
let judge_result = rule_node.judge_satisfy_aggcondition(); let judge_result = rule_node.judge_satisfy_aggcondition();
@@ -902,17 +869,16 @@ mod tests {
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("System".to_owned(), 2); expected_count.insert("System".to_owned(), 2);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
2, 2,
"System".to_owned(), "System".to_owned(),
vec!["7040".to_owned(), "9999".to_owned()], vec!["7040".to_owned(), "9999".to_owned()],
Utc.ymd(1977, 1, 1).and_hms(0, 0, 0), Utc.ymd(1977, 1, 1).and_hms(0, 0, 0),
">= 2".to_string(), ">= 2".to_string(),
)); )];
check_count( check_count(
rule_str, rule_str,
&vec![SIMPLE_RECORD_STR.to_string(), record_str.to_string()], &[SIMPLE_RECORD_STR.to_string(), record_str.to_string()],
expected_count, expected_count,
expected_agg_result, expected_agg_result,
); );
@@ -953,17 +919,16 @@ mod tests {
let default_time = Utc.ymd(1977, 1, 1).and_hms(0, 0, 0); let default_time = Utc.ymd(1977, 1, 1).and_hms(0, 0, 0);
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("System".to_owned(), 2); expected_count.insert("System".to_owned(), 2);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
2, 2,
"System".to_owned(), "System".to_owned(),
vec!["7040".to_owned(), "9999".to_owned()], vec!["7040".to_owned(), "9999".to_owned()],
default_time, default_time,
">= 1".to_string(), ">= 1".to_string(),
)); )];
check_count( check_count(
rule_str, rule_str,
&vec![SIMPLE_RECORD_STR.to_string(), record_str.to_string()], &[SIMPLE_RECORD_STR.to_string(), record_str.to_string()],
expected_count, expected_count,
expected_agg_result, expected_agg_result,
); );
@@ -984,14 +949,13 @@ mod tests {
let default_time = Utc.ymd(1977, 1, 9).and_hms(0, 30, 0); let default_time = Utc.ymd(1977, 1, 9).and_hms(0, 30, 0);
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 3); expected_count.insert("_".to_owned(), 3);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
3, 3,
"_".to_owned(), "_".to_owned(),
vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], vec!["1".to_owned(), "2".to_owned(), "3".to_owned()],
default_time, default_time,
">= 3".to_string(), ">= 3".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1019,14 +983,13 @@ mod tests {
let default_time = Utc.ymd(1977, 1, 9).and_hms(0, 30, 0); let default_time = Utc.ymd(1977, 1, 9).and_hms(0, 30, 0);
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 3); expected_count.insert("_".to_owned(), 3);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
3, 3,
"_".to_owned(), "_".to_owned(),
vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], vec!["1".to_owned(), "2".to_owned(), "3".to_owned()],
default_time, default_time,
">= 3".to_string(), ">= 3".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1054,14 +1017,13 @@ mod tests {
let default_time = Utc.ymd(1977, 1, 9).and_hms(0, 30, 0); let default_time = Utc.ymd(1977, 1, 9).and_hms(0, 30, 0);
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 3); expected_count.insert("_".to_owned(), 3);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
3, 3,
"_".to_owned(), "_".to_owned(),
vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], vec!["1".to_owned(), "2".to_owned(), "3".to_owned()],
default_time, default_time,
">= 3".to_string(), ">= 3".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1071,14 +1033,13 @@ mod tests {
let default_time = Utc.ymd(1977, 1, 9).and_hms(0, 30, 0); let default_time = Utc.ymd(1977, 1, 9).and_hms(0, 30, 0);
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 3); expected_count.insert("_".to_owned(), 3);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
3, 3,
"_".to_owned(), "_".to_owned(),
vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], vec!["1".to_owned(), "2".to_owned(), "3".to_owned()],
default_time, default_time,
">= 3".to_string(), ">= 3".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1096,14 +1057,13 @@ mod tests {
let default_time = Utc.ymd(1977, 1, 9).and_hms(0, 30, 0); let default_time = Utc.ymd(1977, 1, 9).and_hms(0, 30, 0);
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 3); expected_count.insert("_".to_owned(), 3);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
3, 3,
"_".to_owned(), "_".to_owned(),
vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], vec!["1".to_owned(), "2".to_owned(), "3".to_owned()],
default_time, default_time,
">= 3".to_string(), ">= 3".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1131,14 +1091,13 @@ mod tests {
let default_time = Utc.ymd(1977, 1, 9).and_hms(0, 30, 0); let default_time = Utc.ymd(1977, 1, 9).and_hms(0, 30, 0);
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 3); expected_count.insert("_".to_owned(), 3);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
3, 3,
"_".to_owned(), "_".to_owned(),
vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], vec!["1".to_owned(), "2".to_owned(), "3".to_owned()],
default_time, default_time,
">= 3".to_string(), ">= 3".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1166,14 +1125,13 @@ mod tests {
let default_time = Utc.ymd(2021, 12, 21).and_hms(10, 40, 0); let default_time = Utc.ymd(2021, 12, 21).and_hms(10, 40, 0);
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 3); expected_count.insert("_".to_owned(), 3);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
3, 3,
"_".to_owned(), "_".to_owned(),
vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], vec!["1".to_owned(), "2".to_owned(), "3".to_owned()],
default_time, default_time,
">= 3".to_string(), ">= 3".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1201,14 +1159,13 @@ mod tests {
let default_time = Utc.ymd(2021, 12, 21).and_hms_milli(10, 40, 0, 50); let default_time = Utc.ymd(2021, 12, 21).and_hms_milli(10, 40, 0, 50);
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 3); expected_count.insert("_".to_owned(), 3);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
3, 3,
"_".to_owned(), "_".to_owned(),
vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], vec!["1".to_owned(), "2".to_owned(), "3".to_owned()],
default_time, default_time,
">= 3".to_string(), ">= 3".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1236,14 +1193,13 @@ mod tests {
let default_time = Utc.ymd(2021, 12, 21).and_hms_milli(10, 40, 0, 50); let default_time = Utc.ymd(2021, 12, 21).and_hms_milli(10, 40, 0, 50);
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 3); expected_count.insert("_".to_owned(), 3);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
3, 3,
"_".to_owned(), "_".to_owned(),
vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], vec!["1".to_owned(), "2".to_owned(), "3".to_owned()],
default_time, default_time,
">= 3".to_string(), ">= 3".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1279,14 +1235,13 @@ mod tests {
let default_time = Utc.ymd(2021, 12, 21).and_hms_milli(10, 40, 0, 0); let default_time = Utc.ymd(2021, 12, 21).and_hms_milli(10, 40, 0, 0);
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 1); expected_count.insert("_".to_owned(), 1);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
1, 1,
"_".to_owned(), "_".to_owned(),
vec!["1".to_owned()], vec!["1".to_owned()],
default_time, default_time,
">= 1".to_string(), ">= 1".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1296,14 +1251,13 @@ mod tests {
let default_time = Utc.ymd(2021, 12, 21).and_hms_milli(10, 40, 0, 0); let default_time = Utc.ymd(2021, 12, 21).and_hms_milli(10, 40, 0, 0);
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("Windows Event Log".to_owned(), 1); expected_count.insert("Windows Event Log".to_owned(), 1);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
1, 1,
"Windows Event Log".to_owned(), "Windows Event Log".to_owned(),
vec!["1".to_owned()], vec!["1".to_owned()],
default_time, default_time,
">= 1".to_string(), ">= 1".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
} }
@@ -1335,14 +1289,13 @@ mod tests {
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 7); expected_count.insert("_".to_owned(), 7);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
3, 3,
"_".to_owned(), "_".to_owned(),
vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], vec!["1".to_owned(), "2".to_owned(), "3".to_owned()],
Utc.ymd(1977, 1, 9).and_hms(1, 30, 0), Utc.ymd(1977, 1, 9).and_hms(1, 30, 0),
">= 3".to_string(), ">= 3".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1382,8 +1335,7 @@ mod tests {
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 4); expected_count.insert("_".to_owned(), 4);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
4, 4,
"_".to_owned(), "_".to_owned(),
vec![ vec![
@@ -1394,7 +1346,7 @@ mod tests {
], ],
Utc.ymd(1977, 1, 9).and_hms(1, 30, 0), Utc.ymd(1977, 1, 9).and_hms(1, 30, 0),
">= 4".to_string(), ">= 4".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1416,14 +1368,13 @@ mod tests {
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 3); expected_count.insert("_".to_owned(), 3);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![AggResult::new(
expected_agg_result.push(AggResult::new(
3, 3,
"_".to_owned(), "_".to_owned(),
vec!["1".to_owned(), "2".to_owned(), "3".to_owned()], vec!["1".to_owned(), "2".to_owned(), "3".to_owned()],
Utc.ymd(1977, 1, 9).and_hms(1, 30, 0), Utc.ymd(1977, 1, 9).and_hms(1, 30, 0),
">= 3".to_string(), ">= 3".to_string(),
)); )];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1461,8 +1412,8 @@ mod tests {
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), recs.len() as i32); expected_count.insert("_".to_owned(), recs.len() as i32);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![
expected_agg_result.push(AggResult::new( AggResult::new(
4, 4,
"_".to_owned(), "_".to_owned(),
vec![ vec![
@@ -1473,8 +1424,8 @@ mod tests {
], ],
Utc.ymd(1977, 1, 9).and_hms(1, 30, 0), Utc.ymd(1977, 1, 9).and_hms(1, 30, 0),
">= 4".to_string(), ">= 4".to_string(),
)); ),
expected_agg_result.push(AggResult::new( AggResult::new(
4, 4,
"_".to_owned(), "_".to_owned(),
vec![ vec![
@@ -1485,8 +1436,8 @@ mod tests {
], ],
Utc.ymd(1977, 1, 9).and_hms(5, 30, 0), Utc.ymd(1977, 1, 9).and_hms(5, 30, 0),
">= 4".to_string(), ">= 4".to_string(),
)); ),
expected_agg_result.push(AggResult::new( AggResult::new(
4, 4,
"_".to_owned(), "_".to_owned(),
vec![ vec![
@@ -1497,7 +1448,8 @@ mod tests {
], ],
Utc.ymd(1977, 1, 9).and_hms(9, 30, 0), Utc.ymd(1977, 1, 9).and_hms(9, 30, 0),
">= 4".to_string(), ">= 4".to_string(),
)); ),
];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
@@ -1527,16 +1479,15 @@ mod tests {
let mut expected_count = HashMap::new(); let mut expected_count = HashMap::new();
expected_count.insert("_".to_owned(), 11); expected_count.insert("_".to_owned(), 11);
let mut expected_agg_result: Vec<AggResult> = Vec::new(); let expected_agg_result: Vec<AggResult> = vec![
expected_agg_result.push(AggResult::new( AggResult::new(
3, 3,
"_".to_owned(), "_".to_owned(),
vec!["2".to_owned(), "3".to_owned(), "4".to_owned()], vec!["2".to_owned(), "3".to_owned(), "4".to_owned()],
Utc.ymd(1977, 1, 9).and_hms(3, 30, 0), Utc.ymd(1977, 1, 9).and_hms(3, 30, 0),
">= 3".to_string(), ">= 3".to_string(),
)); ),
AggResult::new(
expected_agg_result.push(AggResult::new(
4, 4,
"_".to_owned(), "_".to_owned(),
vec![ vec![
@@ -1547,12 +1498,13 @@ mod tests {
], ],
Utc.ymd(1977, 1, 9).and_hms(20, 00, 0), Utc.ymd(1977, 1, 9).and_hms(20, 00, 0),
">= 3".to_string(), ">= 3".to_string(),
)); ),
];
check_count(&rule_str, &recs, expected_count, expected_agg_result); check_count(&rule_str, &recs, expected_count, expected_agg_result);
} }
fn test_create_recstr_std(event_id: &str, time: &str) -> String { fn test_create_recstr_std(event_id: &str, time: &str) -> String {
return test_create_recstr(event_id, time, "Windows Event Log"); test_create_recstr(event_id, time, "Windows Event Log")
} }
fn test_create_recstr(event_id: &str, time: &str, param1: &str) -> String { fn test_create_recstr(event_id: &str, time: &str, param1: &str) -> String {
@@ -1570,10 +1522,10 @@ mod tests {
} }
} }
}"#; }"#;
return template template
.replace("${EVENT_ID}", event_id) .replace("${EVENT_ID}", event_id)
.replace("${TIME}", time) .replace("${TIME}", time)
.replace("${PARAM1}", param1); .replace("${PARAM1}", param1)
} }
fn create_std_rule(count: &str, timeframe: &str) -> String { fn create_std_rule(count: &str, timeframe: &str) -> String {
@@ -1586,15 +1538,15 @@ mod tests {
timeframe: ${TIME_FRAME} timeframe: ${TIME_FRAME}
details: 'Service name : %param1%¥nMessage : Event Log Service Stopped¥nResults: Selective event log manipulation may follow this event.' details: 'Service name : %param1%¥nMessage : Event Log Service Stopped¥nResults: Selective event log manipulation may follow this event.'
"#; "#;
return template template
.replace("${COUNT}", count) .replace("${COUNT}", count)
.replace("${TIME_FRAME}", timeframe); .replace("${TIME_FRAME}", timeframe)
} }
/// countで対象の数値確認を行うためのテスト用関数 /// countで対象の数値確認を行うためのテスト用関数
fn check_count( fn check_count(
rule_str: &str, rule_str: &str,
records_str: &Vec<String>, records_str: &[String],
expected_counts: HashMap<String, i32>, expected_counts: HashMap<String, i32>,
expect_agg_results: Vec<AggResult>, expect_agg_results: Vec<AggResult>,
) { ) {
@@ -1603,10 +1555,10 @@ mod tests {
let mut rule_node = create_rule("testpath".to_string(), test); let mut rule_node = create_rule("testpath".to_string(), test);
let error_checker = rule_node.init(); let error_checker = rule_node.init();
if error_checker.is_err() { if error_checker.is_err() {
assert!(false, "Failed to init rulenode"); panic!("Failed to init rulenode");
} }
for record_str in records_str { for record_str in records_str {
match serde_json::from_str(&record_str) { match serde_json::from_str(record_str) {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
@@ -1614,7 +1566,7 @@ mod tests {
assert_eq!(result, &true); assert_eq!(result, &true);
} }
Err(_rec) => { Err(_rec) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1630,7 +1582,7 @@ mod tests {
let expect_count = expected_counts.get(&expect_agg.key).unwrap_or(&-1); let expect_count = expected_counts.get(&expect_agg.key).unwrap_or(&-1);
//countupの関数が機能しているかを確認 //countupの関数が機能しているかを確認
assert_eq!( assert_eq!(
*&rule_node.countdata.get(&expect_agg.key).unwrap().len() as i32, rule_node.countdata.get(&expect_agg.key).unwrap().len() as i32,
*expect_count *expect_count
); );
expect_data.push(expect_agg.data); expect_data.push(expect_agg.data);
@@ -1651,7 +1603,7 @@ mod tests {
for expect_field_value in &expect_field_values[index] { for expect_field_value in &expect_field_values[index] {
// テストによってはtimeframeの値と各fieldの値で配列の順番が想定したものと変化してしまう可能性があるため配列の長さを確認したうえで期待した各要素が存在するかを確認する。 // テストによってはtimeframeの値と各fieldの値で配列の順番が想定したものと変化してしまう可能性があるため配列の長さを確認したうえで期待した各要素が存在するかを確認する。
// field`要素の順番については以降の処理で関連しない // field`要素の順番については以降の処理で関連しない
assert!(agg_result.field_values.contains(&expect_field_value)); assert!(agg_result.field_values.contains(expect_field_value));
} }
assert_eq!(agg_result.condition_op_num, expect_condition_op_num[index]); assert_eq!(agg_result.condition_op_num, expect_condition_op_num[index]);
} }

View File

@@ -17,7 +17,7 @@ lazy_static! {
// LeafSelectionNodeのget_matchersクラスの戻り値の配列に新規作成したクラスのインスタンスを追加する。 // LeafSelectionNodeのget_matchersクラスの戻り値の配列に新規作成したクラスのインスタンスを追加する。
pub trait LeafMatcher: mopa::Any { pub trait LeafMatcher: mopa::Any {
/// 指定されたkey_listにマッチするLeafMatcherであるかどうか判定する。 /// 指定されたkey_listにマッチするLeafMatcherであるかどうか判定する。
fn is_target_key(&self, key_list: &Vec<String>) -> bool; fn is_target_key(&self, key_list: &[String]) -> bool;
/// 引数に指定されたJSON形式のデータがマッチするかどうか判定する。 /// 引数に指定されたJSON形式のデータがマッチするかどうか判定する。
/// main.rsでWindows Event LogをJSON形式に変換していて、そのJSON形式のWindowsのイベントログデータがここには来る /// main.rsでWindows Event LogをJSON形式に変換していて、そのJSON形式のWindowsのイベントログデータがここには来る
@@ -26,7 +26,7 @@ pub trait LeafMatcher: mopa::Any {
/// 初期化ロジックをここに記載します。 /// 初期化ロジックをここに記載します。
/// ルールファイルの書き方が間違っている等の原因により、正しくルールファイルからパースできない場合、戻り値のResult型でエラーを返してください。 /// ルールファイルの書き方が間違っている等の原因により、正しくルールファイルからパースできない場合、戻り値のResult型でエラーを返してください。
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>>; fn init(&mut self, key_list: &[String], select_value: &Yaml) -> Result<(), Vec<String>>;
} }
mopafy!(LeafMatcher); mopafy!(LeafMatcher);
@@ -37,12 +37,12 @@ pub struct MinlengthMatcher {
impl MinlengthMatcher { impl MinlengthMatcher {
pub fn new() -> MinlengthMatcher { pub fn new() -> MinlengthMatcher {
return MinlengthMatcher { min_len: 0 }; MinlengthMatcher { min_len: 0 }
} }
} }
impl LeafMatcher for MinlengthMatcher { impl LeafMatcher for MinlengthMatcher {
fn is_target_key(&self, key_list: &Vec<String>) -> bool { fn is_target_key(&self, key_list: &[String]) -> bool {
if key_list.len() != 2 { if key_list.len() != 2 {
return false; return false;
} }
@@ -50,7 +50,7 @@ impl LeafMatcher for MinlengthMatcher {
return key_list.get(1).unwrap() == "min_length"; return key_list.get(1).unwrap() == "min_length";
} }
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>> { fn init(&mut self, key_list: &[String], select_value: &Yaml) -> Result<(), Vec<String>> {
let min_length = select_value.as_i64(); let min_length = select_value.as_i64();
if min_length.is_none() { if min_length.is_none() {
let errmsg = format!( let errmsg = format!(
@@ -61,14 +61,14 @@ impl LeafMatcher for MinlengthMatcher {
} }
self.min_len = min_length.unwrap(); self.min_len = min_length.unwrap();
return Result::Ok(()); Result::Ok(())
} }
fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool { fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool {
return match event_value { match event_value {
Some(s) => s.len() as i64 >= self.min_len, Some(s) => s.len() as i64 >= self.min_len,
None => false, None => false,
}; }
} }
} }
@@ -80,20 +80,20 @@ pub struct RegexesFileMatcher {
impl RegexesFileMatcher { impl RegexesFileMatcher {
pub fn new() -> RegexesFileMatcher { pub fn new() -> RegexesFileMatcher {
return RegexesFileMatcher { regexes: vec![] }; RegexesFileMatcher { regexes: vec![] }
} }
} }
impl LeafMatcher for RegexesFileMatcher { impl LeafMatcher for RegexesFileMatcher {
fn is_target_key(&self, key_list: &Vec<String>) -> bool { fn is_target_key(&self, key_list: &[String]) -> bool {
if key_list.len() != 2 { if key_list.len() != 2 {
return false; return false;
} }
return key_list.get(1).unwrap() == "regexes"; key_list.get(1).unwrap() == "regexes"
} }
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>> { fn init(&mut self, key_list: &[String], select_value: &Yaml) -> Result<(), Vec<String>> {
let value = match select_value { let value = match select_value {
Yaml::String(s) => Option::Some(s.to_owned()), Yaml::String(s) => Option::Some(s.to_owned()),
Yaml::Integer(i) => Option::Some(i.to_string()), Yaml::Integer(i) => Option::Some(i.to_string()),
@@ -118,14 +118,14 @@ impl LeafMatcher for RegexesFileMatcher {
.map(|regex_str| Regex::new(&regex_str).unwrap()) .map(|regex_str| Regex::new(&regex_str).unwrap())
.collect(); .collect();
return Result::Ok(()); Result::Ok(())
} }
fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool { fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool {
return match event_value { match event_value {
Some(s) => utils::check_regex(s, &self.regexes), Some(s) => utils::check_regex(s, &self.regexes),
None => false, None => false,
}; }
} }
} }
@@ -137,12 +137,12 @@ pub struct AllowlistFileMatcher {
impl AllowlistFileMatcher { impl AllowlistFileMatcher {
pub fn new() -> AllowlistFileMatcher { pub fn new() -> AllowlistFileMatcher {
return AllowlistFileMatcher { regexes: vec![] }; AllowlistFileMatcher { regexes: vec![] }
} }
} }
impl LeafMatcher for AllowlistFileMatcher { impl LeafMatcher for AllowlistFileMatcher {
fn is_target_key(&self, key_list: &Vec<String>) -> bool { fn is_target_key(&self, key_list: &[String]) -> bool {
if key_list.len() != 2 { if key_list.len() != 2 {
return false; return false;
} }
@@ -150,7 +150,7 @@ impl LeafMatcher for AllowlistFileMatcher {
return key_list.get(1).unwrap() == "allowlist"; return key_list.get(1).unwrap() == "allowlist";
} }
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>> { fn init(&mut self, key_list: &[String], select_value: &Yaml) -> Result<(), Vec<String>> {
let value = match select_value { let value = match select_value {
Yaml::String(s) => Option::Some(s.to_owned()), Yaml::String(s) => Option::Some(s.to_owned()),
Yaml::Integer(i) => Option::Some(i.to_string()), Yaml::Integer(i) => Option::Some(i.to_string()),
@@ -175,14 +175,14 @@ impl LeafMatcher for AllowlistFileMatcher {
.map(|regex_str| Regex::new(&regex_str).unwrap()) .map(|regex_str| Regex::new(&regex_str).unwrap())
.collect(); .collect();
return Result::Ok(()); Result::Ok(())
} }
fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool { fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool {
return match event_value { match event_value {
Some(s) => !utils::check_allowlist(s, &self.regexes), Some(s) => !utils::check_allowlist(s, &self.regexes),
None => true, None => true,
}; }
} }
} }
@@ -196,39 +196,34 @@ pub struct DefaultMatcher {
impl DefaultMatcher { impl DefaultMatcher {
pub fn new() -> DefaultMatcher { pub fn new() -> DefaultMatcher {
return DefaultMatcher { DefaultMatcher {
re: Option::None, re: Option::None,
pipes: Vec::new(), pipes: Vec::new(),
key_list: Vec::new(), key_list: Vec::new(),
}; }
} }
/// このmatcherの正規表現とマッチするかどうか判定します。 /// このmatcherの正規表現とマッチするかどうか判定します。
/// 判定対象の文字列とこのmatcherが保持する正規表現が完全にマッチした場合のTRUEを返します。 /// 判定対象の文字列とこのmatcherが保持する正規表現が完全にマッチした場合のTRUEを返します。
/// 例えば、判定対象文字列が"abc"で、正規表現が"ab"の場合、正規表現は判定対象文字列の一部分にしか一致していないので、この関数はfalseを返します。 /// 例えば、判定対象文字列が"abc"で、正規表現が"ab"の場合、正規表現は判定対象文字列の一部分にしか一致していないので、この関数はfalseを返します。
fn is_regex_fullmatch(&self, value: &String) -> bool { fn is_regex_fullmatch(&self, value: &str) -> bool {
return self return self.re.as_ref().unwrap().find_iter(value).any(|match_obj| {
.re
.as_ref()
.unwrap()
.find_iter(&value)
.any(|match_obj| {
return match_obj.as_str() == value; return match_obj.as_str() == value;
}); });
} }
/// YEAのルールファイルのフィールド名とそれに続いて指定されるパイプを、正規表現形式の文字列に変換します。 /// YEAのルールファイルのフィールド名とそれに続いて指定されるパイプを、正規表現形式の文字列に変換します。
/// ワイルドカードの文字列を正規表現にする処理もこのメソッドに実装されています。patternにワイルドカードの文字列を指定して、pipesにPipeElement::Wildcardを指定すればOK!! /// ワイルドカードの文字列を正規表現にする処理もこのメソッドに実装されています。patternにワイルドカードの文字列を指定して、pipesにPipeElement::Wildcardを指定すればOK!!
fn from_pattern_to_regex_str(pattern: String, pipes: &Vec<PipeElement>) -> String { fn from_pattern_to_regex_str(pattern: String, pipes: &[PipeElement]) -> String {
// パターンをPipeで処理する。 // パターンをPipeで処理する。
return pipes.iter().fold(pattern, |acc, pipe| { pipes
return pipe.pipe_pattern(acc); .iter()
}); .fold(pattern, |acc, pipe| pipe.pipe_pattern(acc))
} }
} }
impl LeafMatcher for DefaultMatcher { impl LeafMatcher for DefaultMatcher {
fn is_target_key(&self, key_list: &Vec<String>) -> bool { fn is_target_key(&self, key_list: &[String]) -> bool {
if key_list.len() <= 1 { if key_list.len() <= 1 {
return true; return true;
} }
@@ -236,7 +231,7 @@ impl LeafMatcher for DefaultMatcher {
return key_list.get(1).unwrap_or(&"".to_string()) == "value"; return key_list.get(1).unwrap_or(&"".to_string()) == "value";
} }
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>> { fn init(&mut self, key_list: &[String], select_value: &Yaml) -> Result<(), Vec<String>> {
self.key_list = key_list.to_vec(); self.key_list = key_list.to_vec();
if select_value.is_null() { if select_value.is_null() {
return Result::Ok(()); return Result::Ok(());
@@ -261,7 +256,7 @@ impl LeafMatcher for DefaultMatcher {
// Pipeが指定されていればパースする // Pipeが指定されていればパースする
let emp = String::default(); let emp = String::default();
let mut keys: VecDeque<&str> = key_list.get(0).unwrap_or(&emp).split("|").collect(); // key_listが空はあり得ない let mut keys: VecDeque<&str> = key_list.get(0).unwrap_or(&emp).split('|').collect(); // key_listが空はあり得ない
keys.pop_front(); // 一つ目はただのキーで、2つめ以降がpipe keys.pop_front(); // 一つ目はただのキーで、2つめ以降がpipe
while !keys.is_empty() { while !keys.is_empty() {
let key = keys.pop_front().unwrap(); let key = keys.pop_front().unwrap();
@@ -290,12 +285,10 @@ impl LeafMatcher for DefaultMatcher {
); );
return Result::Err(vec![errmsg]); return Result::Err(vec![errmsg]);
} }
let is_re = &self.pipes.iter().any(|pipe_element| { let is_re = &self
return match pipe_element { .pipes
PipeElement::Re => true, .iter()
_ => false, .any(|pipe_element| matches!(pipe_element, PipeElement::Re));
};
});
// 正規表現ではない場合、ワイルドカードであることを表す。 // 正規表現ではない場合、ワイルドカードであることを表す。
// ワイルドカードは正規表現でマッチングするので、ワイルドカードを正規表現に変換するPipeを内部的に追加することにする。 // ワイルドカードは正規表現でマッチングするので、ワイルドカードを正規表現に変換するPipeを内部的に追加することにする。
if !is_re { if !is_re {
@@ -316,7 +309,7 @@ impl LeafMatcher for DefaultMatcher {
} }
self.re = re_result.ok(); self.re = re_result.ok();
return Result::Ok(()); Result::Ok(())
} }
fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool { fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool {
@@ -333,10 +326,10 @@ impl LeafMatcher for DefaultMatcher {
let event_value_str = event_value.unwrap(); let event_value_str = event_value.unwrap();
if self.key_list.is_empty() { if self.key_list.is_empty() {
// この場合ただのgrep検索なので、ただ正規表現に一致するかどうか調べればよいだけ // この場合ただのgrep検索なので、ただ正規表現に一致するかどうか調べればよいだけ
return self.re.as_ref().unwrap().is_match(&event_value_str); return self.re.as_ref().unwrap().is_match(event_value_str);
} else { } else {
// 通常の検索はこっち // 通常の検索はこっち
return self.is_regex_fullmatch(&event_value_str); self.is_regex_fullmatch(event_value_str)
} }
} }
} }
@@ -356,28 +349,28 @@ impl PipeElement {
// enumでポリモーフィズムを実装すると、一つのメソッドに全部の型の実装をする感じになる。Java使い的にはキモイ感じがする。 // enumでポリモーフィズムを実装すると、一つのメソッドに全部の型の実装をする感じになる。Java使い的にはキモイ感じがする。
let fn_add_asterisk_end = |patt: String| { let fn_add_asterisk_end = |patt: String| {
if patt.ends_with("//*") { if patt.ends_with("//*") {
return patt; patt
} else if patt.ends_with("/*") { } else if patt.ends_with("/*") {
return patt + "*"; patt + "*"
} else if patt.ends_with("*") { } else if patt.ends_with('*') {
return patt; patt
} else { } else {
return patt + "*"; patt + "*"
} }
}; };
let fn_add_asterisk_begin = |patt: String| { let fn_add_asterisk_begin = |patt: String| {
if patt.starts_with("//*") { if patt.starts_with("//*") {
return patt; patt
} else if patt.starts_with("/*") { } else if patt.starts_with("/*") {
return "*".to_string() + &patt; "*".to_string() + &patt
} else if patt.starts_with("*") { } else if patt.starts_with('*') {
return patt; patt
} else { } else {
return "*".to_string() + &patt; "*".to_string() + &patt
} }
}; };
let val: String = match self { match self {
// startswithの場合はpatternの最後にwildcardを足すことで対応する // startswithの場合はpatternの最後にwildcardを足すことで対応する
PipeElement::Startswith => fn_add_asterisk_end(pattern), PipeElement::Startswith => fn_add_asterisk_end(pattern),
// endswithの場合はpatternの最初にwildcardを足すことで対応する // endswithの場合はpatternの最初にwildcardを足すことで対応する
@@ -388,8 +381,7 @@ impl PipeElement {
PipeElement::Re => pattern, PipeElement::Re => pattern,
// WildCardは正規表現に変換する。 // WildCardは正規表現に変換する。
PipeElement::Wildcard => PipeElement::pipe_pattern_wildcard(pattern), PipeElement::Wildcard => PipeElement::pipe_pattern_wildcard(pattern),
}; }
return val;
} }
/// PipeElement::Wildcardのパイプ処理です。 /// PipeElement::Wildcardのパイプ処理です。
@@ -456,11 +448,7 @@ impl PipeElement {
regex::escape(pattern) regex::escape(pattern)
} else { } else {
// wildcardの場合、"*"は".*"という正規表現に変換し、"?"は"."に変換する。 // wildcardの場合、"*"は".*"という正規表現に変換し、"?"は"."に変換する。
let wildcard_regex_value = if pattern.to_string() == "*" { let wildcard_regex_value = if *pattern == "*" { ".*" } else { "." };
".*"
} else {
"."
};
wildcard_regex_value.to_string() wildcard_regex_value.to_string()
}; };
@@ -470,7 +458,7 @@ impl PipeElement {
// sigmaのwildcardはcase insensitive // sigmaのwildcardはcase insensitive
// なので、正規表現の先頭にcase insensitiveであることを表す記号を付与 // なので、正規表現の先頭にcase insensitiveであることを表す記号を付与
return "(?i)".to_string() + &ret; "(?i)".to_string() + &ret
} }
} }
@@ -523,20 +511,20 @@ mod tests {
// Channel // Channel
{ {
// LeafSelectionNodeが正しく読み込めることを確認 // LeafSelectionNodeが正しく読み込めることを確認
let child_node = detection_childs[0].as_ref() as &dyn SelectionNode; // TODO キャストしないとエラーでるけど、このキャストよく分からん。 let child_node = detection_childs[0] as &dyn SelectionNode; // TODO キャストしないとエラーでるけど、このキャストよく分からん。
assert_eq!(child_node.is::<LeafSelectionNode>(), true); assert!(child_node.is::<LeafSelectionNode>());
let child_node = child_node.downcast_ref::<LeafSelectionNode>().unwrap(); let child_node = child_node.downcast_ref::<LeafSelectionNode>().unwrap();
assert_eq!(child_node.get_key(), "Channel"); assert_eq!(child_node.get_key(), "Channel");
assert_eq!(child_node.get_childs().len(), 0); assert_eq!(child_node.get_childs().len(), 0);
// 比較する正規表現が正しいことを確認 // 比較する正規表現が正しいことを確認
let matcher = &child_node.matcher; let matcher = &child_node.matcher;
assert_eq!(matcher.is_some(), true); assert!(matcher.is_some());
let matcher = child_node.matcher.as_ref().unwrap(); let matcher = child_node.matcher.as_ref().unwrap();
assert_eq!(matcher.is::<DefaultMatcher>(), true); assert!(matcher.is::<DefaultMatcher>());
let matcher = matcher.downcast_ref::<DefaultMatcher>().unwrap(); let matcher = matcher.downcast_ref::<DefaultMatcher>().unwrap();
assert_eq!(matcher.re.is_some(), true); assert!(matcher.re.is_some());
let re = matcher.re.as_ref(); let re = matcher.re.as_ref();
assert_eq!( assert_eq!(
re.unwrap().as_str(), re.unwrap().as_str(),
@@ -547,20 +535,20 @@ mod tests {
// EventID // EventID
{ {
// LeafSelectionNodeが正しく読み込めることを確認 // LeafSelectionNodeが正しく読み込めることを確認
let child_node = detection_childs[1].as_ref() as &dyn SelectionNode; let child_node = detection_childs[1] as &dyn SelectionNode;
assert_eq!(child_node.is::<LeafSelectionNode>(), true); assert!(child_node.is::<LeafSelectionNode>());
let child_node = child_node.downcast_ref::<LeafSelectionNode>().unwrap(); let child_node = child_node.downcast_ref::<LeafSelectionNode>().unwrap();
assert_eq!(child_node.get_key(), "EventID"); assert_eq!(child_node.get_key(), "EventID");
assert_eq!(child_node.get_childs().len(), 0); assert_eq!(child_node.get_childs().len(), 0);
// 比較する正規表現が正しいことを確認 // 比較する正規表現が正しいことを確認
let matcher = &child_node.matcher; let matcher = &child_node.matcher;
assert_eq!(matcher.is_some(), true); assert!(matcher.is_some());
let matcher = child_node.matcher.as_ref().unwrap(); let matcher = child_node.matcher.as_ref().unwrap();
assert_eq!(matcher.is::<DefaultMatcher>(), true); assert!(matcher.is::<DefaultMatcher>());
let matcher = matcher.downcast_ref::<DefaultMatcher>().unwrap(); let matcher = matcher.downcast_ref::<DefaultMatcher>().unwrap();
assert_eq!(matcher.re.is_some(), true); assert!(matcher.re.is_some());
let re = matcher.re.as_ref(); let re = matcher.re.as_ref();
assert_eq!(re.unwrap().as_str(), "(?i)4103"); assert_eq!(re.unwrap().as_str(), "(?i)4103");
} }
@@ -568,38 +556,38 @@ mod tests {
// ContextInfo // ContextInfo
{ {
// OrSelectionNodeを正しく読み込めることを確認 // OrSelectionNodeを正しく読み込めることを確認
let child_node = detection_childs[2].as_ref() as &dyn SelectionNode; let child_node = detection_childs[2] as &dyn SelectionNode;
assert_eq!(child_node.is::<OrSelectionNode>(), true); assert!(child_node.is::<OrSelectionNode>());
let child_node = child_node.downcast_ref::<OrSelectionNode>().unwrap(); let child_node = child_node.downcast_ref::<OrSelectionNode>().unwrap();
let ancestors = child_node.get_childs(); let ancestors = child_node.get_childs();
assert_eq!(ancestors.len(), 2); assert_eq!(ancestors.len(), 2);
// OrSelectionNodeの下にLeafSelectionNodeがあるパターンをテスト // OrSelectionNodeの下にLeafSelectionNodeがあるパターンをテスト
// LeafSelectionNodeである、Host Applicationードが正しいことを確認 // LeafSelectionNodeである、Host Applicationードが正しいことを確認
let hostapp_en_node = ancestors[0].as_ref() as &dyn SelectionNode; let hostapp_en_node = ancestors[0] as &dyn SelectionNode;
assert_eq!(hostapp_en_node.is::<LeafSelectionNode>(), true); assert!(hostapp_en_node.is::<LeafSelectionNode>());
let hostapp_en_node = hostapp_en_node.downcast_ref::<LeafSelectionNode>().unwrap(); let hostapp_en_node = hostapp_en_node.downcast_ref::<LeafSelectionNode>().unwrap();
let hostapp_en_matcher = &hostapp_en_node.matcher; let hostapp_en_matcher = &hostapp_en_node.matcher;
assert_eq!(hostapp_en_matcher.is_some(), true); assert!(hostapp_en_matcher.is_some());
let hostapp_en_matcher = hostapp_en_matcher.as_ref().unwrap(); let hostapp_en_matcher = hostapp_en_matcher.as_ref().unwrap();
assert_eq!(hostapp_en_matcher.is::<DefaultMatcher>(), true); assert!(hostapp_en_matcher.is::<DefaultMatcher>());
let hostapp_en_matcher = hostapp_en_matcher.downcast_ref::<DefaultMatcher>().unwrap(); let hostapp_en_matcher = hostapp_en_matcher.downcast_ref::<DefaultMatcher>().unwrap();
assert_eq!(hostapp_en_matcher.re.is_some(), true); assert!(hostapp_en_matcher.re.is_some());
let re = hostapp_en_matcher.re.as_ref(); let re = hostapp_en_matcher.re.as_ref();
assert_eq!(re.unwrap().as_str(), "(?i)Host Application"); assert_eq!(re.unwrap().as_str(), "(?i)Host Application");
// LeafSelectionNodeである、ホスト アプリケーションノードが正しいことを確認 // LeafSelectionNodeである、ホスト アプリケーションノードが正しいことを確認
let hostapp_jp_node = ancestors[1].as_ref() as &dyn SelectionNode; let hostapp_jp_node = ancestors[1] as &dyn SelectionNode;
assert_eq!(hostapp_jp_node.is::<LeafSelectionNode>(), true); assert!(hostapp_jp_node.is::<LeafSelectionNode>());
let hostapp_jp_node = hostapp_jp_node.downcast_ref::<LeafSelectionNode>().unwrap(); let hostapp_jp_node = hostapp_jp_node.downcast_ref::<LeafSelectionNode>().unwrap();
let hostapp_jp_matcher = &hostapp_jp_node.matcher; let hostapp_jp_matcher = &hostapp_jp_node.matcher;
assert_eq!(hostapp_jp_matcher.is_some(), true); assert!(hostapp_jp_matcher.is_some());
let hostapp_jp_matcher = hostapp_jp_matcher.as_ref().unwrap(); let hostapp_jp_matcher = hostapp_jp_matcher.as_ref().unwrap();
assert_eq!(hostapp_jp_matcher.is::<DefaultMatcher>(), true); assert!(hostapp_jp_matcher.is::<DefaultMatcher>());
let hostapp_jp_matcher = hostapp_jp_matcher.downcast_ref::<DefaultMatcher>().unwrap(); let hostapp_jp_matcher = hostapp_jp_matcher.downcast_ref::<DefaultMatcher>().unwrap();
assert_eq!(hostapp_jp_matcher.re.is_some(), true); assert!(hostapp_jp_matcher.re.is_some());
let re = hostapp_jp_matcher.re.as_ref(); let re = hostapp_jp_matcher.re.as_ref();
assert_eq!(re.unwrap().as_str(), "(?i)ホスト アプリケーション"); assert_eq!(re.unwrap().as_str(), "(?i)ホスト アプリケーション");
} }
@@ -607,36 +595,36 @@ mod tests {
// ImagePath // ImagePath
{ {
// AndSelectionNodeを正しく読み込めることを確認 // AndSelectionNodeを正しく読み込めることを確認
let child_node = detection_childs[3].as_ref() as &dyn SelectionNode; let child_node = detection_childs[3] as &dyn SelectionNode;
assert_eq!(child_node.is::<AndSelectionNode>(), true); assert!(child_node.is::<AndSelectionNode>());
let child_node = child_node.downcast_ref::<AndSelectionNode>().unwrap(); let child_node = child_node.downcast_ref::<AndSelectionNode>().unwrap();
let ancestors = child_node.get_childs(); let ancestors = child_node.get_childs();
assert_eq!(ancestors.len(), 3); assert_eq!(ancestors.len(), 3);
// min-lenが正しく読み込めることを確認 // min-lenが正しく読み込めることを確認
{ {
let ancestor_node = ancestors[0].as_ref() as &dyn SelectionNode; let ancestor_node = ancestors[0] as &dyn SelectionNode;
assert_eq!(ancestor_node.is::<LeafSelectionNode>(), true); assert!(ancestor_node.is::<LeafSelectionNode>());
let ancestor_node = ancestor_node.downcast_ref::<LeafSelectionNode>().unwrap(); let ancestor_node = ancestor_node.downcast_ref::<LeafSelectionNode>().unwrap();
let ancestor_node = &ancestor_node.matcher; let ancestor_node = &ancestor_node.matcher;
assert_eq!(ancestor_node.is_some(), true); assert!(ancestor_node.is_some());
let ancestor_matcher = ancestor_node.as_ref().unwrap(); let ancestor_matcher = ancestor_node.as_ref().unwrap();
assert_eq!(ancestor_matcher.is::<MinlengthMatcher>(), true); assert!(ancestor_matcher.is::<MinlengthMatcher>());
let ancestor_matcher = ancestor_matcher.downcast_ref::<MinlengthMatcher>().unwrap(); let ancestor_matcher = ancestor_matcher.downcast_ref::<MinlengthMatcher>().unwrap();
assert_eq!(ancestor_matcher.min_len, 1234321); assert_eq!(ancestor_matcher.min_len, 1234321);
} }
// regexesが正しく読み込めることを確認 // regexesが正しく読み込めることを確認
{ {
let ancestor_node = ancestors[1].as_ref() as &dyn SelectionNode; let ancestor_node = ancestors[1] as &dyn SelectionNode;
assert_eq!(ancestor_node.is::<LeafSelectionNode>(), true); assert!(ancestor_node.is::<LeafSelectionNode>());
let ancestor_node = ancestor_node.downcast_ref::<LeafSelectionNode>().unwrap(); let ancestor_node = ancestor_node.downcast_ref::<LeafSelectionNode>().unwrap();
let ancestor_node = &ancestor_node.matcher; let ancestor_node = &ancestor_node.matcher;
assert_eq!(ancestor_node.is_some(), true); assert!(ancestor_node.is_some());
let ancestor_matcher = ancestor_node.as_ref().unwrap(); let ancestor_matcher = ancestor_node.as_ref().unwrap();
assert_eq!(ancestor_matcher.is::<RegexesFileMatcher>(), true); assert!(ancestor_matcher.is::<RegexesFileMatcher>());
let ancestor_matcher = ancestor_matcher let ancestor_matcher = ancestor_matcher
.downcast_ref::<RegexesFileMatcher>() .downcast_ref::<RegexesFileMatcher>()
.unwrap(); .unwrap();
@@ -657,14 +645,14 @@ mod tests {
// allowlist.txtが読み込めることを確認 // allowlist.txtが読み込めることを確認
{ {
let ancestor_node = ancestors[2].as_ref() as &dyn SelectionNode; let ancestor_node = ancestors[2] as &dyn SelectionNode;
assert_eq!(ancestor_node.is::<LeafSelectionNode>(), true); assert!(ancestor_node.is::<LeafSelectionNode>());
let ancestor_node = ancestor_node.downcast_ref::<LeafSelectionNode>().unwrap(); let ancestor_node = ancestor_node.downcast_ref::<LeafSelectionNode>().unwrap();
let ancestor_node = &ancestor_node.matcher; let ancestor_node = &ancestor_node.matcher;
assert_eq!(ancestor_node.is_some(), true); assert!(ancestor_node.is_some());
let ancestor_matcher = ancestor_node.as_ref().unwrap(); let ancestor_matcher = ancestor_node.as_ref().unwrap();
assert_eq!(ancestor_matcher.is::<AllowlistFileMatcher>(), true); assert!(ancestor_matcher.is::<AllowlistFileMatcher>());
let ancestor_matcher = ancestor_matcher let ancestor_matcher = ancestor_matcher
.downcast_ref::<AllowlistFileMatcher>() .downcast_ref::<AllowlistFileMatcher>()
.unwrap(); .unwrap();
@@ -707,10 +695,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "failed to parse json record."); panic!("failed to parse json record.");
} }
} }
} }
@@ -737,10 +725,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -767,10 +755,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "failed to parse json record."); panic!("failed to parse json record.");
} }
} }
} }
@@ -798,10 +786,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -829,10 +817,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -859,10 +847,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -889,10 +877,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -920,10 +908,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -951,10 +939,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -982,10 +970,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1013,10 +1001,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1044,10 +1032,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1074,10 +1062,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1108,10 +1096,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1142,10 +1130,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1175,10 +1163,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1217,10 +1205,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); 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) => { Err(_rec) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1259,10 +1247,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); 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) => { Err(_rec) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1301,10 +1289,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); 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) => { Err(_rec) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1343,10 +1331,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); 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) => { Err(_rec) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1385,10 +1373,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); 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) => { Err(_rec) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1427,10 +1415,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); 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) => { Err(_rec) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1457,10 +1445,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1487,10 +1475,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1517,10 +1505,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1605,10 +1593,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1635,10 +1623,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1667,10 +1655,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -1699,10 +1687,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }

View File

@@ -21,7 +21,7 @@ use self::count::{AggRecordTimeInfo, TimeFrameInfo};
use super::detection::EvtxRecordInfo; use super::detection::EvtxRecordInfo;
pub fn create_rule(rulepath: String, yaml: Yaml) -> RuleNode { pub fn create_rule(rulepath: String, yaml: Yaml) -> RuleNode {
return RuleNode::new(rulepath, yaml); RuleNode::new(rulepath, yaml)
} }
/// Ruleファイルを表すード /// Ruleファイルを表すード
@@ -34,7 +34,7 @@ pub struct RuleNode {
impl Debug for RuleNode { impl Debug for RuleNode {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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 {} unsafe impl Send for RuleNode {}
impl RuleNode { impl RuleNode {
pub fn new(rulepath: String, yaml: Yaml) -> RuleNode { pub fn new(rule_path: String, yaml_data: Yaml) -> RuleNode {
return RuleNode { RuleNode {
rulepath: rulepath, rulepath: rule_path,
yaml: yaml, yaml: yaml_data,
detection: DetectionNode::new(), detection: DetectionNode::new(),
countdata: HashMap::new(), countdata: HashMap::new(),
}; }
} }
pub fn init(&mut self) -> Result<(), Vec<String>> { pub fn init(&mut self) -> Result<(), Vec<String>> {
@@ -56,14 +56,14 @@ impl RuleNode {
// detection node initialization // detection node initialization
let detection_result = self.detection.init(&self.yaml["detection"]); let detection_result = self.detection.init(&self.yaml["detection"]);
if detection_result.is_err() { if let Err(err_detail) = detection_result {
errmsgs.extend(detection_result.unwrap_err()); errmsgs.extend(err_detail);
} }
if errmsgs.is_empty() { if errmsgs.is_empty() {
return Result::Ok(()); Result::Ok(())
} else { } else {
return Result::Err(errmsgs); Result::Err(errmsgs)
} }
} }
@@ -72,11 +72,11 @@ impl RuleNode {
if result && self.has_agg_condition() { if result && self.has_agg_condition() {
count::count(self, &event_record.record); count::count(self, &event_record.record);
} }
return result; result
} }
/// aggregation conditionが存在するかを返す関数 /// aggregation conditionが存在するかを返す関数
pub fn has_agg_condition(&self) -> bool { pub fn has_agg_condition(&self) -> bool {
return self.detection.aggregation_condition.is_some(); self.detection.aggregation_condition.is_some()
} }
/// Aggregation Conditionの結果を配列で返却する関数 /// Aggregation Conditionの結果を配列で返却する関数
pub fn judge_satisfy_aggcondition(&self) -> Vec<AggResult> { pub fn judge_satisfy_aggcondition(&self) -> Vec<AggResult> {
@@ -84,21 +84,17 @@ impl RuleNode {
if !self.has_agg_condition() { if !self.has_agg_condition() {
return ret; return ret;
} }
ret.append(&mut count::aggregation_condition_select(&self)); ret.append(&mut count::aggregation_condition_select(self));
return ret; ret
} }
pub fn check_exist_countdata(&self) -> bool { pub fn check_exist_countdata(&self) -> bool {
self.countdata.len() > 0 !self.countdata.is_empty()
} }
/// ルール内のAggregationParseInfo(Aggregation Condition)を取得する関数 /// ルール内のAggregationParseInfo(Aggregation Condition)を取得する関数
pub fn get_agg_condition(&self) -> Option<&AggregationParseInfo> { pub fn get_agg_condition(&self) -> Option<&AggregationParseInfo> {
match self.detection.aggregation_condition.as_ref() { match self.detection.aggregation_condition.as_ref() {
None => { None => None,
return None; Some(agg_parse_info) => Some(agg_parse_info),
}
Some(agg_parse_info) => {
return Some(agg_parse_info);
}
} }
} }
} }
@@ -120,12 +116,12 @@ pub fn get_detection_keys(node: &RuleNode) -> Vec<String> {
if key.is_empty() { if key.is_empty() {
return Option::None; return Option::None;
} }
return Option::Some(key.to_string()); Option::Some(key.to_string())
}); });
ret.extend(keys); ret.extend(keys);
} }
return ret; ret
} }
/// Ruleファイルのdetectionを表すード /// Ruleファイルのdetectionを表すード
@@ -138,12 +134,12 @@ struct DetectionNode {
impl DetectionNode { impl DetectionNode {
fn new() -> DetectionNode { fn new() -> DetectionNode {
return DetectionNode { DetectionNode {
name_to_selection: HashMap::new(), name_to_selection: HashMap::new(),
condition: Option::None, condition: Option::None,
aggregation_condition: Option::None, aggregation_condition: Option::None,
timeframe: Option::None, timeframe: Option::None,
}; }
} }
fn init(&mut self, detection_yaml: &Yaml) -> Result<(), Vec<String>> { fn init(&mut self, detection_yaml: &Yaml) -> Result<(), Vec<String>> {
@@ -169,7 +165,7 @@ impl DetectionNode {
]); ]);
} }
keys.nth(0).unwrap().to_string() keys.next().unwrap().to_string()
}; };
// conditionをパースして、SelectionNodeに変換する // conditionをパースして、SelectionNodeに変換する
@@ -193,9 +189,9 @@ impl DetectionNode {
} }
if err_msgs.is_empty() { if err_msgs.is_empty() {
return Result::Ok(()); Result::Ok(())
} else { } else {
return Result::Err(err_msgs); Result::Err(err_msgs)
} }
} }
@@ -205,7 +201,7 @@ impl DetectionNode {
} }
let condition = &self.condition.as_ref().unwrap(); let condition = &self.condition.as_ref().unwrap();
return condition.select(event_record); condition.select(event_record)
} }
/// selectionードをパースします。 /// selectionードをパースします。
@@ -221,7 +217,7 @@ impl DetectionNode {
let mut err_msgs = vec![]; let mut err_msgs = vec![];
for key in keys { for key in keys {
let name = key.as_str().unwrap_or(""); let name = key.as_str().unwrap_or("");
if name.len() == 0 { if name.is_empty() {
continue; continue;
} }
// condition等、特殊なキーワードを無視する。 // condition等、特殊なキーワードを無視する。
@@ -231,11 +227,11 @@ impl DetectionNode {
// パースして、エラーメッセージがあれば配列にためて、戻り値で返す。 // パースして、エラーメッセージがあれば配列にためて、戻り値で返す。
let selection_node = self.parse_selection(&detection_hash[key]); let selection_node = self.parse_selection(&detection_hash[key]);
if selection_node.is_some() { if let Some(node) = selection_node {
let mut selection_node = selection_node.unwrap(); let mut selection_node = node;
let init_result = selection_node.init(); let init_result = selection_node.init();
if init_result.is_err() { if let Err(err_detail) = init_result {
err_msgs.extend(init_result.unwrap_err()); err_msgs.extend(err_detail);
} else { } else {
let rc_selection = Arc::new(selection_node); let rc_selection = Arc::new(selection_node);
self.name_to_selection self.name_to_selection
@@ -248,18 +244,18 @@ impl DetectionNode {
} }
// selectionードが無いのはエラー // selectionードが無いのはエラー
if self.name_to_selection.len() == 0 { if self.name_to_selection.is_empty() {
return Result::Err(vec![ return Result::Err(vec![
"There is no selection node under detection.".to_string() "There is no selection node under detection.".to_string()
]); ]);
} }
return Result::Ok(()); Result::Ok(())
} }
/// selectionをパースします。 /// selectionをパースします。
fn parse_selection(&self, selection_yaml: &Yaml) -> Option<Box<dyn SelectionNode>> { 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をパースします。 /// selectionをパースします。
@@ -280,7 +276,7 @@ impl DetectionNode {
let child_node = self.parse_selection_recursively(child_key_list, child_yaml); let child_node = self.parse_selection_recursively(child_key_list, child_yaml);
and_node.child_nodes.push(child_node); and_node.child_nodes.push(child_node);
}); });
return Box::new(and_node); Box::new(and_node)
} else if yaml.as_vec().is_some() { } else if yaml.as_vec().is_some() {
// 配列はOR条件と解釈する。 // 配列はOR条件と解釈する。
let mut or_node = selectionnodes::OrSelectionNode::new(); let mut or_node = selectionnodes::OrSelectionNode::new();
@@ -289,13 +285,13 @@ impl DetectionNode {
or_node.child_nodes.push(child_node); or_node.child_nodes.push(child_node);
}); });
return Box::new(or_node); Box::new(or_node)
} else { } else {
// 連想配列と配列以外は末端ノード // 連想配列と配列以外は末端ノード
return Box::new(selectionnodes::LeafSelectionNode::new( Box::new(selectionnodes::LeafSelectionNode::new(
key_list, key_list,
yaml.clone(), yaml.clone(),
)); ))
} }
} }
} }
@@ -317,19 +313,19 @@ pub struct AggResult {
impl AggResult { impl AggResult {
pub fn new( pub fn new(
data: i64, count_data: i64,
key: String, key_name: String,
field_values: Vec<String>, field_value: Vec<String>,
start_timedate: DateTime<Utc>, event_start_timedate: DateTime<Utc>,
condition_op_num: String, condition_op_number: String,
) -> AggResult { ) -> AggResult {
return AggResult { AggResult {
data: data, data: count_data,
key: key, key: key_name,
field_values: field_values, field_values: field_value,
start_timedate: start_timedate, start_timedate: event_start_timedate,
condition_op_num: condition_op_num, condition_op_num: condition_op_number,
}; }
} }
} }
@@ -341,12 +337,12 @@ mod tests {
pub fn parse_rule_from_str(rule_str: &str) -> RuleNode { pub fn parse_rule_from_str(rule_str: &str) -> RuleNode {
let rule_yaml = YamlLoader::load_from_str(rule_str); 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 rule_yamls = rule_yaml.unwrap();
let mut rule_yaml = rule_yamls.into_iter(); let mut rule_yaml = rule_yamls.into_iter();
let mut rule_node = create_rule("testpath".to_string(), rule_yaml.next().unwrap()); let mut rule_node = create_rule("testpath".to_string(), rule_yaml.next().unwrap());
assert_eq!(rule_node.init().is_ok(), true); assert!(rule_node.init().is_ok());
return rule_node; rule_node
} }
#[test] #[test]
@@ -371,10 +367,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -401,10 +397,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -431,10 +427,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -514,10 +510,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -573,10 +569,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -639,10 +635,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -683,10 +679,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -728,10 +724,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -792,10 +788,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -856,10 +852,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -902,10 +898,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); 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) => { Err(_rec) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -961,15 +957,15 @@ mod tests {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
let result = rule_node.select(&recinfo); let result = rule_node.select(&recinfo);
assert_eq!(rule_node.detection.aggregation_condition.is_some(), true); assert!(rule_node.detection.aggregation_condition.is_some());
assert_eq!(result, true); assert!(result);
assert_eq!( assert_eq!(
*&rule_node.countdata.get(key).unwrap().len() as i32, rule_node.countdata.get(key).unwrap().len() as i32,
expect_count expect_count
); );
} }
Err(_rec) => { Err(_rec) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }

View File

@@ -19,10 +19,10 @@ pub trait SelectionNode: mopa::Any {
fn init(&mut self) -> Result<(), Vec<String>>; fn init(&mut self) -> Result<(), Vec<String>>;
// 子ノードを取得する(グラフ理論のchildと同じ意味) // 子ノードを取得する(グラフ理論のchildと同じ意味)
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>>; fn get_childs(&self) -> Vec<&dyn SelectionNode>;
// 子孫ノードを取得する(グラフ理論のdescendantと同じ意味) // 子孫ノードを取得する(グラフ理論のdescendantと同じ意味)
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>>; fn get_descendants(&self) -> Vec<&dyn SelectionNode>;
} }
mopafy!(SelectionNode); mopafy!(SelectionNode);
@@ -33,17 +33,17 @@ pub struct AndSelectionNode {
impl AndSelectionNode { impl AndSelectionNode {
pub fn new() -> AndSelectionNode { pub fn new() -> AndSelectionNode {
return AndSelectionNode { AndSelectionNode {
child_nodes: vec![], child_nodes: vec![],
}; }
} }
} }
impl SelectionNode for AndSelectionNode { impl SelectionNode for AndSelectionNode {
fn select(&self, event_record: &EvtxRecordInfo) -> bool { fn select(&self, event_record: &EvtxRecordInfo) -> bool {
return self.child_nodes.iter().all(|child_node| { self.child_nodes
return child_node.select(event_record); .iter()
}); .all(|child_node| child_node.select(event_record))
} }
fn init(&mut self) -> Result<(), Vec<String>> { fn init(&mut self) -> Result<(), Vec<String>> {
@@ -52,50 +52,48 @@ impl SelectionNode for AndSelectionNode {
.iter_mut() .iter_mut()
.map(|child_node| { .map(|child_node| {
let res = child_node.init(); let res = child_node.init();
if res.is_err() { if let Err(err) = res {
return res.unwrap_err(); err
} else { } else {
return vec![]; vec![]
} }
}) })
.fold( .fold(
vec![], vec![],
|mut acc: Vec<String>, cur: Vec<String>| -> Vec<String> { |mut acc: Vec<String>, cur: Vec<String>| -> Vec<String> {
acc.extend(cur.into_iter()); acc.extend(cur.into_iter());
return acc; acc
}, },
); );
if err_msgs.is_empty() { if err_msgs.is_empty() {
return Result::Ok(()); Result::Ok(())
} else { } 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![]; let mut ret = vec![];
self.child_nodes.iter().for_each(|child_node| { 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(); let mut ret = self.get_childs();
self.child_nodes self.child_nodes
.iter() .iter()
.map(|child_node| { .map(|child_node| child_node.get_descendants())
return child_node.get_descendants();
})
.flatten() .flatten()
.for_each(|descendant_node| { .for_each(|descendant_node| {
ret.push(descendant_node); ret.push(descendant_node);
}); });
return ret; ret
} }
} }
@@ -106,17 +104,17 @@ pub struct OrSelectionNode {
impl OrSelectionNode { impl OrSelectionNode {
pub fn new() -> OrSelectionNode { pub fn new() -> OrSelectionNode {
return OrSelectionNode { OrSelectionNode {
child_nodes: vec![], child_nodes: vec![],
}; }
} }
} }
impl SelectionNode for OrSelectionNode { impl SelectionNode for OrSelectionNode {
fn select(&self, event_record: &EvtxRecordInfo) -> bool { fn select(&self, event_record: &EvtxRecordInfo) -> bool {
return self.child_nodes.iter().any(|child_node| { self.child_nodes
return child_node.select(event_record); .iter()
}); .any(|child_node| child_node.select(event_record))
} }
fn init(&mut self) -> Result<(), Vec<String>> { fn init(&mut self) -> Result<(), Vec<String>> {
@@ -125,50 +123,48 @@ impl SelectionNode for OrSelectionNode {
.iter_mut() .iter_mut()
.map(|child_node| { .map(|child_node| {
let res = child_node.init(); let res = child_node.init();
if res.is_err() { if let Err(err) = res {
return res.unwrap_err(); err
} else { } else {
return vec![]; vec![]
} }
}) })
.fold( .fold(
vec![], vec![],
|mut acc: Vec<String>, cur: Vec<String>| -> Vec<String> { |mut acc: Vec<String>, cur: Vec<String>| -> Vec<String> {
acc.extend(cur.into_iter()); acc.extend(cur.into_iter());
return acc; acc
}, },
); );
if err_msgs.is_empty() { if err_msgs.is_empty() {
return Result::Ok(()); Result::Ok(())
} else { } 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![]; let mut ret = vec![];
self.child_nodes.iter().for_each(|child_node| { 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(); let mut ret = self.get_childs();
self.child_nodes self.child_nodes
.iter() .iter()
.map(|child_node| { .map(|child_node| child_node.get_descendants())
return child_node.get_descendants();
})
.flatten() .flatten()
.for_each(|descendant_node| { .for_each(|descendant_node| {
ret.push(descendant_node); ret.push(descendant_node);
}); });
return ret; ret
} }
} }
@@ -178,26 +174,26 @@ pub struct NotSelectionNode {
} }
impl NotSelectionNode { impl NotSelectionNode {
pub fn new(node: Box<dyn SelectionNode>) -> NotSelectionNode { pub fn new(select_node: Box<dyn SelectionNode>) -> NotSelectionNode {
return NotSelectionNode { node: node }; NotSelectionNode { node: select_node }
} }
} }
impl SelectionNode for NotSelectionNode { impl SelectionNode for NotSelectionNode {
fn select(&self, event_record: &EvtxRecordInfo) -> bool { 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>> { fn init(&mut self) -> Result<(), Vec<String>> {
return Result::Ok(()); Result::Ok(())
} }
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>> { fn get_childs(&self) -> Vec<&dyn SelectionNode> {
return vec![]; vec![]
} }
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>> { fn get_descendants(&self) -> Vec<&dyn SelectionNode> {
return self.get_childs(); self.get_childs()
} }
} }
@@ -210,28 +206,28 @@ pub struct RefSelectionNode {
} }
impl RefSelectionNode { impl RefSelectionNode {
pub fn new(selection_node: Arc<Box<dyn SelectionNode>>) -> RefSelectionNode { pub fn new(select_node: Arc<Box<dyn SelectionNode>>) -> RefSelectionNode {
return RefSelectionNode { RefSelectionNode {
selection_node: selection_node, selection_node: select_node,
}; }
} }
} }
impl SelectionNode for RefSelectionNode { impl SelectionNode for RefSelectionNode {
fn select(&self, event_record: &EvtxRecordInfo) -> bool { 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>> { fn init(&mut self) -> Result<(), Vec<String>> {
return Result::Ok(()); Result::Ok(())
} }
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>> { fn get_childs(&self) -> Vec<&dyn SelectionNode> {
return vec![&self.selection_node]; vec![self.selection_node.as_ref().as_ref()]
} }
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>> { fn get_descendants(&self) -> Vec<&dyn SelectionNode> {
return self.get_childs(); self.get_childs()
} }
} }
@@ -244,17 +240,17 @@ pub struct LeafSelectionNode {
} }
impl LeafSelectionNode { impl LeafSelectionNode {
pub fn new(key_list: Vec<String>, value_yaml: Yaml) -> LeafSelectionNode { pub fn new(keys: Vec<String>, value_yaml: Yaml) -> LeafSelectionNode {
return LeafSelectionNode { LeafSelectionNode {
key: String::default(), key: String::default(),
key_list: key_list, key_list: keys,
select_value: value_yaml, select_value: value_yaml,
matcher: Option::None, matcher: Option::None,
}; }
} }
pub fn get_key(&self) -> &String { pub fn get_key(&self) -> &String {
return &self.key; &self.key
} }
fn _create_key(&self) -> String { fn _create_key(&self) -> String {
@@ -263,8 +259,8 @@ impl LeafSelectionNode {
} }
let topkey = self.key_list[0].to_string(); let topkey = self.key_list[0].to_string();
let values: Vec<&str> = topkey.split("|").collect(); let values: Vec<&str> = topkey.split('|').collect();
return values[0].to_string(); values[0].to_string()
} }
/// JSON形式のEventJSONから値を取得する関数 aliasも考慮されている。 /// JSON形式のEventJSONから値を取得する関数 aliasも考慮されている。
@@ -274,18 +270,18 @@ impl LeafSelectionNode {
return Option::Some(&record.data_string); return Option::Some(&record.data_string);
} }
return record.get_value(self.get_key()); record.get_value(self.get_key())
} }
/// matchers::LeafMatcherの一覧を取得する。 /// matchers::LeafMatcherの一覧を取得する。
/// 上から順番に調べて、一番始めに一致したMatcherが適用される /// 上から順番に調べて、一番始めに一致したMatcherが適用される
fn get_matchers(&self) -> Vec<Box<dyn matchers::LeafMatcher>> { fn get_matchers(&self) -> Vec<Box<dyn matchers::LeafMatcher>> {
return vec![ vec![
Box::new(matchers::MinlengthMatcher::new()), Box::new(matchers::MinlengthMatcher::new()),
Box::new(matchers::RegexesFileMatcher::new()), Box::new(matchers::RegexesFileMatcher::new()),
Box::new(matchers::AllowlistFileMatcher::new()), Box::new(matchers::AllowlistFileMatcher::new()),
Box::new(matchers::DefaultMatcher::new()), Box::new(matchers::DefaultMatcher::new()),
]; ]
} }
} }
@@ -370,7 +366,7 @@ impl SelectionNode for LeafSelectionNode {
} }
let replaced_str = let replaced_str =
utils::replace_target_character(self.get_event_value(&event_record), filter_rule); utils::replace_target_character(self.get_event_value(event_record), filter_rule);
return self return self
.matcher .matcher
@@ -409,12 +405,12 @@ impl SelectionNode for LeafSelectionNode {
.init(&match_key_list, &self.select_value); .init(&match_key_list, &self.select_value);
} }
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>> { fn get_childs(&self) -> Vec<&dyn SelectionNode> {
return vec![]; vec![]
} }
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>> { fn get_descendants(&self) -> Vec<&dyn SelectionNode> {
return vec![]; vec![]
} }
} }
@@ -445,10 +441,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -478,10 +474,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -510,10 +506,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -542,10 +538,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), true); assert!(rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }
@@ -574,10 +570,10 @@ mod tests {
Ok(record) => { Ok(record) => {
let keys = detections::rule::get_detection_keys(&rule_node); let keys = detections::rule::get_detection_keys(&rule_node);
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys); let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
assert_eq!(rule_node.select(&recinfo), false); assert!(!rule_node.select(&recinfo));
} }
Err(_) => { Err(_) => {
assert!(false, "Failed to parse json record."); panic!("Failed to parse json record.");
} }
} }
} }

View File

@@ -19,25 +19,25 @@ use std::string::String;
use super::detection::EvtxRecordInfo; use super::detection::EvtxRecordInfo;
pub fn concat_selection_key(key_list: &Vec<String>) -> String { pub fn concat_selection_key(key_list: &[String]) -> String {
return key_list return key_list
.iter() .iter()
.fold("detection -> selection".to_string(), |mut acc, cur| { .fold("detection -> selection".to_string(), |mut acc, cur| {
acc = acc + " -> " + cur; acc = acc + " -> " + cur;
return acc; acc
}); });
} }
pub fn check_regex(string: &str, regex_list: &Vec<Regex>) -> bool { pub fn check_regex(string: &str, regex_list: &[Regex]) -> bool {
for regex in regex_list { for regex in regex_list {
if regex.is_match(string) == false { if !regex.is_match(string) {
continue; continue;
} }
return true; return true;
} }
return false; false
} }
/// replace string from all defined regex in input to replace_str /// replace string from all defined regex in input to replace_str
@@ -45,9 +45,7 @@ pub fn replace_target_character<'a>(
input_str: Option<&'a String>, input_str: Option<&'a String>,
replace_rule: Option<&'a DataFilterRule>, replace_rule: Option<&'a DataFilterRule>,
) -> Option<String> { ) -> Option<String> {
if input_str.is_none() { input_str?;
return None;
}
if replace_rule.is_none() { if replace_rule.is_none() {
return Some(input_str.unwrap().to_string()); return Some(input_str.unwrap().to_string());
} }
@@ -55,32 +53,32 @@ pub fn replace_target_character<'a>(
let replace_regex_rule = &replace_rule.unwrap().regex_rule; let replace_regex_rule = &replace_rule.unwrap().regex_rule;
let replace_str = &replace_rule.unwrap().replace_str; let replace_str = &replace_rule.unwrap().replace_str;
return Some( Some(
replace_regex_rule replace_regex_rule
.replace_all(input_str.unwrap(), replace_str) .replace_all(input_str.unwrap(), replace_str)
.to_string(), .to_string(),
); )
} }
pub fn check_allowlist(target: &str, regexes: &Vec<Regex>) -> bool { pub fn check_allowlist(target: &str, regexes: &[Regex]) -> bool {
for regex in regexes { for regex in regexes {
if regex.is_match(target) { if regex.is_match(target) {
return true; return true;
} }
} }
return false; false
} }
pub fn value_to_string(value: &Value) -> Option<String> { pub fn value_to_string(value: &Value) -> Option<String> {
return match value { match value {
Value::Null => Option::None, Value::Null => Option::None,
Value::Bool(b) => Option::Some(b.to_string()), Value::Bool(b) => Option::Some(b.to_string()),
Value::Number(n) => Option::Some(n.to_string()), Value::Number(n) => Option::Some(n.to_string()),
Value::String(s) => Option::Some(s.to_string()), Value::String(s) => Option::Some(s.to_string()),
Value::Array(_) => Option::None, Value::Array(_) => Option::None,
Value::Object(_) => Option::None, Value::Object(_) => Option::None,
}; }
} }
pub fn read_txt(filename: &str) -> Result<Vec<String>, String> { pub fn read_txt(filename: &str) -> Result<Vec<String>, String> {
@@ -90,12 +88,12 @@ pub fn read_txt(filename: &str) -> Result<Vec<String>, String> {
return Result::Err(errmsg); return Result::Err(errmsg);
} }
let reader = BufReader::new(f.unwrap()); let reader = BufReader::new(f.unwrap());
return Result::Ok( Result::Ok(
reader reader
.lines() .lines()
.map(|line| line.unwrap_or(String::default())) .map(|line| line.unwrap_or_default())
.collect(), .collect(),
); )
} }
pub fn read_csv(filename: &str) -> Result<Vec<Vec<String>>, String> { pub fn read_csv(filename: &str) -> Result<Vec<Vec<String>>, String> {
@@ -106,8 +104,8 @@ pub fn read_csv(filename: &str) -> Result<Vec<Vec<String>>, String> {
let mut contents: String = String::new(); let mut contents: String = String::new();
let mut ret = vec![]; let mut ret = vec![];
let read_res = f.unwrap().read_to_string(&mut contents); let read_res = f.unwrap().read_to_string(&mut contents);
if read_res.is_err() { if let Err(e) = read_res {
return Result::Err(read_res.unwrap_err().to_string()); return Result::Err(e.to_string());
} }
let mut rdr = csv::Reader::from_reader(contents.as_bytes()); let mut rdr = csv::Reader::from_reader(contents.as_bytes());
@@ -122,19 +120,19 @@ pub fn read_csv(filename: &str) -> Result<Vec<Vec<String>>, String> {
ret.push(v); ret.push(v);
}); });
return Result::Ok(ret); Result::Ok(ret)
} }
pub fn is_target_event_id(s: &String) -> bool { pub fn is_target_event_id(s: &str) -> bool {
return configs::CONFIG.read().unwrap().target_eventids.is_target(s); configs::CONFIG.read().unwrap().target_eventids.is_target(s)
} }
pub fn get_event_id_key() -> String { pub fn get_event_id_key() -> String {
return "Event.System.EventID".to_string(); "Event.System.EventID".to_string()
} }
pub fn get_event_time() -> String { pub fn get_event_time() -> String {
return "Event.System.TimeCreated_attributes.SystemTime".to_string(); "Event.System.TimeCreated_attributes.SystemTime".to_string()
} }
pub fn str_time_to_datetime(system_time_str: &str) -> Option<DateTime<Utc>> { pub fn str_time_to_datetime(system_time_str: &str) -> Option<DateTime<Utc>> {
@@ -146,30 +144,24 @@ pub fn str_time_to_datetime(system_time_str: &str) -> Option<DateTime<Utc>> {
if rfc3339_time.is_err() { if rfc3339_time.is_err() {
return Option::None; return Option::None;
} }
let datetime = Utc Utc.from_local_datetime(&rfc3339_time.unwrap().naive_utc())
.from_local_datetime(&rfc3339_time.unwrap().naive_utc()) .single()
.single();
if datetime.is_none() {
return Option::None;
} else {
return Option::Some(datetime.unwrap());
}
} }
/// serde:Valueの型を確認し、文字列を返します。 /// serde:Valueの型を確認し、文字列を返します。
pub fn get_serde_number_to_string(value: &serde_json::Value) -> Option<String> { pub fn get_serde_number_to_string(value: &serde_json::Value) -> Option<String> {
if value.is_string() { if value.is_string() {
return Option::Some(value.as_str().unwrap_or("").to_string()); Option::Some(value.as_str().unwrap_or("").to_string())
} else if value.is_object() { } else if value.is_object() {
// Object type is not specified record value. // Object type is not specified record value.
return Option::None; Option::None
} else { } else {
return Option::Some(value.to_string()); Option::Some(value.to_string())
} }
} }
pub fn get_event_value<'a>(key: &String, event_value: &'a Value) -> Option<&'a Value> { pub fn get_event_value<'a>(key: &str, event_value: &'a Value) -> Option<&'a Value> {
if key.len() == 0 { if key.is_empty() {
return Option::None; return Option::None;
} }
@@ -180,28 +172,28 @@ pub fn get_event_value<'a>(key: &String, event_value: &'a Value) -> Option<&'a V
let splits = configs::EVENTKEY_ALIAS.get_event_key_split(key); let splits = configs::EVENTKEY_ALIAS.get_event_key_split(key);
let mut start_idx = 0; let mut start_idx = 0;
for key in splits.unwrap() { for key in splits.unwrap() {
if ret.is_object() == false { if !ret.is_object() {
return Option::None; return Option::None;
} }
let val = &event_key[start_idx..(*key + start_idx)]; let val = &event_key[start_idx..(*key + start_idx)];
ret = &ret[val]; ret = &ret[val];
start_idx = *key + start_idx; start_idx += *key;
start_idx += 1; start_idx += 1;
} }
return Option::Some(ret); Option::Some(ret)
} else { } else {
let mut ret: &Value = event_value; let mut ret: &Value = event_value;
let event_key = key; let event_key = key;
for key in event_key.split(".") { for key in event_key.split('.') {
if ret.is_object() == false { if !ret.is_object() {
return Option::None; return Option::None;
} }
ret = &ret[key]; ret = &ret[key];
} }
return Option::Some(ret); Option::Some(ret)
} }
} }
@@ -211,20 +203,20 @@ pub fn get_thread_num() -> usize {
let threadnum = &conf let threadnum = &conf
.args .args
.value_of("thread-number") .value_of("thread-number")
.unwrap_or(def_thread_num_str.as_str()); .unwrap_or_else(|| def_thread_num_str.as_str());
return threadnum.parse::<usize>().unwrap().clone(); threadnum.parse::<usize>().unwrap()
} }
pub fn create_tokio_runtime() -> Runtime { pub fn create_tokio_runtime() -> Runtime {
return Builder::new_multi_thread() Builder::new_multi_thread()
.worker_threads(get_thread_num()) .worker_threads(get_thread_num())
.thread_name("yea-thread") .thread_name("yea-thread")
.build() .build()
.unwrap(); .unwrap()
} }
// EvtxRecordInfoを作成します。 // EvtxRecordInfoを作成します。
pub fn create_rec_info(data: Value, path: String, keys: &Vec<String>) -> EvtxRecordInfo { pub fn create_rec_info(data: Value, path: String, keys: &[String]) -> EvtxRecordInfo {
// EvtxRecordInfoを作る // EvtxRecordInfoを作る
let data_str = data.to_string(); let data_str = data.to_string();
let mut rec = EvtxRecordInfo { let mut rec = EvtxRecordInfo {
@@ -255,7 +247,7 @@ pub fn create_rec_info(data: Value, path: String, keys: &Vec<String>) -> EvtxRec
rec.key_2_value.insert(key.to_string(), val.unwrap()); rec.key_2_value.insert(key.to_string(), val.unwrap());
} }
return rec; rec
} }
#[cfg(test)] #[cfg(test)]
@@ -267,30 +259,32 @@ mod tests {
#[test] #[test]
fn test_check_regex() { fn test_check_regex() {
let regexes = utils::read_txt("./rules/config/regex/detectlist_suspicous_services.txt") let regexes: Vec<Regex> =
utils::read_txt("./rules/config/regex/detectlist_suspicous_services.txt")
.unwrap() .unwrap()
.into_iter() .into_iter()
.map(|regex_str| Regex::new(&regex_str).unwrap()) .map(|regex_str| Regex::new(&regex_str).unwrap())
.collect(); .collect();
let regextext = utils::check_regex("\\cvtres.exe", &regexes); let regextext = utils::check_regex("\\cvtres.exe", &regexes);
assert!(regextext == true); assert!(regextext);
let regextext = utils::check_regex("\\hogehoge.exe", &regexes); let regextext = utils::check_regex("\\hogehoge.exe", &regexes);
assert!(regextext == false); assert!(!regextext);
} }
#[test] #[test]
fn test_check_allowlist() { fn test_check_allowlist() {
let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate.exe\""; let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate.exe\"";
let allowlist = utils::read_txt("./rules/config/regex/allowlist_legitimate_services.txt") let allowlist: Vec<Regex> =
utils::read_txt("./rules/config/regex/allowlist_legitimate_services.txt")
.unwrap() .unwrap()
.into_iter() .into_iter()
.map(|allow_str| Regex::new(&allow_str).unwrap()) .map(|allow_str| Regex::new(&allow_str).unwrap())
.collect(); .collect();
assert!(true == utils::check_allowlist(commandline, &allowlist)); assert!(utils::check_allowlist(commandline, &allowlist));
let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate2.exe\""; let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate2.exe\"";
assert!(false == utils::check_allowlist(commandline, &allowlist)); assert!(!utils::check_allowlist(commandline, &allowlist));
} }
#[test] #[test]
@@ -360,15 +354,9 @@ mod tests {
}; };
let none_test_str: Option<&String> = None; let none_test_str: Option<&String> = None;
assert_eq!( assert!(utils::replace_target_character(none_test_str, None).is_none());
utils::replace_target_character(none_test_str, None).is_none(),
true
);
assert_eq!( assert!(utils::replace_target_character(none_test_str, Some(&test_filter_rule)).is_none());
utils::replace_target_character(none_test_str, Some(&test_filter_rule)).is_none(),
true
);
let tmp = "h\ra\ny\ta\tb\nu\r\nsa".to_string(); let tmp = "h\ra\ny\ta\tb\nu\r\nsa".to_string();
let test_str: Option<&String> = Some(&tmp); let test_str: Option<&String> = Some(&tmp);

View File

@@ -52,17 +52,16 @@ fn load_record_filters() -> HashMap<String, DataFilterRule> {
let key = line.get(0).unwrap_or(empty).trim(); let key = line.get(0).unwrap_or(empty).trim();
let regex_str = line.get(1).unwrap_or(empty).trim(); let regex_str = line.get(1).unwrap_or(empty).trim();
let replaced_str = line.get(2).unwrap_or(empty).trim(); let replaced_str = line.get(2).unwrap_or(empty).trim();
if key.len() == 0 || regex_str.len() == 0 { if key.is_empty() || regex_str.is_empty() {
return; return;
} }
let regex_rule: Option<Regex> = match Regex::new(regex_str) { let regex_rule: Option<Regex> = match Regex::new(regex_str) {
Ok(regex) => Some(regex), Ok(regex) => Some(regex),
Err(_err) => { Err(_err) => {
let errmsg = format!("failed to read regex filter in record_data_filter.txt"); let errmsg = "failed to read regex filter in record_data_filter.txt";
if configs::CONFIG.read().unwrap().args.is_present("verbose") { if configs::CONFIG.read().unwrap().args.is_present("verbose") {
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg) AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), errmsg).ok();
.ok();
} }
if !*QUIET_ERRORS_FLAG { if !*QUIET_ERRORS_FLAG {
ERROR_LOG_STACK ERROR_LOG_STACK
@@ -85,7 +84,7 @@ fn load_record_filters() -> HashMap<String, DataFilterRule> {
}, },
); );
}); });
return ret; ret
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@@ -109,7 +108,7 @@ pub fn exclude_ids() -> RuleExclude {
exclude_ids.insert_ids("./rules/config/exclude_rules.txt"); exclude_ids.insert_ids("./rules/config/exclude_rules.txt");
return exclude_ids; exclude_ids
} }
impl RuleExclude { impl RuleExclude {
@@ -129,11 +128,10 @@ impl RuleExclude {
.unwrap() .unwrap()
.push(format!("{} does not exist", filename)); .push(format!("{} does not exist", filename));
} }
return ();
} }
let reader = BufReader::new(f.unwrap()); let reader = BufReader::new(f.unwrap());
for v in reader.lines() { for v in reader.lines() {
let v = v.unwrap().split("#").collect::<Vec<&str>>()[0] let v = v.unwrap().split('#').collect::<Vec<&str>>()[0]
.trim() .trim()
.to_string(); .to_string();
if v.is_empty() || !IDS_REGEX.is_match(&v) { if v.is_empty() || !IDS_REGEX.is_match(&v) {

View File

@@ -16,7 +16,7 @@ use hayabusa::detections::rule::{get_detection_keys, RuleNode};
use hayabusa::filter; use hayabusa::filter;
use hayabusa::omikuji::Omikuji; use hayabusa::omikuji::Omikuji;
use hayabusa::{afterfact::after_fact, detections::utils}; use hayabusa::{afterfact::after_fact, detections::utils};
use hayabusa::{detections::configs, timeline::timeline::Timeline}; use hayabusa::{detections::configs, timeline::timelines::Timeline};
use hhmmss::Hhmmss; use hhmmss::Hhmmss;
use pbr::ProgressBar; use pbr::ProgressBar;
use serde_json::Value; use serde_json::Value;
@@ -53,19 +53,25 @@ pub struct App {
rule_keys: Vec<String>, rule_keys: Vec<String>,
} }
impl Default for App {
fn default() -> Self {
Self::new()
}
}
impl App { impl App {
pub fn new() -> App { pub fn new() -> App {
return App { App {
rt: utils::create_tokio_runtime(), rt: utils::create_tokio_runtime(),
rule_keys: Vec::new(), rule_keys: Vec::new(),
}; }
} }
fn exec(&mut self) { fn exec(&mut self) {
let analysis_start_time: DateTime<Local> = Local::now(); let analysis_start_time: DateTime<Local> = Local::now();
if !configs::CONFIG.read().unwrap().args.is_present("quiet") { if !configs::CONFIG.read().unwrap().args.is_present("quiet") {
self.output_logo(); self.output_logo();
println!(""); println!();
self.output_eggs(&format!( self.output_eggs(&format!(
"{:02}/{:02}", "{:02}/{:02}",
&analysis_start_time.month().to_owned(), &analysis_start_time.month().to_owned(),
@@ -98,12 +104,12 @@ impl App {
.ok(); .ok();
return; return;
} }
if configs::CONFIG.read().unwrap().args.args.len() == 0 { if configs::CONFIG.read().unwrap().args.args.is_empty() {
println!( println!(
"{}", "{}",
configs::CONFIG.read().unwrap().args.usage().to_string() configs::CONFIG.read().unwrap().args.usage().to_string()
); );
println!(""); println!();
return; return;
} }
if let Some(csv_path) = configs::CONFIG.read().unwrap().args.value_of("output") { if let Some(csv_path) = configs::CONFIG.read().unwrap().args.value_of("output") {
@@ -121,7 +127,7 @@ impl App {
} }
if *STATISTICS_FLAG { if *STATISTICS_FLAG {
println!("Generating Event ID Statistics"); println!("Generating Event ID Statistics");
println!(""); println!();
} }
if configs::CONFIG if configs::CONFIG
.read() .read()
@@ -138,11 +144,11 @@ impl App {
if !filepath.ends_with(".evtx") if !filepath.ends_with(".evtx")
|| Path::new(filepath) || Path::new(filepath)
.file_stem() .file_stem()
.unwrap_or(OsStr::new(".")) .unwrap_or_else(|| OsStr::new("."))
.to_str() .to_str()
.unwrap() .unwrap()
.trim() .trim()
.starts_with(".") .starts_with('.')
{ {
AlertMessage::alert( AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()), &mut BufWriter::new(std::io::stderr().lock()),
@@ -153,8 +159,8 @@ impl App {
} }
self.analysis_files(vec![PathBuf::from(filepath)]); self.analysis_files(vec![PathBuf::from(filepath)]);
} else if let Some(directory) = configs::CONFIG.read().unwrap().args.value_of("directory") { } else if let Some(directory) = configs::CONFIG.read().unwrap().args.value_of("directory") {
let evtx_files = self.collect_evtxfiles(&directory); let evtx_files = self.collect_evtxfiles(directory);
if evtx_files.len() == 0 { if evtx_files.is_empty() {
AlertMessage::alert( AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()), &mut BufWriter::new(std::io::stderr().lock()),
&"No .evtx files were found.".to_string(), &"No .evtx files were found.".to_string(),
@@ -174,9 +180,9 @@ impl App {
} }
let analysis_end_time: DateTime<Local> = Local::now(); let analysis_end_time: DateTime<Local> = Local::now();
let analysis_duration = analysis_end_time.signed_duration_since(analysis_start_time); let analysis_duration = analysis_end_time.signed_duration_since(analysis_start_time);
println!(""); println!();
println!("Elapsed Time: {}", &analysis_duration.hhmmssxxx()); println!("Elapsed Time: {}", &analysis_duration.hhmmssxxx());
println!(""); println!();
// Qオプションを付けた場合もしくはパースのエラーがない場合はerrorのstackが9となるのでエラーログファイル自体が生成されない。 // Qオプションを付けた場合もしくはパースのエラーがない場合はerrorのstackが9となるのでエラーログファイル自体が生成されない。
if ERROR_LOG_STACK.lock().unwrap().len() > 0 { if ERROR_LOG_STACK.lock().unwrap().len() > 0 {
@@ -200,7 +206,7 @@ impl App {
let log_dir = env::var("windir").expect("windir is not found"); let log_dir = env::var("windir").expect("windir is not found");
let evtx_files = let evtx_files =
self.collect_evtxfiles(&[log_dir, "System32\\winevt\\Logs".to_string()].join("/")); self.collect_evtxfiles(&[log_dir, "System32\\winevt\\Logs".to_string()].join("/"));
if evtx_files.len() == 0 { if evtx_files.is_empty() {
AlertMessage::alert( AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()), &mut BufWriter::new(std::io::stderr().lock()),
&"No .evtx files were found.".to_string(), &"No .evtx files were found.".to_string(),
@@ -208,14 +214,14 @@ impl App {
.ok(); .ok();
return None; return None;
} }
return Some(evtx_files); Some(evtx_files)
} else { } else {
AlertMessage::alert( AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()), &mut BufWriter::new(std::io::stderr().lock()),
&"-l / --liveanalysis needs to be run as Administrator on Windows.\r\n".to_string(), &"-l / --liveanalysis needs to be run as Administrator on Windows.\r\n".to_string(),
) )
.ok(); .ok();
return None; None
} }
} }
@@ -243,27 +249,27 @@ impl App {
let path = e.unwrap().path(); let path = e.unwrap().path();
if path.is_dir() { if path.is_dir() {
path.to_str().and_then(|path_str| { path.to_str().map(|path_str| {
let subdir_ret = self.collect_evtxfiles(path_str); let subdir_ret = self.collect_evtxfiles(path_str);
ret.extend(subdir_ret); ret.extend(subdir_ret);
return Option::Some(()); Option::Some(())
}); });
} else { } else {
let path_str = path.to_str().unwrap_or(""); let path_str = path.to_str().unwrap_or("");
if path_str.ends_with(".evtx") if path_str.ends_with(".evtx")
&& !Path::new(path_str) && !Path::new(path_str)
.file_stem() .file_stem()
.unwrap_or(OsStr::new(".")) .unwrap_or_else(|| OsStr::new("."))
.to_str() .to_str()
.unwrap() .unwrap()
.starts_with(".") .starts_with('.')
{ {
ret.push(path); ret.push(path);
} }
} }
} }
return ret; ret
} }
fn print_contributors(&self) { fn print_contributors(&self) {
@@ -295,7 +301,7 @@ impl App {
&filter::exclude_ids(), &filter::exclude_ids(),
); );
if rule_files.len() == 0 { if rule_files.is_empty() {
AlertMessage::alert( AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()), &mut BufWriter::new(std::io::stderr().lock()),
&"No rules were loaded. Please download the latest rules with the --update-rules option.\r\n".to_string(), &"No rules were loaded. Please download the latest rules with the --update-rules option.\r\n".to_string(),
@@ -369,14 +375,14 @@ impl App {
// target_eventids.txtでフィルタする。 // target_eventids.txtでフィルタする。
let data = record_result.unwrap().data; let data = record_result.unwrap().data;
if self._is_target_event_id(&data) == false { if !self._is_target_event_id(&data) {
continue; continue;
} }
// EvtxRecordInfo構造体に変更 // EvtxRecordInfo構造体に変更
records_per_detect.push(data); records_per_detect.push(data);
} }
if records_per_detect.len() == 0 { if records_per_detect.is_empty() {
break; break;
} }
@@ -397,7 +403,7 @@ impl App {
tl.tm_stats_dsp_msg(); tl.tm_stats_dsp_msg();
return detection; detection
} }
async fn create_rec_infos( async fn create_rec_infos(
@@ -407,28 +413,28 @@ impl App {
) -> Vec<EvtxRecordInfo> { ) -> Vec<EvtxRecordInfo> {
let path = Arc::new(path.to_string()); let path = Arc::new(path.to_string());
let rule_keys = Arc::new(rule_keys); let rule_keys = Arc::new(rule_keys);
let threads: Vec<JoinHandle<EvtxRecordInfo>> = records_per_detect let threads: Vec<JoinHandle<EvtxRecordInfo>> = {
let this = records_per_detect
.into_iter() .into_iter()
.map(|rec| { .map(|rec| -> JoinHandle<EvtxRecordInfo> {
let arc_rule_keys = Arc::clone(&rule_keys); let arc_rule_keys = Arc::clone(&rule_keys);
let arc_path = Arc::clone(&path); let arc_path = Arc::clone(&path);
return spawn(async move { spawn(async move {
let rec_info = utils::create_rec_info(rec, arc_path.to_string(), &arc_rule_keys)
utils::create_rec_info(rec, arc_path.to_string(), &arc_rule_keys);
return rec_info;
});
}) })
.collect(); });
FromIterator::from_iter(this)
};
let mut ret = vec![]; let mut ret = vec![];
for thread in threads.into_iter() { for thread in threads.into_iter() {
ret.push(thread.await.unwrap()); ret.push(thread.await.unwrap());
} }
return ret; ret
} }
fn get_all_keys(&self, rules: &Vec<RuleNode>) -> Vec<String> { fn get_all_keys(&self, rules: &[RuleNode]) -> Vec<String> {
let mut key_set = HashSet::new(); let mut key_set = HashSet::new();
for rule in rules { for rule in rules {
let keys = get_detection_keys(rule); let keys = get_detection_keys(rule);
@@ -436,7 +442,7 @@ impl App {
} }
let ret: Vec<String> = key_set.into_iter().collect(); let ret: Vec<String> = key_set.into_iter().collect();
return ret; ret
} }
// target_eventids.txtの設定を元にフィルタする。 // target_eventids.txtの設定を元にフィルタする。
@@ -446,11 +452,11 @@ impl App {
return true; return true;
} }
return match eventid.unwrap() { match eventid.unwrap() {
Value::String(s) => utils::is_target_event_id(s), Value::String(s) => utils::is_target_event_id(s),
Value::Number(n) => utils::is_target_event_id(&n.to_string()), Value::Number(n) => utils::is_target_event_id(&n.to_string()),
_ => true, // レコードからEventIdが取得できない場合は、特にフィルタしない _ => true, // レコードからEventIdが取得できない場合は、特にフィルタしない
}; }
} }
fn evtx_to_jsons(&self, evtx_filepath: PathBuf) -> Option<EvtxParser<File>> { fn evtx_to_jsons(&self, evtx_filepath: PathBuf) -> Option<EvtxParser<File>> {
@@ -462,11 +468,11 @@ impl App {
parse_config = parse_config.num_threads(0); // 設定しないと遅かったので、設定しておく。 parse_config = parse_config.num_threads(0); // 設定しないと遅かったので、設定しておく。
let evtx_parser = evtx_parser.with_configuration(parse_config); let evtx_parser = evtx_parser.with_configuration(parse_config);
return Option::Some(evtx_parser); Option::Some(evtx_parser)
} }
Err(e) => { Err(e) => {
eprintln!("{}", e); eprintln!("{}", e);
return Option::None; Option::None
} }
} }
} }
@@ -479,8 +485,8 @@ impl App {
/// output logo /// output logo
fn output_logo(&self) { fn output_logo(&self) {
let fp = &format!("art/logo.txt"); let fp = &"art/logo.txt".to_string();
let content = fs::read_to_string(fp).unwrap_or("".to_owned()); let content = fs::read_to_string(fp).unwrap_or_default();
println!("{}", content); println!("{}", content);
} }
@@ -495,7 +501,7 @@ impl App {
match eggs.get(exec_datestr) { match eggs.get(exec_datestr) {
None => {} None => {}
Some(path) => { Some(path) => {
let content = fs::read_to_string(path).unwrap_or("".to_owned()); let content = fs::read_to_string(path).unwrap_or_default();
println!("{}", content); println!("{}", content);
} }
} }

View File

@@ -18,7 +18,7 @@ impl SlackNotify {
eprintln!("WEBHOOK_URL not found"); eprintln!("WEBHOOK_URL not found");
return false; return false;
} }
return true; true
} }
// send message to slack. // send message to slack.

View File

@@ -1,2 +1,2 @@
pub mod statistics; pub mod statistics;
pub mod timeline; pub mod timelines;

View File

@@ -20,16 +20,16 @@ impl EventStatistics {
end_time: String, end_time: String,
stats_list: HashMap<String, usize>, stats_list: HashMap<String, usize>,
) -> EventStatistics { ) -> EventStatistics {
return EventStatistics { EventStatistics {
total, total,
filepath, filepath,
start_time, start_time,
end_time, end_time,
stats_list, stats_list,
}; }
} }
pub fn start(&mut self, records: &Vec<EvtxRecordInfo>) { pub fn start(&mut self, records: &[EvtxRecordInfo]) {
// 引数でstatisticsオプションが指定されている時だけ、統計情報を出力する。 // 引数でstatisticsオプションが指定されている時だけ、統計情報を出力する。
if !configs::CONFIG if !configs::CONFIG
.read() .read()
@@ -49,8 +49,8 @@ impl EventStatistics {
self.stats_eventid(records); self.stats_eventid(records);
} }
fn stats_time_cnt(&mut self, records: &Vec<EvtxRecordInfo>) { fn stats_time_cnt(&mut self, records: &[EvtxRecordInfo]) {
if records.len() == 0 { if records.is_empty() {
return; return;
} }
self.filepath = records[0].evtx_filepath.as_str().to_owned(); self.filepath = records[0].evtx_filepath.as_str().to_owned();
@@ -62,18 +62,16 @@ impl EventStatistics {
&"Event.System.TimeCreated_attributes.SystemTime".to_string(), &"Event.System.TimeCreated_attributes.SystemTime".to_string(),
&record.record, &record.record,
) )
.and_then(|evt_value| { .map(|evt_value| evt_value.to_string());
return Option::Some(evt_value.to_string());
});
if evttime.is_none() { if evttime.is_none() {
continue; continue;
} }
let evttime = evttime.unwrap(); let evttime = evttime.unwrap();
if self.start_time.len() == 0 || evttime < self.start_time { if self.start_time.is_empty() || evttime < self.start_time {
self.start_time = evttime.to_string(); self.start_time = evttime.to_string();
} }
if self.end_time.len() == 0 || evttime > self.end_time { if self.end_time.is_empty() || evttime > self.end_time {
self.end_time = evttime; self.end_time = evttime;
} }
} }
@@ -81,7 +79,7 @@ impl EventStatistics {
} }
// EventIDで集計 // EventIDで集計
fn stats_eventid(&mut self, records: &Vec<EvtxRecordInfo>) { fn stats_eventid(&mut self, records: &[EvtxRecordInfo]) {
// let mut evtstat_map = HashMap::new(); // let mut evtstat_map = HashMap::new();
for record in records.iter() { for record in records.iter() {
let evtid = utils::get_event_value(&"EventID".to_string(), &record.record); let evtid = utils::get_event_value(&"EventID".to_string(), &record.record);

View File

@@ -8,6 +8,12 @@ pub struct Timeline {
pub stats: EventStatistics, pub stats: EventStatistics,
} }
impl Default for Timeline {
fn default() -> Self {
Self::new()
}
}
impl Timeline { impl Timeline {
pub fn new() -> Timeline { pub fn new() -> Timeline {
let totalcnt = 0; let totalcnt = 0;
@@ -17,10 +23,10 @@ impl Timeline {
let statslst = HashMap::new(); let statslst = HashMap::new();
let statistic = EventStatistics::new(totalcnt, filepath, starttm, endtm, statslst); let statistic = EventStatistics::new(totalcnt, filepath, starttm, endtm, statslst);
return Timeline { stats: statistic }; Timeline { stats: statistic }
} }
pub fn start(&mut self, records: &Vec<EvtxRecordInfo>) { pub fn start(&mut self, records: &[EvtxRecordInfo]) {
self.stats.start(records); self.stats.start(records);
} }
@@ -46,7 +52,7 @@ impl Timeline {
// 集計件数でソート // 集計件数でソート
let mut mapsorted: Vec<_> = self.stats.stats_list.iter().collect(); let mut mapsorted: Vec<_> = self.stats.stats_list.iter().collect();
mapsorted.sort_by(|x, y| y.1.cmp(&x.1)); mapsorted.sort_by(|x, y| y.1.cmp(x.1));
// イベントID毎の出力メッセージ生成 // イベントID毎の出力メッセージ生成
let stats_msges: Vec<String> = self.tm_stats_set_msg(mapsorted); let stats_msges: Vec<String> = self.tm_stats_set_msg(mapsorted);
@@ -95,6 +101,6 @@ impl Timeline {
} }
} }
msges.push("---------------------------------------".to_string()); msges.push("---------------------------------------".to_string());
return msges; msges
} }
} }

View File

@@ -23,6 +23,12 @@ pub struct ParseYaml {
pub errorrule_count: u128, pub errorrule_count: u128,
} }
impl Default for ParseYaml {
fn default() -> Self {
Self::new()
}
}
impl ParseYaml { impl ParseYaml {
pub fn new() -> ParseYaml { pub fn new() -> ParseYaml {
ParseYaml { ParseYaml {
@@ -37,7 +43,7 @@ impl ParseYaml {
let mut file_content = String::new(); let mut file_content = String::new();
let mut fr = fs::File::open(path) let mut fr = fs::File::open(path)
.map(|f| BufReader::new(f)) .map(BufReader::new)
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
fr.read_to_string(&mut file_content) fr.read_to_string(&mut file_content)
@@ -76,7 +82,7 @@ impl ParseYaml {
.as_ref() .as_ref()
.to_path_buf() .to_path_buf()
.extension() .extension()
.unwrap_or(OsStr::new("")) .unwrap_or_else(|| OsStr::new(""))
!= "yml" != "yml"
{ {
return io::Result::Ok(String::default()); return io::Result::Ok(String::default());
@@ -126,7 +132,7 @@ impl ParseYaml {
yaml_docs.extend(yaml_contents.unwrap().into_iter().map(|yaml_content| { yaml_docs.extend(yaml_contents.unwrap().into_iter().map(|yaml_content| {
let filepath = format!("{}", path.as_ref().to_path_buf().display()); let filepath = format!("{}", path.as_ref().to_path_buf().display());
return (filepath, yaml_content); (filepath, yaml_content)
})); }));
} else { } else {
let mut entries = fs::read_dir(path)?; let mut entries = fs::read_dir(path)?;
@@ -144,7 +150,7 @@ impl ParseYaml {
// 拡張子がymlでないファイルは無視 // 拡張子がymlでないファイルは無視
let path = entry.path(); let path = entry.path();
if path.extension().unwrap_or(OsStr::new("")) != "yml" { if path.extension().unwrap_or_else(|| OsStr::new("")) != "yml" {
return io::Result::Ok(ret); return io::Result::Ok(ret);
} }
@@ -192,10 +198,10 @@ impl ParseYaml {
let yaml_contents = yaml_contents.unwrap().into_iter().map(|yaml_content| { let yaml_contents = yaml_contents.unwrap().into_iter().map(|yaml_content| {
let filepath = format!("{}", entry.path().display()); let filepath = format!("{}", entry.path().display());
return (filepath, yaml_content); (filepath, yaml_content)
}); });
ret.extend(yaml_contents); ret.extend(yaml_contents);
return io::Result::Ok(ret); io::Result::Ok(ret)
})?; })?;
} }
@@ -254,11 +260,11 @@ impl ParseYaml {
} }
} }
return Option::Some((filepath, yaml_doc)); Option::Some((filepath, yaml_doc))
}) })
.collect(); .collect();
self.files.extend(files); self.files.extend(files);
return io::Result::Ok(String::default()); io::Result::Ok(String::default())
} }
} }
@@ -329,7 +335,7 @@ mod tests {
let path = Path::new("test_files/rules/yaml/error.yml"); let path = Path::new("test_files/rules/yaml/error.yml");
let ret = yaml.read_file(path.to_path_buf()).unwrap(); let ret = yaml.read_file(path.to_path_buf()).unwrap();
let rule = YamlLoader::load_from_str(&ret); let rule = YamlLoader::load_from_str(&ret);
assert_eq!(rule.is_err(), true); assert!(rule.is_err());
} }
#[test] #[test]
@@ -337,7 +343,7 @@ mod tests {
fn test_default_level_read_yaml() { fn test_default_level_read_yaml() {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/level_yaml"); let path = Path::new("test_files/rules/level_yaml");
yaml.read_dir(path.to_path_buf(), &"", &filter::exclude_ids()) yaml.read_dir(path.to_path_buf(), "", &filter::exclude_ids())
.unwrap(); .unwrap();
assert_eq!(yaml.files.len(), 5); assert_eq!(yaml.files.len(), 5);
} }
@@ -346,7 +352,7 @@ mod tests {
fn test_info_level_read_yaml() { fn test_info_level_read_yaml() {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/level_yaml"); let path = Path::new("test_files/rules/level_yaml");
yaml.read_dir(path.to_path_buf(), &"informational", &filter::exclude_ids()) yaml.read_dir(path.to_path_buf(), "informational", &filter::exclude_ids())
.unwrap(); .unwrap();
assert_eq!(yaml.files.len(), 5); assert_eq!(yaml.files.len(), 5);
} }
@@ -354,7 +360,7 @@ mod tests {
fn test_low_level_read_yaml() { fn test_low_level_read_yaml() {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/level_yaml"); let path = Path::new("test_files/rules/level_yaml");
yaml.read_dir(path.to_path_buf(), &"LOW", &filter::exclude_ids()) yaml.read_dir(path.to_path_buf(), "LOW", &filter::exclude_ids())
.unwrap(); .unwrap();
assert_eq!(yaml.files.len(), 4); assert_eq!(yaml.files.len(), 4);
} }
@@ -362,7 +368,7 @@ mod tests {
fn test_medium_level_read_yaml() { fn test_medium_level_read_yaml() {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/level_yaml"); let path = Path::new("test_files/rules/level_yaml");
yaml.read_dir(path.to_path_buf(), &"MEDIUM", &filter::exclude_ids()) yaml.read_dir(path.to_path_buf(), "MEDIUM", &filter::exclude_ids())
.unwrap(); .unwrap();
assert_eq!(yaml.files.len(), 3); assert_eq!(yaml.files.len(), 3);
} }
@@ -370,7 +376,7 @@ mod tests {
fn test_high_level_read_yaml() { fn test_high_level_read_yaml() {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/level_yaml"); let path = Path::new("test_files/rules/level_yaml");
yaml.read_dir(path.to_path_buf(), &"HIGH", &filter::exclude_ids()) yaml.read_dir(path.to_path_buf(), "HIGH", &filter::exclude_ids())
.unwrap(); .unwrap();
assert_eq!(yaml.files.len(), 2); assert_eq!(yaml.files.len(), 2);
} }
@@ -378,7 +384,7 @@ mod tests {
fn test_critical_level_read_yaml() { fn test_critical_level_read_yaml() {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/level_yaml"); let path = Path::new("test_files/rules/level_yaml");
yaml.read_dir(path.to_path_buf(), &"CRITICAL", &filter::exclude_ids()) yaml.read_dir(path.to_path_buf(), "CRITICAL", &filter::exclude_ids())
.unwrap(); .unwrap();
assert_eq!(yaml.files.len(), 1); assert_eq!(yaml.files.len(), 1);
} }
@@ -388,7 +394,7 @@ mod tests {
let mut yaml = yaml::ParseYaml::new(); let mut yaml = yaml::ParseYaml::new();
let path = Path::new("test_files/rules/yaml"); let path = Path::new("test_files/rules/yaml");
yaml.read_dir(path.to_path_buf(), &"", &filter::exclude_ids()) yaml.read_dir(path.to_path_buf(), "", &filter::exclude_ids())
.unwrap(); .unwrap();
assert_eq!(yaml.ignorerule_count, 10); assert_eq!(yaml.ignorerule_count, 10);
} }
@@ -401,8 +407,7 @@ mod tests {
let exclude_ids = RuleExclude { let exclude_ids = RuleExclude {
no_use_rule: HashSet::new(), no_use_rule: HashSet::new(),
}; };
yaml.read_dir(path.to_path_buf(), &"", &exclude_ids) yaml.read_dir(path.to_path_buf(), "", &exclude_ids).unwrap();
.unwrap();
assert_eq!(yaml.ignorerule_count, 0); assert_eq!(yaml.ignorerule_count, 0);
} }
#[test] #[test]
@@ -412,8 +417,7 @@ mod tests {
let exclude_ids = RuleExclude { let exclude_ids = RuleExclude {
no_use_rule: HashSet::new(), no_use_rule: HashSet::new(),
}; };
yaml.read_dir(path.to_path_buf(), &"", &exclude_ids) yaml.read_dir(path.to_path_buf(), "", &exclude_ids).unwrap();
.unwrap();
assert_eq!(yaml.ignorerule_count, 1); assert_eq!(yaml.ignorerule_count, 1);
} }
} }