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として処理する
AlertMessage::warn(
&mut BufWriter::new(std::io::stderr().lock()),
&read_result.as_ref().unwrap_err(),
read_result.as_ref().unwrap_err(),
)
.ok();
return None;
@@ -71,12 +71,12 @@ pub fn set_output_color() -> Option<HashMap<String, Vec<u8>>> {
return;
}
let color_code = convert_color_result.unwrap();
if level.len() == 0 || color_code.len() < 3 {
if level.is_empty() || color_code.len() < 3 {
return;
}
color_map.insert(level.to_string(), color_code);
});
return Some(color_map);
Some(color_map)
}
pub fn after_fact() {
@@ -142,7 +142,7 @@ fn emit_csv<W: std::io::Write>(
if displayflag {
if color_map.is_some() {
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 {
timestamp: &format!(
"{} ",
@@ -227,10 +227,10 @@ fn emit_csv<W: std::io::Write>(
total_detect_counts_by_level[level_suffix] += 1;
}
}
println!("");
println!();
wtr.flush()?;
println!("");
println!();
_print_unique_results(
total_detect_counts_by_level,
"Total".to_string(),
@@ -285,7 +285,7 @@ fn _print_unique_results(
output_str = output_raw_str;
} else {
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
.truecolor(output_color[0], output_color[1], output_color[2])
.to_string();
@@ -296,14 +296,14 @@ fn _print_unique_results(
}
/// 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で出力する
let mut output_color: Vec<u8> = vec![255, 255, 255];
let target_color = color_map.get(level);
if target_color.is_some() {
output_color = target_color.unwrap().to_vec();
if let Some(color) = target_color {
output_color = color.to_vec();
}
return output_color;
output_color
}
fn format_time(time: &DateTime<Utc>) -> String {
@@ -319,11 +319,11 @@ where
Tz::Offset: std::fmt::Display,
{
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") {
return time.to_rfc3339();
time.to_rfc3339()
} 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 {
use crate::afterfact::emit_csv;
use crate::detections::print;
use crate::detections::print::DetectInfo;
use chrono::{Local, TimeZone, Utc};
use serde_json::Value;
use std::fs::File;
@@ -372,15 +373,18 @@ mod tests {
"##;
let event: Value = serde_json::from_str(val).unwrap();
messages.insert(
testfilepath.to_string(),
testrulepath.to_string(),
&event,
test_level.to_string(),
test_computername.to_string(),
test_eventid.to_string(),
test_title.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
@@ -452,15 +456,18 @@ mod tests {
"##;
let event: Value = serde_json::from_str(val).unwrap();
messages.insert(
testfilepath.to_string(),
testrulepath.to_string(),
&event,
test_level.to_string(),
test_computername.to_string(),
test_eventid.to_string(),
test_title.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();
}
@@ -520,6 +527,6 @@ mod tests {
let white_color_header = "\u{1b}[38;2;255;255;255m";
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("HIGH".to_owned(), 4);
levelmap.insert("CRITICAL".to_owned(), 5);
return levelmap;
levelmap
};
pub static ref EVENTKEY_ALIAS: EventKeyAliasConfig =
load_eventkey_alias("./rules/config/eventkey_alias.txt");
@@ -29,6 +29,12 @@ pub struct ConfigReader {
pub target_eventids: TargetEventIds,
}
impl Default for ConfigReader {
fn default() -> Self {
Self::new()
}
}
impl ConfigReader {
pub fn new() -> Self {
ConfigReader {
@@ -41,7 +47,7 @@ impl ConfigReader {
fn build_app<'a>() -> ArgMatches<'a> {
let program = std::env::args()
.nth(0)
.next()
.and_then(|s| {
std::path::PathBuf::from(s)
.file_stem()
@@ -91,7 +97,7 @@ fn is_test_mode() -> bool {
}
}
return false;
false
}
#[derive(Debug, Clone)]
@@ -99,19 +105,25 @@ pub struct TargetEventIds {
ids: HashSet<String>,
}
impl TargetEventIds {
pub fn new() -> TargetEventIds {
return TargetEventIds {
ids: HashSet::new(),
};
impl Default for TargetEventIds {
fn default() -> Self {
Self::new()
}
}
pub fn is_target(&self, id: &String) -> bool {
impl TargetEventIds {
pub fn new() -> TargetEventIds {
TargetEventIds {
ids: HashSet::new(),
}
}
pub fn is_target(&self, id: &str) -> bool {
// 中身が空の場合は全EventIdを対象とする。
if self.ids.is_empty() {
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() {
AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()),
&lines.as_ref().unwrap_err(),
lines.as_ref().unwrap_err(),
)
.ok();
return ret;
@@ -134,7 +146,7 @@ fn load_target_ids(path: &str) -> TargetEventIds {
ret.ids.insert(line);
}
return ret;
ret
}
#[derive(Debug, Clone)]
@@ -143,6 +155,12 @@ pub struct TargetEventTime {
end_time: Option<DateTime<Utc>>,
}
impl Default for TargetEventTime {
fn default() -> Self {
Self::new()
}
}
impl TargetEventTime {
pub fn new() -> Self {
let start_time =
@@ -180,17 +198,17 @@ impl TargetEventTime {
} else {
None
};
return Self::set(start_time, end_time);
Self::set(start_time, end_time)
}
pub fn set(
start_time: Option<chrono::DateTime<chrono::Utc>>,
end_time: Option<chrono::DateTime<chrono::Utc>>,
input_start_time: Option<chrono::DateTime<chrono::Utc>>,
input_end_time: Option<chrono::DateTime<chrono::Utc>>,
) -> Self {
return Self {
start_time: start_time,
end_time: end_time,
};
Self {
start_time: input_start_time,
end_time: input_end_time,
}
}
pub fn is_target(&self, eventtime: &Option<DateTime<Utc>>) -> bool {
@@ -207,7 +225,7 @@ impl TargetEventTime {
return false;
}
}
return true;
true
}
}
@@ -219,18 +237,24 @@ pub struct EventKeyAliasConfig {
impl EventKeyAliasConfig {
pub fn new() -> EventKeyAliasConfig {
return EventKeyAliasConfig {
EventKeyAliasConfig {
key_to_eventkey: HashMap::new(),
key_to_split_eventkey: HashMap::new(),
};
}
}
pub fn get_event_key(&self, alias: &String) -> Option<&String> {
return self.key_to_eventkey.get(alias);
pub fn get_event_key(&self, alias: &str) -> Option<&String> {
self.key_to_eventkey.get(alias)
}
pub fn get_event_key_split(&self, alias: &String) -> Option<&Vec<usize>> {
return self.key_to_split_eventkey.get(alias);
pub fn get_event_key_split(&self, alias: &str) -> Option<&Vec<usize>> {
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() {
AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()),
&read_result.as_ref().unwrap_err(),
read_result.as_ref().unwrap_err(),
)
.ok();
return config;
@@ -255,20 +279,20 @@ fn load_eventkey_alias(path: &str) -> EventKeyAliasConfig {
let empty = &"".to_string();
let alias = line.get(0).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;
}
config
.key_to_eventkey
.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
.key_to_split_eventkey
.insert(alias.to_owned(), splits);
});
config.key_to_eventkey.shrink_to_fit();
return config;
config
}
#[derive(Debug, Clone)]
@@ -278,16 +302,22 @@ pub struct EventInfo {
pub comment: String,
}
impl Default for EventInfo {
fn default() -> Self {
Self::new()
}
}
impl EventInfo {
pub fn new() -> EventInfo {
let evttitle = "Unknown".to_string();
let detectflg = "".to_string();
let comment = "".to_string();
return EventInfo {
EventInfo {
evttitle,
detectflg,
comment,
};
}
}
}
#[derive(Debug, Clone)]
@@ -295,14 +325,20 @@ pub struct EventInfoConfig {
eventinfo: HashMap<String, EventInfo>,
}
impl Default for EventInfoConfig {
fn default() -> Self {
Self::new()
}
}
impl EventInfoConfig {
pub fn new() -> EventInfoConfig {
return EventInfoConfig {
EventInfoConfig {
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() {
AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()),
&read_result.as_ref().unwrap_err(),
read_result.as_ref().unwrap_err(),
)
.ok();
return config;
@@ -339,7 +375,7 @@ fn load_eventcode_info(path: &str) -> EventInfoConfig {
.eventinfo
.insert(eventcode.to_owned(), infodata.to_owned());
});
return config;
config
}
#[cfg(test)]
@@ -375,9 +411,9 @@ mod tests {
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());
assert_eq!(time_filter.is_target(&out_of_range1), false);
assert_eq!(time_filter.is_target(&within_range), true);
assert_eq!(time_filter.is_target(&out_of_range2), false);
assert!(!time_filter.is_target(&out_of_range1));
assert!(time_filter.is_target(&within_range));
assert!(!time_filter.is_target(&out_of_range2));
}
#[test]
@@ -386,7 +422,7 @@ mod tests {
let end_time = Some("2020-03-30T12:00:09Z".parse::<DateTime<Utc>>().unwrap());
let time_filter = configs::TargetEventTime::set(start_time, end_time);
assert_eq!(time_filter.is_target(&start_time), true);
assert_eq!(time_filter.is_target(&end_time), true);
assert!(time_filter.is_target(&start_time));
assert!(time_filter.is_target(&end_time));
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -19,25 +19,25 @@ use std::string::String;
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
.iter()
.fold("detection -> selection".to_string(), |mut 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 {
if regex.is_match(string) == false {
if !regex.is_match(string) {
continue;
}
return true;
}
return false;
false
}
/// 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>,
replace_rule: Option<&'a DataFilterRule>,
) -> Option<String> {
if input_str.is_none() {
return None;
}
input_str?;
if replace_rule.is_none() {
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_str = &replace_rule.unwrap().replace_str;
return Some(
Some(
replace_regex_rule
.replace_all(input_str.unwrap(), replace_str)
.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 {
if regex.is_match(target) {
return true;
}
}
return false;
false
}
pub fn value_to_string(value: &Value) -> Option<String> {
return match value {
match value {
Value::Null => Option::None,
Value::Bool(b) => Option::Some(b.to_string()),
Value::Number(n) => Option::Some(n.to_string()),
Value::String(s) => Option::Some(s.to_string()),
Value::Array(_) => Option::None,
Value::Object(_) => Option::None,
};
}
}
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);
}
let reader = BufReader::new(f.unwrap());
return Result::Ok(
Result::Ok(
reader
.lines()
.map(|line| line.unwrap_or(String::default()))
.map(|line| line.unwrap_or_default())
.collect(),
);
)
}
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 ret = vec![];
let read_res = f.unwrap().read_to_string(&mut contents);
if read_res.is_err() {
return Result::Err(read_res.unwrap_err().to_string());
if let Err(e) = read_res {
return Result::Err(e.to_string());
}
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);
});
return Result::Ok(ret);
Result::Ok(ret)
}
pub fn is_target_event_id(s: &String) -> bool {
return configs::CONFIG.read().unwrap().target_eventids.is_target(s);
pub fn is_target_event_id(s: &str) -> bool {
configs::CONFIG.read().unwrap().target_eventids.is_target(s)
}
pub fn get_event_id_key() -> String {
return "Event.System.EventID".to_string();
"Event.System.EventID".to_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>> {
@@ -146,30 +144,24 @@ pub fn str_time_to_datetime(system_time_str: &str) -> Option<DateTime<Utc>> {
if rfc3339_time.is_err() {
return Option::None;
}
let datetime = Utc
.from_local_datetime(&rfc3339_time.unwrap().naive_utc())
.single();
if datetime.is_none() {
return Option::None;
} else {
return Option::Some(datetime.unwrap());
}
Utc.from_local_datetime(&rfc3339_time.unwrap().naive_utc())
.single()
}
/// serde:Valueの型を確認し、文字列を返します。
pub fn get_serde_number_to_string(value: &serde_json::Value) -> Option<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() {
// Object type is not specified record value.
return Option::None;
Option::None
} 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> {
if key.len() == 0 {
pub fn get_event_value<'a>(key: &str, event_value: &'a Value) -> Option<&'a Value> {
if key.is_empty() {
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 mut start_idx = 0;
for key in splits.unwrap() {
if ret.is_object() == false {
if !ret.is_object() {
return Option::None;
}
let val = &event_key[start_idx..(*key + start_idx)];
ret = &ret[val];
start_idx = *key + start_idx;
start_idx += *key;
start_idx += 1;
}
return Option::Some(ret);
Option::Some(ret)
} else {
let mut ret: &Value = event_value;
let event_key = key;
for key in event_key.split(".") {
if ret.is_object() == false {
for key in event_key.split('.') {
if !ret.is_object() {
return Option::None;
}
ret = &ret[key];
}
return Option::Some(ret);
Option::Some(ret)
}
}
@@ -211,20 +203,20 @@ pub fn get_thread_num() -> usize {
let threadnum = &conf
.args
.value_of("thread-number")
.unwrap_or(def_thread_num_str.as_str());
return threadnum.parse::<usize>().unwrap().clone();
.unwrap_or_else(|| def_thread_num_str.as_str());
threadnum.parse::<usize>().unwrap()
}
pub fn create_tokio_runtime() -> Runtime {
return Builder::new_multi_thread()
Builder::new_multi_thread()
.worker_threads(get_thread_num())
.thread_name("yea-thread")
.build()
.unwrap();
.unwrap()
}
// 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を作る
let data_str = data.to_string();
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());
}
return rec;
rec
}
#[cfg(test)]
@@ -267,30 +259,32 @@ mod tests {
#[test]
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()
.into_iter()
.map(|regex_str| Regex::new(&regex_str).unwrap())
.collect();
let regextext = utils::check_regex("\\cvtres.exe", &regexes);
assert!(regextext == true);
assert!(regextext);
let regextext = utils::check_regex("\\hogehoge.exe", &regexes);
assert!(regextext == false);
assert!(!regextext);
}
#[test]
fn test_check_allowlist() {
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()
.into_iter()
.map(|allow_str| Regex::new(&allow_str).unwrap())
.collect();
assert!(true == utils::check_allowlist(commandline, &allowlist));
assert!(utils::check_allowlist(commandline, &allowlist));
let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate2.exe\"";
assert!(false == utils::check_allowlist(commandline, &allowlist));
assert!(!utils::check_allowlist(commandline, &allowlist));
}
#[test]
@@ -360,15 +354,9 @@ mod tests {
};
let none_test_str: Option<&String> = None;
assert_eq!(
utils::replace_target_character(none_test_str, None).is_none(),
true
);
assert!(utils::replace_target_character(none_test_str, None).is_none());
assert_eq!(
utils::replace_target_character(none_test_str, Some(&test_filter_rule)).is_none(),
true
);
assert!(utils::replace_target_character(none_test_str, Some(&test_filter_rule)).is_none());
let tmp = "h\ra\ny\ta\tb\nu\r\nsa".to_string();
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 regex_str = line.get(1).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;
}
let regex_rule: Option<Regex> = match Regex::new(regex_str) {
Ok(regex) => Some(regex),
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") {
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg)
.ok();
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), errmsg).ok();
}
if !*QUIET_ERRORS_FLAG {
ERROR_LOG_STACK
@@ -85,7 +84,7 @@ fn load_record_filters() -> HashMap<String, DataFilterRule> {
},
);
});
return ret;
ret
}
#[derive(Clone, Debug)]
@@ -109,7 +108,7 @@ pub fn exclude_ids() -> RuleExclude {
exclude_ids.insert_ids("./rules/config/exclude_rules.txt");
return exclude_ids;
exclude_ids
}
impl RuleExclude {
@@ -129,11 +128,10 @@ impl RuleExclude {
.unwrap()
.push(format!("{} does not exist", filename));
}
return ();
}
let reader = BufReader::new(f.unwrap());
for v in reader.lines() {
let v = v.unwrap().split("#").collect::<Vec<&str>>()[0]
let v = v.unwrap().split('#').collect::<Vec<&str>>()[0]
.trim()
.to_string();
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::omikuji::Omikuji;
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 pbr::ProgressBar;
use serde_json::Value;
@@ -53,19 +53,25 @@ pub struct App {
rule_keys: Vec<String>,
}
impl Default for App {
fn default() -> Self {
Self::new()
}
}
impl App {
pub fn new() -> App {
return App {
App {
rt: utils::create_tokio_runtime(),
rule_keys: Vec::new(),
};
}
}
fn exec(&mut self) {
let analysis_start_time: DateTime<Local> = Local::now();
if !configs::CONFIG.read().unwrap().args.is_present("quiet") {
self.output_logo();
println!("");
println!();
self.output_eggs(&format!(
"{:02}/{:02}",
&analysis_start_time.month().to_owned(),
@@ -98,12 +104,12 @@ impl App {
.ok();
return;
}
if configs::CONFIG.read().unwrap().args.args.len() == 0 {
if configs::CONFIG.read().unwrap().args.args.is_empty() {
println!(
"{}",
configs::CONFIG.read().unwrap().args.usage().to_string()
);
println!("");
println!();
return;
}
if let Some(csv_path) = configs::CONFIG.read().unwrap().args.value_of("output") {
@@ -121,7 +127,7 @@ impl App {
}
if *STATISTICS_FLAG {
println!("Generating Event ID Statistics");
println!("");
println!();
}
if configs::CONFIG
.read()
@@ -138,11 +144,11 @@ impl App {
if !filepath.ends_with(".evtx")
|| Path::new(filepath)
.file_stem()
.unwrap_or(OsStr::new("."))
.unwrap_or_else(|| OsStr::new("."))
.to_str()
.unwrap()
.trim()
.starts_with(".")
.starts_with('.')
{
AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()),
@@ -153,8 +159,8 @@ impl App {
}
self.analysis_files(vec![PathBuf::from(filepath)]);
} else if let Some(directory) = configs::CONFIG.read().unwrap().args.value_of("directory") {
let evtx_files = self.collect_evtxfiles(&directory);
if evtx_files.len() == 0 {
let evtx_files = self.collect_evtxfiles(directory);
if evtx_files.is_empty() {
AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()),
&"No .evtx files were found.".to_string(),
@@ -174,9 +180,9 @@ impl App {
}
let analysis_end_time: DateTime<Local> = Local::now();
let analysis_duration = analysis_end_time.signed_duration_since(analysis_start_time);
println!("");
println!();
println!("Elapsed Time: {}", &analysis_duration.hhmmssxxx());
println!("");
println!();
// Qオプションを付けた場合もしくはパースのエラーがない場合はerrorのstackが9となるのでエラーログファイル自体が生成されない。
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 evtx_files =
self.collect_evtxfiles(&[log_dir, "System32\\winevt\\Logs".to_string()].join("/"));
if evtx_files.len() == 0 {
if evtx_files.is_empty() {
AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()),
&"No .evtx files were found.".to_string(),
@@ -208,14 +214,14 @@ impl App {
.ok();
return None;
}
return Some(evtx_files);
Some(evtx_files)
} else {
AlertMessage::alert(
&mut BufWriter::new(std::io::stderr().lock()),
&"-l / --liveanalysis needs to be run as Administrator on Windows.\r\n".to_string(),
)
.ok();
return None;
None
}
}
@@ -243,27 +249,27 @@ impl App {
let path = e.unwrap().path();
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);
ret.extend(subdir_ret);
return Option::Some(());
Option::Some(())
});
} else {
let path_str = path.to_str().unwrap_or("");
if path_str.ends_with(".evtx")
&& !Path::new(path_str)
.file_stem()
.unwrap_or(OsStr::new("."))
.unwrap_or_else(|| OsStr::new("."))
.to_str()
.unwrap()
.starts_with(".")
.starts_with('.')
{
ret.push(path);
}
}
}
return ret;
ret
}
fn print_contributors(&self) {
@@ -295,7 +301,7 @@ impl App {
&filter::exclude_ids(),
);
if rule_files.len() == 0 {
if rule_files.is_empty() {
AlertMessage::alert(
&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(),
@@ -369,14 +375,14 @@ impl App {
// target_eventids.txtでフィルタする。
let data = record_result.unwrap().data;
if self._is_target_event_id(&data) == false {
if !self._is_target_event_id(&data) {
continue;
}
// EvtxRecordInfo構造体に変更
records_per_detect.push(data);
}
if records_per_detect.len() == 0 {
if records_per_detect.is_empty() {
break;
}
@@ -397,7 +403,7 @@ impl App {
tl.tm_stats_dsp_msg();
return detection;
detection
}
async fn create_rec_infos(
@@ -407,28 +413,28 @@ impl App {
) -> Vec<EvtxRecordInfo> {
let path = Arc::new(path.to_string());
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()
.map(|rec| {
.map(|rec| -> JoinHandle<EvtxRecordInfo> {
let arc_rule_keys = Arc::clone(&rule_keys);
let arc_path = Arc::clone(&path);
return spawn(async move {
let rec_info =
utils::create_rec_info(rec, arc_path.to_string(), &arc_rule_keys);
return rec_info;
});
spawn(async move {
utils::create_rec_info(rec, arc_path.to_string(), &arc_rule_keys)
})
.collect();
});
FromIterator::from_iter(this)
};
let mut ret = vec![];
for thread in threads.into_iter() {
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();
for rule in rules {
let keys = get_detection_keys(rule);
@@ -436,7 +442,7 @@ impl App {
}
let ret: Vec<String> = key_set.into_iter().collect();
return ret;
ret
}
// target_eventids.txtの設定を元にフィルタする。
@@ -446,11 +452,11 @@ impl App {
return true;
}
return match eventid.unwrap() {
match eventid.unwrap() {
Value::String(s) => utils::is_target_event_id(s),
Value::Number(n) => utils::is_target_event_id(&n.to_string()),
_ => true, // レコードからEventIdが取得できない場合は、特にフィルタしない
};
}
}
fn evtx_to_jsons(&self, evtx_filepath: PathBuf) -> Option<EvtxParser<File>> {
@@ -462,11 +468,11 @@ impl App {
parse_config = parse_config.num_threads(0); // 設定しないと遅かったので、設定しておく。
let evtx_parser = evtx_parser.with_configuration(parse_config);
return Option::Some(evtx_parser);
Option::Some(evtx_parser)
}
Err(e) => {
eprintln!("{}", e);
return Option::None;
Option::None
}
}
}
@@ -479,8 +485,8 @@ impl App {
/// output logo
fn output_logo(&self) {
let fp = &format!("art/logo.txt");
let content = fs::read_to_string(fp).unwrap_or("".to_owned());
let fp = &"art/logo.txt".to_string();
let content = fs::read_to_string(fp).unwrap_or_default();
println!("{}", content);
}
@@ -495,7 +501,7 @@ impl App {
match eggs.get(exec_datestr) {
None => {}
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);
}
}

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,12 @@ pub struct Timeline {
pub stats: EventStatistics,
}
impl Default for Timeline {
fn default() -> Self {
Self::new()
}
}
impl Timeline {
pub fn new() -> Timeline {
let totalcnt = 0;
@@ -17,10 +23,10 @@ impl Timeline {
let statslst = HashMap::new();
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);
}
@@ -46,7 +52,7 @@ impl Timeline {
// 集計件数でソート
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毎の出力メッセージ生成
let stats_msges: Vec<String> = self.tm_stats_set_msg(mapsorted);
@@ -95,6 +101,6 @@ impl Timeline {
}
}
msges.push("---------------------------------------".to_string());
return msges;
msges
}
}

View File

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