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:
@@ -49,7 +49,7 @@ pub fn set_output_color() -> Option<HashMap<String, Vec<u8>>> {
|
|||||||
// color情報がない場合は通常の白色の出力が出てくるのみで動作への影響を与えない為warnとして処理する
|
// color情報がない場合は通常の白色の出力が出てくるのみで動作への影響を与えない為warnとして処理する
|
||||||
AlertMessage::warn(
|
AlertMessage::warn(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
&mut BufWriter::new(std::io::stderr().lock()),
|
||||||
&read_result.as_ref().unwrap_err(),
|
read_result.as_ref().unwrap_err(),
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
return None;
|
return None;
|
||||||
@@ -71,12 +71,12 @@ pub fn set_output_color() -> Option<HashMap<String, Vec<u8>>> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let color_code = convert_color_result.unwrap();
|
let color_code = convert_color_result.unwrap();
|
||||||
if level.len() == 0 || color_code.len() < 3 {
|
if level.is_empty() || color_code.len() < 3 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
color_map.insert(level.to_string(), color_code);
|
color_map.insert(level.to_string(), color_code);
|
||||||
});
|
});
|
||||||
return Some(color_map);
|
Some(color_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn after_fact() {
|
pub fn after_fact() {
|
||||||
@@ -142,7 +142,7 @@ fn emit_csv<W: std::io::Write>(
|
|||||||
if displayflag {
|
if displayflag {
|
||||||
if color_map.is_some() {
|
if color_map.is_some() {
|
||||||
let output_color =
|
let output_color =
|
||||||
_get_output_color(&color_map.as_ref().unwrap(), &detect_info.level);
|
_get_output_color(color_map.as_ref().unwrap(), &detect_info.level);
|
||||||
wtr.serialize(DisplayFormat {
|
wtr.serialize(DisplayFormat {
|
||||||
timestamp: &format!(
|
timestamp: &format!(
|
||||||
"{} ",
|
"{} ",
|
||||||
@@ -227,10 +227,10 @@ fn emit_csv<W: std::io::Write>(
|
|||||||
total_detect_counts_by_level[level_suffix] += 1;
|
total_detect_counts_by_level[level_suffix] += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("");
|
println!();
|
||||||
|
|
||||||
wtr.flush()?;
|
wtr.flush()?;
|
||||||
println!("");
|
println!();
|
||||||
_print_unique_results(
|
_print_unique_results(
|
||||||
total_detect_counts_by_level,
|
total_detect_counts_by_level,
|
||||||
"Total".to_string(),
|
"Total".to_string(),
|
||||||
@@ -285,7 +285,7 @@ fn _print_unique_results(
|
|||||||
output_str = output_raw_str;
|
output_str = output_raw_str;
|
||||||
} else {
|
} else {
|
||||||
let output_color =
|
let output_color =
|
||||||
_get_output_color(&color_map.as_ref().unwrap(), &level_name.to_string());
|
_get_output_color(color_map.as_ref().unwrap(), &level_name.to_string());
|
||||||
output_str = output_raw_str
|
output_str = output_raw_str
|
||||||
.truecolor(output_color[0], output_color[1], output_color[2])
|
.truecolor(output_color[0], output_color[1], output_color[2])
|
||||||
.to_string();
|
.to_string();
|
||||||
@@ -296,14 +296,14 @@ fn _print_unique_results(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// levelに対応したtruecolorの値の配列を返す関数
|
/// levelに対応したtruecolorの値の配列を返す関数
|
||||||
fn _get_output_color(color_map: &HashMap<String, Vec<u8>>, level: &String) -> Vec<u8> {
|
fn _get_output_color(color_map: &HashMap<String, Vec<u8>>, level: &str) -> Vec<u8> {
|
||||||
// カラーをつけない場合は255,255,255で出力する
|
// カラーをつけない場合は255,255,255で出力する
|
||||||
let mut output_color: Vec<u8> = vec![255, 255, 255];
|
let mut output_color: Vec<u8> = vec![255, 255, 255];
|
||||||
let target_color = color_map.get(level);
|
let target_color = color_map.get(level);
|
||||||
if target_color.is_some() {
|
if let Some(color) = target_color {
|
||||||
output_color = target_color.unwrap().to_vec();
|
output_color = color.to_vec();
|
||||||
}
|
}
|
||||||
return output_color;
|
output_color
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_time(time: &DateTime<Utc>) -> String {
|
fn format_time(time: &DateTime<Utc>) -> String {
|
||||||
@@ -319,11 +319,11 @@ where
|
|||||||
Tz::Offset: std::fmt::Display,
|
Tz::Offset: std::fmt::Display,
|
||||||
{
|
{
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("rfc-2822") {
|
if configs::CONFIG.read().unwrap().args.is_present("rfc-2822") {
|
||||||
return time.to_rfc2822();
|
time.to_rfc2822()
|
||||||
} else if configs::CONFIG.read().unwrap().args.is_present("rfc-3339") {
|
} else if configs::CONFIG.read().unwrap().args.is_present("rfc-3339") {
|
||||||
return time.to_rfc3339();
|
time.to_rfc3339()
|
||||||
} else {
|
} else {
|
||||||
return time.format("%Y-%m-%d %H:%M:%S%.3f %:z").to_string();
|
time.format("%Y-%m-%d %H:%M:%S%.3f %:z").to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,6 +331,7 @@ where
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::afterfact::emit_csv;
|
use crate::afterfact::emit_csv;
|
||||||
use crate::detections::print;
|
use crate::detections::print;
|
||||||
|
use crate::detections::print::DetectInfo;
|
||||||
use chrono::{Local, TimeZone, Utc};
|
use chrono::{Local, TimeZone, Utc};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@@ -372,15 +373,18 @@ mod tests {
|
|||||||
"##;
|
"##;
|
||||||
let event: Value = serde_json::from_str(val).unwrap();
|
let event: Value = serde_json::from_str(val).unwrap();
|
||||||
messages.insert(
|
messages.insert(
|
||||||
testfilepath.to_string(),
|
|
||||||
testrulepath.to_string(),
|
|
||||||
&event,
|
&event,
|
||||||
test_level.to_string(),
|
|
||||||
test_computername.to_string(),
|
|
||||||
test_eventid.to_string(),
|
|
||||||
test_title.to_string(),
|
|
||||||
output.to_string(),
|
output.to_string(),
|
||||||
test_attack.to_string(),
|
DetectInfo {
|
||||||
|
filepath: testfilepath.to_string(),
|
||||||
|
rulepath: testrulepath.to_string(),
|
||||||
|
level: test_level.to_string(),
|
||||||
|
computername: test_computername.to_string(),
|
||||||
|
eventid: test_eventid.to_string(),
|
||||||
|
alert: test_title.to_string(),
|
||||||
|
detail: String::default(),
|
||||||
|
tag_info: test_attack.to_string(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let expect_time = Utc
|
let expect_time = Utc
|
||||||
@@ -452,15 +456,18 @@ mod tests {
|
|||||||
"##;
|
"##;
|
||||||
let event: Value = serde_json::from_str(val).unwrap();
|
let event: Value = serde_json::from_str(val).unwrap();
|
||||||
messages.insert(
|
messages.insert(
|
||||||
testfilepath.to_string(),
|
|
||||||
testrulepath.to_string(),
|
|
||||||
&event,
|
&event,
|
||||||
test_level.to_string(),
|
|
||||||
test_computername.to_string(),
|
|
||||||
test_eventid.to_string(),
|
|
||||||
test_title.to_string(),
|
|
||||||
output.to_string(),
|
output.to_string(),
|
||||||
test_attack.to_string(),
|
DetectInfo {
|
||||||
|
filepath: testfilepath.to_string(),
|
||||||
|
rulepath: testrulepath.to_string(),
|
||||||
|
level: test_level.to_string(),
|
||||||
|
computername: test_computername.to_string(),
|
||||||
|
eventid: test_eventid.to_string(),
|
||||||
|
alert: test_title.to_string(),
|
||||||
|
detail: String::default(),
|
||||||
|
tag_info: test_attack.to_string(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
messages.debug();
|
messages.debug();
|
||||||
}
|
}
|
||||||
@@ -520,6 +527,6 @@ mod tests {
|
|||||||
let white_color_header = "\u{1b}[38;2;255;255;255m";
|
let white_color_header = "\u{1b}[38;2;255;255;255m";
|
||||||
let white_color_footer = "\u{1b}[0m";
|
let white_color_footer = "\u{1b}[0m";
|
||||||
|
|
||||||
return white_color_header.to_owned() + target + white_color_footer;
|
white_color_header.to_owned() + target + white_color_footer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ lazy_static! {
|
|||||||
levelmap.insert("MEDIUM".to_owned(), 3);
|
levelmap.insert("MEDIUM".to_owned(), 3);
|
||||||
levelmap.insert("HIGH".to_owned(), 4);
|
levelmap.insert("HIGH".to_owned(), 4);
|
||||||
levelmap.insert("CRITICAL".to_owned(), 5);
|
levelmap.insert("CRITICAL".to_owned(), 5);
|
||||||
return levelmap;
|
levelmap
|
||||||
};
|
};
|
||||||
pub static ref EVENTKEY_ALIAS: EventKeyAliasConfig =
|
pub static ref EVENTKEY_ALIAS: EventKeyAliasConfig =
|
||||||
load_eventkey_alias("./rules/config/eventkey_alias.txt");
|
load_eventkey_alias("./rules/config/eventkey_alias.txt");
|
||||||
@@ -29,6 +29,12 @@ pub struct ConfigReader {
|
|||||||
pub target_eventids: TargetEventIds,
|
pub target_eventids: TargetEventIds,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for ConfigReader {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ConfigReader {
|
impl ConfigReader {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
ConfigReader {
|
ConfigReader {
|
||||||
@@ -41,7 +47,7 @@ impl ConfigReader {
|
|||||||
|
|
||||||
fn build_app<'a>() -> ArgMatches<'a> {
|
fn build_app<'a>() -> ArgMatches<'a> {
|
||||||
let program = std::env::args()
|
let program = std::env::args()
|
||||||
.nth(0)
|
.next()
|
||||||
.and_then(|s| {
|
.and_then(|s| {
|
||||||
std::path::PathBuf::from(s)
|
std::path::PathBuf::from(s)
|
||||||
.file_stem()
|
.file_stem()
|
||||||
@@ -91,7 +97,7 @@ fn is_test_mode() -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -99,19 +105,25 @@ pub struct TargetEventIds {
|
|||||||
ids: HashSet<String>,
|
ids: HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for TargetEventIds {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TargetEventIds {
|
impl TargetEventIds {
|
||||||
pub fn new() -> TargetEventIds {
|
pub fn new() -> TargetEventIds {
|
||||||
return TargetEventIds {
|
TargetEventIds {
|
||||||
ids: HashSet::new(),
|
ids: HashSet::new(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_target(&self, id: &String) -> bool {
|
pub fn is_target(&self, id: &str) -> bool {
|
||||||
// 中身が空の場合は全EventIdを対象とする。
|
// 中身が空の場合は全EventIdを対象とする。
|
||||||
if self.ids.is_empty() {
|
if self.ids.is_empty() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return self.ids.contains(id);
|
self.ids.contains(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +133,7 @@ fn load_target_ids(path: &str) -> TargetEventIds {
|
|||||||
if lines.is_err() {
|
if lines.is_err() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
&mut BufWriter::new(std::io::stderr().lock()),
|
||||||
&lines.as_ref().unwrap_err(),
|
lines.as_ref().unwrap_err(),
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
return ret;
|
return ret;
|
||||||
@@ -134,7 +146,7 @@ fn load_target_ids(path: &str) -> TargetEventIds {
|
|||||||
ret.ids.insert(line);
|
ret.ids.insert(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -143,6 +155,12 @@ pub struct TargetEventTime {
|
|||||||
end_time: Option<DateTime<Utc>>,
|
end_time: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for TargetEventTime {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TargetEventTime {
|
impl TargetEventTime {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let start_time =
|
let start_time =
|
||||||
@@ -180,17 +198,17 @@ impl TargetEventTime {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
return Self::set(start_time, end_time);
|
Self::set(start_time, end_time)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(
|
pub fn set(
|
||||||
start_time: Option<chrono::DateTime<chrono::Utc>>,
|
input_start_time: Option<chrono::DateTime<chrono::Utc>>,
|
||||||
end_time: Option<chrono::DateTime<chrono::Utc>>,
|
input_end_time: Option<chrono::DateTime<chrono::Utc>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
return Self {
|
Self {
|
||||||
start_time: start_time,
|
start_time: input_start_time,
|
||||||
end_time: end_time,
|
end_time: input_end_time,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_target(&self, eventtime: &Option<DateTime<Utc>>) -> bool {
|
pub fn is_target(&self, eventtime: &Option<DateTime<Utc>>) -> bool {
|
||||||
@@ -207,7 +225,7 @@ impl TargetEventTime {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,18 +237,24 @@ pub struct EventKeyAliasConfig {
|
|||||||
|
|
||||||
impl EventKeyAliasConfig {
|
impl EventKeyAliasConfig {
|
||||||
pub fn new() -> EventKeyAliasConfig {
|
pub fn new() -> EventKeyAliasConfig {
|
||||||
return EventKeyAliasConfig {
|
EventKeyAliasConfig {
|
||||||
key_to_eventkey: HashMap::new(),
|
key_to_eventkey: HashMap::new(),
|
||||||
key_to_split_eventkey: HashMap::new(),
|
key_to_split_eventkey: HashMap::new(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_event_key(&self, alias: &String) -> Option<&String> {
|
pub fn get_event_key(&self, alias: &str) -> Option<&String> {
|
||||||
return self.key_to_eventkey.get(alias);
|
self.key_to_eventkey.get(alias)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_event_key_split(&self, alias: &String) -> Option<&Vec<usize>> {
|
pub fn get_event_key_split(&self, alias: &str) -> Option<&Vec<usize>> {
|
||||||
return self.key_to_split_eventkey.get(alias);
|
self.key_to_split_eventkey.get(alias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EventKeyAliasConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +265,7 @@ fn load_eventkey_alias(path: &str) -> EventKeyAliasConfig {
|
|||||||
if read_result.is_err() {
|
if read_result.is_err() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
&mut BufWriter::new(std::io::stderr().lock()),
|
||||||
&read_result.as_ref().unwrap_err(),
|
read_result.as_ref().unwrap_err(),
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
return config;
|
return config;
|
||||||
@@ -255,20 +279,20 @@ fn load_eventkey_alias(path: &str) -> EventKeyAliasConfig {
|
|||||||
let empty = &"".to_string();
|
let empty = &"".to_string();
|
||||||
let alias = line.get(0).unwrap_or(empty);
|
let alias = line.get(0).unwrap_or(empty);
|
||||||
let event_key = line.get(1).unwrap_or(empty);
|
let event_key = line.get(1).unwrap_or(empty);
|
||||||
if alias.len() == 0 || event_key.len() == 0 {
|
if alias.is_empty() || event_key.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
config
|
config
|
||||||
.key_to_eventkey
|
.key_to_eventkey
|
||||||
.insert(alias.to_owned(), event_key.to_owned());
|
.insert(alias.to_owned(), event_key.to_owned());
|
||||||
let splits = event_key.split(".").map(|s| s.len()).collect();
|
let splits = event_key.split('.').map(|s| s.len()).collect();
|
||||||
config
|
config
|
||||||
.key_to_split_eventkey
|
.key_to_split_eventkey
|
||||||
.insert(alias.to_owned(), splits);
|
.insert(alias.to_owned(), splits);
|
||||||
});
|
});
|
||||||
config.key_to_eventkey.shrink_to_fit();
|
config.key_to_eventkey.shrink_to_fit();
|
||||||
return config;
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -278,16 +302,22 @@ pub struct EventInfo {
|
|||||||
pub comment: String,
|
pub comment: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for EventInfo {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl EventInfo {
|
impl EventInfo {
|
||||||
pub fn new() -> EventInfo {
|
pub fn new() -> EventInfo {
|
||||||
let evttitle = "Unknown".to_string();
|
let evttitle = "Unknown".to_string();
|
||||||
let detectflg = "".to_string();
|
let detectflg = "".to_string();
|
||||||
let comment = "".to_string();
|
let comment = "".to_string();
|
||||||
return EventInfo {
|
EventInfo {
|
||||||
evttitle,
|
evttitle,
|
||||||
detectflg,
|
detectflg,
|
||||||
comment,
|
comment,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -295,14 +325,20 @@ pub struct EventInfoConfig {
|
|||||||
eventinfo: HashMap<String, EventInfo>,
|
eventinfo: HashMap<String, EventInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for EventInfoConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl EventInfoConfig {
|
impl EventInfoConfig {
|
||||||
pub fn new() -> EventInfoConfig {
|
pub fn new() -> EventInfoConfig {
|
||||||
return EventInfoConfig {
|
EventInfoConfig {
|
||||||
eventinfo: HashMap::new(),
|
eventinfo: HashMap::new(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
pub fn get_event_id(&self, eventid: &String) -> Option<&EventInfo> {
|
pub fn get_event_id(&self, eventid: &str) -> Option<&EventInfo> {
|
||||||
return self.eventinfo.get(eventid);
|
self.eventinfo.get(eventid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +349,7 @@ fn load_eventcode_info(path: &str) -> EventInfoConfig {
|
|||||||
if read_result.is_err() {
|
if read_result.is_err() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
&mut BufWriter::new(std::io::stderr().lock()),
|
||||||
&read_result.as_ref().unwrap_err(),
|
read_result.as_ref().unwrap_err(),
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
return config;
|
return config;
|
||||||
@@ -339,7 +375,7 @@ fn load_eventcode_info(path: &str) -> EventInfoConfig {
|
|||||||
.eventinfo
|
.eventinfo
|
||||||
.insert(eventcode.to_owned(), infodata.to_owned());
|
.insert(eventcode.to_owned(), infodata.to_owned());
|
||||||
});
|
});
|
||||||
return config;
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -375,9 +411,9 @@ mod tests {
|
|||||||
let within_range = Some("2019-02-27T01:05:01Z".parse::<DateTime<Utc>>().unwrap());
|
let within_range = Some("2019-02-27T01:05:01Z".parse::<DateTime<Utc>>().unwrap());
|
||||||
let out_of_range2 = Some("2021-02-27T01:05:01Z".parse::<DateTime<Utc>>().unwrap());
|
let out_of_range2 = Some("2021-02-27T01:05:01Z".parse::<DateTime<Utc>>().unwrap());
|
||||||
|
|
||||||
assert_eq!(time_filter.is_target(&out_of_range1), false);
|
assert!(!time_filter.is_target(&out_of_range1));
|
||||||
assert_eq!(time_filter.is_target(&within_range), true);
|
assert!(time_filter.is_target(&within_range));
|
||||||
assert_eq!(time_filter.is_target(&out_of_range2), false);
|
assert!(!time_filter.is_target(&out_of_range2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -386,7 +422,7 @@ mod tests {
|
|||||||
let end_time = Some("2020-03-30T12:00:09Z".parse::<DateTime<Utc>>().unwrap());
|
let end_time = Some("2020-03-30T12:00:09Z".parse::<DateTime<Utc>>().unwrap());
|
||||||
let time_filter = configs::TargetEventTime::set(start_time, end_time);
|
let time_filter = configs::TargetEventTime::set(start_time, end_time);
|
||||||
|
|
||||||
assert_eq!(time_filter.is_target(&start_time), true);
|
assert!(time_filter.is_target(&start_time));
|
||||||
assert_eq!(time_filter.is_target(&end_time), true);
|
assert!(time_filter.is_target(&end_time));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ extern crate csv;
|
|||||||
|
|
||||||
use crate::detections::configs;
|
use crate::detections::configs;
|
||||||
use crate::detections::print::AlertMessage;
|
use crate::detections::print::AlertMessage;
|
||||||
|
use crate::detections::print::DetectInfo;
|
||||||
use crate::detections::print::ERROR_LOG_STACK;
|
use crate::detections::print::ERROR_LOG_STACK;
|
||||||
use crate::detections::print::MESSAGES;
|
use crate::detections::print::MESSAGES;
|
||||||
use crate::detections::print::QUIET_ERRORS_FLAG;
|
use crate::detections::print::QUIET_ERRORS_FLAG;
|
||||||
@@ -31,8 +32,8 @@ pub struct EvtxRecordInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EvtxRecordInfo {
|
impl EvtxRecordInfo {
|
||||||
pub fn get_value(&self, key: &String) -> Option<&String> {
|
pub fn get_value(&self, key: &str) -> Option<&String> {
|
||||||
return self.key_2_value.get(key);
|
self.key_2_value.get(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,12 +43,12 @@ pub struct Detection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Detection {
|
impl Detection {
|
||||||
pub fn new(rules: Vec<RuleNode>) -> Detection {
|
pub fn new(rule_nodes: Vec<RuleNode>) -> Detection {
|
||||||
return Detection { rules: rules };
|
Detection { rules: rule_nodes }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(self, rt: &Runtime, records: Vec<EvtxRecordInfo>) -> Self {
|
pub fn start(self, rt: &Runtime, records: Vec<EvtxRecordInfo>) -> Self {
|
||||||
return rt.block_on(self.execute_rules(records));
|
rt.block_on(self.execute_rules(records))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ルールファイルをパースします。
|
// ルールファイルをパースします。
|
||||||
@@ -104,9 +105,9 @@ impl Detection {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
parseerror_count += 1;
|
parseerror_count += 1;
|
||||||
println!(""); // 一行開けるためのprintln
|
println!(); // 一行開けるためのprintln
|
||||||
});
|
});
|
||||||
return Option::None;
|
Option::None
|
||||||
};
|
};
|
||||||
// parse rule files
|
// parse rule files
|
||||||
let ret = rulefile_loader
|
let ret = rulefile_loader
|
||||||
@@ -120,7 +121,7 @@ impl Detection {
|
|||||||
&parseerror_count,
|
&parseerror_count,
|
||||||
&rulefile_loader.ignorerule_count,
|
&rulefile_loader.ignorerule_count,
|
||||||
);
|
);
|
||||||
return ret;
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// 複数のイベントレコードに対して、複数のルールを1個実行します。
|
// 複数のイベントレコードに対して、複数のルールを1個実行します。
|
||||||
@@ -132,10 +133,7 @@ impl Detection {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|rule| {
|
.map(|rule| {
|
||||||
let records_cloned = Arc::clone(&records_arc);
|
let records_cloned = Arc::clone(&records_arc);
|
||||||
return spawn(async move {
|
spawn(async move { Detection::execute_rule(rule, records_cloned) })
|
||||||
let moved_rule = Detection::execute_rule(rule, records_cloned);
|
|
||||||
return moved_rule;
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -151,7 +149,7 @@ impl Detection {
|
|||||||
// self.rulesが再度所有権を取り戻せるように、Detection::execute_ruleで引数に渡したruleを戻り値として返すようにしている。
|
// self.rulesが再度所有権を取り戻せるように、Detection::execute_ruleで引数に渡したruleを戻り値として返すようにしている。
|
||||||
self.rules = rules;
|
self.rules = rules;
|
||||||
|
|
||||||
return self;
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_aggcondition_msges(self, rt: &Runtime) {
|
pub fn add_aggcondition_msges(self, rt: &Runtime) {
|
||||||
@@ -175,17 +173,17 @@ impl Detection {
|
|||||||
fn execute_rule(mut rule: RuleNode, records: Arc<Vec<EvtxRecordInfo>>) -> RuleNode {
|
fn execute_rule(mut rule: RuleNode, records: Arc<Vec<EvtxRecordInfo>>) -> RuleNode {
|
||||||
let agg_condition = rule.has_agg_condition();
|
let agg_condition = rule.has_agg_condition();
|
||||||
for record_info in records.as_ref() {
|
for record_info in records.as_ref() {
|
||||||
let result = rule.select(&record_info);
|
let result = rule.select(record_info);
|
||||||
if !result {
|
if !result {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// aggregation conditionが存在しない場合はそのまま出力対応を行う
|
// aggregation conditionが存在しない場合はそのまま出力対応を行う
|
||||||
if !agg_condition {
|
if !agg_condition {
|
||||||
Detection::insert_message(&rule, &record_info);
|
Detection::insert_message(&rule, record_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rule;
|
rule
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 条件に合致したレコードを表示するための関数
|
/// 条件に合致したレコードを表示するための関数
|
||||||
@@ -193,23 +191,27 @@ impl Detection {
|
|||||||
let tag_info: Vec<String> = rule.yaml["tags"]
|
let tag_info: Vec<String> = rule.yaml["tags"]
|
||||||
.as_vec()
|
.as_vec()
|
||||||
.unwrap_or(&Vec::default())
|
.unwrap_or(&Vec::default())
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|info| info.as_str().unwrap_or("").replace("attack.", ""))
|
.map(|info| info.as_str().unwrap_or("").replace("attack.", ""))
|
||||||
.collect();
|
.collect();
|
||||||
MESSAGES.lock().unwrap().insert(
|
MESSAGES.lock().unwrap().insert(
|
||||||
record_info.evtx_filepath.to_string(),
|
|
||||||
rule.rulepath.to_string(),
|
|
||||||
&record_info.record,
|
&record_info.record,
|
||||||
rule.yaml["level"].as_str().unwrap_or("-").to_string(),
|
|
||||||
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(),
|
rule.yaml["details"].as_str().unwrap_or("").to_string(),
|
||||||
tag_info.join(" : "),
|
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("\"", ""),
|
||||||
|
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"]
|
let tag_info: Vec<String> = rule.yaml["tags"]
|
||||||
.as_vec()
|
.as_vec()
|
||||||
.unwrap_or(&Vec::default())
|
.unwrap_or(&Vec::default())
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|info| info.as_str().unwrap_or("").replace("attack.", ""))
|
.map(|info| info.as_str().unwrap_or("").replace("attack.", ""))
|
||||||
.collect();
|
.collect();
|
||||||
let output = Detection::create_count_output(rule, &agg_result);
|
let output = Detection::create_count_output(rule, &agg_result);
|
||||||
MESSAGES.lock().unwrap().insert_message(
|
MESSAGES.lock().unwrap().insert_message(
|
||||||
"-".to_owned(),
|
DetectInfo {
|
||||||
rule.rulepath.to_owned(),
|
filepath: "-".to_owned(),
|
||||||
|
rulepath: rule.rulepath.to_owned(),
|
||||||
|
level: rule.yaml["level"].as_str().unwrap_or("").to_owned(),
|
||||||
|
computername: "-".to_owned(),
|
||||||
|
eventid: "-".to_owned(),
|
||||||
|
alert: rule.yaml["title"].as_str().unwrap_or("").to_owned(),
|
||||||
|
detail: output,
|
||||||
|
tag_info: tag_info.join(" : "),
|
||||||
|
},
|
||||||
agg_result.start_timedate,
|
agg_result.start_timedate,
|
||||||
rule.yaml["level"].as_str().unwrap_or("").to_owned(),
|
|
||||||
"-".to_owned(),
|
|
||||||
"-".to_owned(),
|
|
||||||
rule.yaml["title"].as_str().unwrap_or("").to_owned(),
|
|
||||||
output.to_owned(),
|
|
||||||
tag_info.join(" : "),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,15 +246,11 @@ impl Detection {
|
|||||||
let agg_condition_raw_str: Vec<&str> = rule.yaml["detection"]["condition"]
|
let agg_condition_raw_str: Vec<&str> = rule.yaml["detection"]["condition"]
|
||||||
.as_str()
|
.as_str()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.split("|")
|
.split('|')
|
||||||
.collect();
|
.collect();
|
||||||
// この関数が呼び出されている段階で既にaggregation conditionは存在する前提なのでunwrap前の確認は行わない
|
// この関数が呼び出されている段階で既にaggregation conditionは存在する前提なのでunwrap前の確認は行わない
|
||||||
let agg_condition = rule.get_agg_condition().unwrap();
|
let agg_condition = rule.get_agg_condition().unwrap();
|
||||||
let exist_timeframe = rule.yaml["detection"]["timeframe"]
|
let exist_timeframe = rule.yaml["detection"]["timeframe"].as_str().unwrap_or("") != "";
|
||||||
.as_str()
|
|
||||||
.unwrap_or("")
|
|
||||||
.to_string()
|
|
||||||
!= "";
|
|
||||||
// この関数が呼び出されている段階で既にaggregation conditionは存在する前提なのでagg_conditionの配列の長さは2となる
|
// この関数が呼び出されている段階で既にaggregation conditionは存在する前提なのでagg_conditionの配列の長さは2となる
|
||||||
ret.push_str(agg_condition_raw_str[1].trim());
|
ret.push_str(agg_condition_raw_str[1].trim());
|
||||||
if exist_timeframe {
|
if exist_timeframe {
|
||||||
@@ -281,8 +281,9 @@ impl Detection {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_rule_load_info(
|
pub fn print_rule_load_info(
|
||||||
rc: &HashMap<String, u128>,
|
rc: &HashMap<String, u128>,
|
||||||
parseerror_count: &u128,
|
parseerror_count: &u128,
|
||||||
@@ -302,7 +303,7 @@ impl Detection {
|
|||||||
"Total enabled detection rules: {}",
|
"Total enabled detection rules: {}",
|
||||||
total - ignore_count - parseerror_count
|
total - ignore_count - parseerror_count
|
||||||
);
|
);
|
||||||
println!("");
|
println!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,12 @@ lazy_static! {
|
|||||||
.is_present("statistics");
|
.is_present("statistics");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Message {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let messages: BTreeMap<DateTime<Utc>, Vec<DetectInfo>> = BTreeMap::new();
|
let messages: BTreeMap<DateTime<Utc>, Vec<DetectInfo>> = BTreeMap::new();
|
||||||
@@ -64,67 +70,21 @@ impl Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// メッセージの設定を行う関数。aggcondition対応のためrecordではなく出力をする対象時間がDatetime形式での入力としている
|
/// メッセージの設定を行う関数。aggcondition対応のためrecordではなく出力をする対象時間がDatetime形式での入力としている
|
||||||
pub fn insert_message(
|
pub fn insert_message(&mut self, detect_info: DetectInfo, event_time: DateTime<Utc>) {
|
||||||
&mut self,
|
if let Some(v) = self.map.get_mut(&event_time) {
|
||||||
target_file: String,
|
v.push(detect_info);
|
||||||
rule_path: String,
|
} else {
|
||||||
event_time: DateTime<Utc>,
|
let m = vec![detect_info; 1];
|
||||||
level: String,
|
self.map.insert(event_time, m);
|
||||||
computername: String,
|
|
||||||
eventid: String,
|
|
||||||
event_title: String,
|
|
||||||
event_detail: String,
|
|
||||||
tag_info: String,
|
|
||||||
) {
|
|
||||||
let detect_info = DetectInfo {
|
|
||||||
filepath: target_file,
|
|
||||||
rulepath: rule_path,
|
|
||||||
level: level,
|
|
||||||
computername: computername,
|
|
||||||
eventid: eventid,
|
|
||||||
alert: event_title,
|
|
||||||
detail: event_detail,
|
|
||||||
tag_info: tag_info,
|
|
||||||
};
|
|
||||||
|
|
||||||
match self.map.get_mut(&event_time) {
|
|
||||||
Some(v) => {
|
|
||||||
v.push(detect_info);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let m = vec![detect_info; 1];
|
|
||||||
self.map.insert(event_time, m);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// メッセージを設定
|
/// メッセージを設定
|
||||||
pub fn insert(
|
pub fn insert(&mut self, event_record: &Value, output: String, mut detect_info: DetectInfo) {
|
||||||
&mut self,
|
detect_info.detail = self.parse_message(event_record, output);
|
||||||
target_file: String,
|
|
||||||
rule_path: String,
|
|
||||||
event_record: &Value,
|
|
||||||
level: String,
|
|
||||||
computername: String,
|
|
||||||
eventid: String,
|
|
||||||
event_title: String,
|
|
||||||
output: String,
|
|
||||||
tag_info: String,
|
|
||||||
) {
|
|
||||||
let message = &self.parse_message(event_record, output);
|
|
||||||
let default_time = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0);
|
let default_time = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0);
|
||||||
let time = Message::get_event_time(event_record).unwrap_or(default_time);
|
let time = Message::get_event_time(event_record).unwrap_or(default_time);
|
||||||
self.insert_message(
|
self.insert_message(detect_info, time)
|
||||||
target_file,
|
|
||||||
rule_path,
|
|
||||||
time,
|
|
||||||
level,
|
|
||||||
computername,
|
|
||||||
eventid,
|
|
||||||
event_title,
|
|
||||||
message.to_string(),
|
|
||||||
tag_info,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_message(&mut self, event_record: &Value, output: String) -> String {
|
fn parse_message(&mut self, event_record: &Value, output: String) -> String {
|
||||||
@@ -141,9 +101,9 @@ impl Message {
|
|||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|
||||||
if let Some(array_str) = configs::EVENTKEY_ALIAS.get_event_key(&target_str) {
|
if let Some(array_str) = configs::EVENTKEY_ALIAS.get_event_key(&target_str) {
|
||||||
let split: Vec<&str> = array_str.split(".").collect();
|
let split: Vec<&str> = array_str.split('.').collect();
|
||||||
let mut is_exist_event_key = false;
|
let mut is_exist_event_key = false;
|
||||||
let mut tmp_event_record: &Value = event_record.into();
|
let mut tmp_event_record: &Value = event_record;
|
||||||
for s in &split {
|
for s in &split {
|
||||||
if let Some(record) = tmp_event_record.get(s) {
|
if let Some(record) = tmp_event_record.get(s) {
|
||||||
is_exist_event_key = true;
|
is_exist_event_key = true;
|
||||||
@@ -193,7 +153,7 @@ impl Message {
|
|||||||
}
|
}
|
||||||
detect_count += detect_infos.len();
|
detect_count += detect_infos.len();
|
||||||
}
|
}
|
||||||
println!("");
|
println!();
|
||||||
println!("Total events:{:?}", detect_count);
|
println!("Total events:{:?}", detect_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,20 +184,14 @@ impl AlertMessage {
|
|||||||
}
|
}
|
||||||
let mut error_log_writer = BufWriter::new(File::create(path).unwrap());
|
let mut error_log_writer = BufWriter::new(File::create(path).unwrap());
|
||||||
error_log_writer
|
error_log_writer
|
||||||
.write(
|
.write_all(
|
||||||
format!(
|
format!(
|
||||||
"user input: {:?}\n",
|
"user input: {:?}\n",
|
||||||
format_args!(
|
format_args!("{}", env::args().collect::<Vec<String>>().join(" "))
|
||||||
"{}",
|
|
||||||
env::args()
|
|
||||||
.map(|arg| arg)
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(" ")
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.ok();
|
||||||
for error_log in ERROR_LOG_STACK.lock().unwrap().iter() {
|
for error_log in ERROR_LOG_STACK.lock().unwrap().iter() {
|
||||||
writeln!(error_log_writer, "{}", error_log).ok();
|
writeln!(error_log_writer, "{}", error_log).ok();
|
||||||
}
|
}
|
||||||
@@ -245,22 +199,23 @@ impl AlertMessage {
|
|||||||
"Errors were generated. Please check {} for details.",
|
"Errors were generated. Please check {} for details.",
|
||||||
ERROR_LOG_PATH.to_string()
|
ERROR_LOG_PATH.to_string()
|
||||||
);
|
);
|
||||||
println!("");
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ERRORメッセージを表示する関数
|
/// ERRORメッセージを表示する関数
|
||||||
pub fn alert<W: Write>(w: &mut W, contents: &String) -> io::Result<()> {
|
pub fn alert<W: Write>(w: &mut W, contents: &str) -> io::Result<()> {
|
||||||
writeln!(w, "[ERROR] {}", contents)
|
writeln!(w, "[ERROR] {}", contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// WARNメッセージを表示する関数
|
/// WARNメッセージを表示する関数
|
||||||
pub fn warn<W: Write>(w: &mut W, contents: &String) -> io::Result<()> {
|
pub fn warn<W: Write>(w: &mut W, contents: &str) -> io::Result<()> {
|
||||||
writeln!(w, "[WARN] {}", contents)
|
writeln!(w, "[WARN] {}", contents)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::detections::print::DetectInfo;
|
||||||
use crate::detections::print::{AlertMessage, Message};
|
use crate::detections::print::{AlertMessage, Message};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::io::BufWriter;
|
use std::io::BufWriter;
|
||||||
@@ -284,15 +239,18 @@ mod tests {
|
|||||||
"##;
|
"##;
|
||||||
let event_record_1: Value = serde_json::from_str(json_str_1).unwrap();
|
let event_record_1: Value = serde_json::from_str(json_str_1).unwrap();
|
||||||
message.insert(
|
message.insert(
|
||||||
"a".to_string(),
|
|
||||||
"test_rule".to_string(),
|
|
||||||
&event_record_1,
|
&event_record_1,
|
||||||
"high".to_string(),
|
|
||||||
"testcomputer1".to_string(),
|
|
||||||
"1".to_string(),
|
|
||||||
"test1".to_string(),
|
|
||||||
"CommandLine1: %CommandLine%".to_string(),
|
"CommandLine1: %CommandLine%".to_string(),
|
||||||
"txxx.001".to_string(),
|
DetectInfo {
|
||||||
|
filepath: "a".to_string(),
|
||||||
|
rulepath: "test_rule".to_string(),
|
||||||
|
level: "high".to_string(),
|
||||||
|
computername: "testcomputer1".to_string(),
|
||||||
|
eventid: "1".to_string(),
|
||||||
|
alert: "test1".to_string(),
|
||||||
|
detail: String::default(),
|
||||||
|
tag_info: "txxx.001".to_string(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let json_str_2 = r##"
|
let json_str_2 = r##"
|
||||||
@@ -311,15 +269,18 @@ mod tests {
|
|||||||
"##;
|
"##;
|
||||||
let event_record_2: Value = serde_json::from_str(json_str_2).unwrap();
|
let event_record_2: Value = serde_json::from_str(json_str_2).unwrap();
|
||||||
message.insert(
|
message.insert(
|
||||||
"a".to_string(),
|
|
||||||
"test_rule2".to_string(),
|
|
||||||
&event_record_2,
|
&event_record_2,
|
||||||
"high".to_string(),
|
|
||||||
"testcomputer2".to_string(),
|
|
||||||
"2".to_string(),
|
|
||||||
"test2".to_string(),
|
|
||||||
"CommandLine2: %CommandLine%".to_string(),
|
"CommandLine2: %CommandLine%".to_string(),
|
||||||
"txxx.002".to_string(),
|
DetectInfo {
|
||||||
|
filepath: "a".to_string(),
|
||||||
|
rulepath: "test_rule2".to_string(),
|
||||||
|
level: "high".to_string(),
|
||||||
|
computername: "testcomputer2".to_string(),
|
||||||
|
eventid: "2".to_string(),
|
||||||
|
alert: "test2".to_string(),
|
||||||
|
detail: String::default(),
|
||||||
|
tag_info: "txxx.002".to_string(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let json_str_3 = r##"
|
let json_str_3 = r##"
|
||||||
@@ -338,15 +299,18 @@ mod tests {
|
|||||||
"##;
|
"##;
|
||||||
let event_record_3: Value = serde_json::from_str(json_str_3).unwrap();
|
let event_record_3: Value = serde_json::from_str(json_str_3).unwrap();
|
||||||
message.insert(
|
message.insert(
|
||||||
"a".to_string(),
|
|
||||||
"test_rule3".to_string(),
|
|
||||||
&event_record_3,
|
&event_record_3,
|
||||||
"high".to_string(),
|
|
||||||
"testcomputer3".to_string(),
|
|
||||||
"3".to_string(),
|
|
||||||
"test3".to_string(),
|
|
||||||
"CommandLine3: %CommandLine%".to_string(),
|
"CommandLine3: %CommandLine%".to_string(),
|
||||||
"txxx.003".to_string(),
|
DetectInfo {
|
||||||
|
filepath: "a".to_string(),
|
||||||
|
rulepath: "test_rule3".to_string(),
|
||||||
|
level: "high".to_string(),
|
||||||
|
computername: "testcomputer3".to_string(),
|
||||||
|
eventid: "3".to_string(),
|
||||||
|
alert: "test3".to_string(),
|
||||||
|
detail: String::default(),
|
||||||
|
tag_info: "txxx.003".to_string(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let json_str_4 = r##"
|
let json_str_4 = r##"
|
||||||
@@ -360,15 +324,18 @@ mod tests {
|
|||||||
"##;
|
"##;
|
||||||
let event_record_4: Value = serde_json::from_str(json_str_4).unwrap();
|
let event_record_4: Value = serde_json::from_str(json_str_4).unwrap();
|
||||||
message.insert(
|
message.insert(
|
||||||
"a".to_string(),
|
|
||||||
"test_rule4".to_string(),
|
|
||||||
&event_record_4,
|
&event_record_4,
|
||||||
"medium".to_string(),
|
|
||||||
"testcomputer4".to_string(),
|
|
||||||
"4".to_string(),
|
|
||||||
"test4".to_string(),
|
|
||||||
"CommandLine4: %CommandLine%".to_string(),
|
"CommandLine4: %CommandLine%".to_string(),
|
||||||
"txxx.004".to_string(),
|
DetectInfo {
|
||||||
|
filepath: "a".to_string(),
|
||||||
|
rulepath: "test_rule4".to_string(),
|
||||||
|
level: "medium".to_string(),
|
||||||
|
computername: "testcomputer4".to_string(),
|
||||||
|
eventid: "4".to_string(),
|
||||||
|
alert: "test4".to_string(),
|
||||||
|
detail: String::default(),
|
||||||
|
tag_info: "txxx.004".to_string(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let display = format!("{}", format_args!("{:?}", message));
|
let display = format!("{}", format_args!("{:?}", message));
|
||||||
|
|||||||
@@ -28,15 +28,15 @@ pub struct AggregationParseInfo {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AggregationConditionToken {
|
pub enum AggregationConditionToken {
|
||||||
COUNT(String), // count
|
Count(String), // count
|
||||||
SPACE, // 空白
|
Space, // 空白
|
||||||
BY, // by
|
BY, // by
|
||||||
EQ, // ..と等しい
|
EQ, // ..と等しい
|
||||||
LE, // ..以下
|
LE, // ..以下
|
||||||
LT, // ..未満
|
LT, // ..未満
|
||||||
GE, // ..以上
|
GE, // ..以上
|
||||||
GT, // .よりおおきい
|
GT, // .よりおおきい
|
||||||
KEYWORD(String), // BYのフィールド名
|
Keyword(String), // BYのフィールド名
|
||||||
}
|
}
|
||||||
|
|
||||||
/// SIGMAルールでいうAggregationConditionを解析する。
|
/// SIGMAルールでいうAggregationConditionを解析する。
|
||||||
@@ -52,12 +52,12 @@ impl AggegationConditionCompiler {
|
|||||||
pub fn compile(&self, condition_str: String) -> Result<Option<AggregationParseInfo>, String> {
|
pub fn compile(&self, condition_str: String) -> Result<Option<AggregationParseInfo>, String> {
|
||||||
let result = self.compile_body(condition_str);
|
let result = self.compile_body(condition_str);
|
||||||
if let Result::Err(msg) = result {
|
if let Result::Err(msg) = result {
|
||||||
return Result::Err(format!(
|
Result::Err(format!(
|
||||||
"An aggregation condition parse error has occurred. {}",
|
"An aggregation condition parse error has occurred. {}",
|
||||||
msg
|
msg
|
||||||
));
|
))
|
||||||
} else {
|
} else {
|
||||||
return result;
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ impl AggegationConditionCompiler {
|
|||||||
|
|
||||||
let tokens = self.tokenize(aggregation_str)?;
|
let tokens = self.tokenize(aggregation_str)?;
|
||||||
|
|
||||||
return self.parse(tokens);
|
self.parse(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 字句解析します。
|
/// 字句解析します。
|
||||||
@@ -90,10 +90,10 @@ impl AggegationConditionCompiler {
|
|||||||
&self,
|
&self,
|
||||||
condition_str: String,
|
condition_str: String,
|
||||||
) -> Result<Vec<AggregationConditionToken>, String> {
|
) -> Result<Vec<AggregationConditionToken>, String> {
|
||||||
let mut cur_condition_str = condition_str.clone();
|
let mut cur_condition_str = condition_str;
|
||||||
|
|
||||||
let mut tokens = Vec::new();
|
let mut tokens = Vec::new();
|
||||||
while cur_condition_str.len() != 0 {
|
while !cur_condition_str.is_empty() {
|
||||||
let captured = self::AGGREGATION_REGEXMAP.iter().find_map(|regex| {
|
let captured = self::AGGREGATION_REGEXMAP.iter().find_map(|regex| {
|
||||||
return regex.captures(cur_condition_str.as_str());
|
return regex.captures(cur_condition_str.as_str());
|
||||||
});
|
});
|
||||||
@@ -105,7 +105,7 @@ impl AggegationConditionCompiler {
|
|||||||
let mached_str = captured.unwrap().get(0).unwrap().as_str();
|
let mached_str = captured.unwrap().get(0).unwrap().as_str();
|
||||||
let token = self.to_enum(mached_str.to_string());
|
let token = self.to_enum(mached_str.to_string());
|
||||||
|
|
||||||
if let AggregationConditionToken::SPACE = token {
|
if let AggregationConditionToken::Space = token {
|
||||||
// 空白は特に意味ないので、読み飛ばす。
|
// 空白は特に意味ないので、読み飛ばす。
|
||||||
cur_condition_str = cur_condition_str.replacen(mached_str, "", 1);
|
cur_condition_str = cur_condition_str.replacen(mached_str, "", 1);
|
||||||
continue;
|
continue;
|
||||||
@@ -115,19 +115,19 @@ impl AggegationConditionCompiler {
|
|||||||
cur_condition_str = cur_condition_str.replacen(mached_str, "", 1);
|
cur_condition_str = cur_condition_str.replacen(mached_str, "", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result::Ok(tokens);
|
Result::Ok(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 比較演算子かどうか判定します。
|
/// 比較演算子かどうか判定します。
|
||||||
fn is_cmp_op(&self, token: &AggregationConditionToken) -> bool {
|
fn is_cmp_op(&self, token: &AggregationConditionToken) -> bool {
|
||||||
return match token {
|
matches!(
|
||||||
AggregationConditionToken::EQ => true,
|
token,
|
||||||
AggregationConditionToken::LE => true,
|
AggregationConditionToken::EQ
|
||||||
AggregationConditionToken::LT => true,
|
| AggregationConditionToken::LE
|
||||||
AggregationConditionToken::GE => true,
|
| AggregationConditionToken::LT
|
||||||
AggregationConditionToken::GT => true,
|
| AggregationConditionToken::GE
|
||||||
_ => false,
|
| AggregationConditionToken::GT
|
||||||
};
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 構文解析します。
|
/// 構文解析します。
|
||||||
@@ -144,7 +144,7 @@ impl AggegationConditionCompiler {
|
|||||||
let token = token_ite.next().unwrap();
|
let token = token_ite.next().unwrap();
|
||||||
|
|
||||||
let mut count_field_name: Option<String> = Option::None;
|
let mut count_field_name: Option<String> = Option::None;
|
||||||
if let AggregationConditionToken::COUNT(field_name) = token {
|
if let AggregationConditionToken::Count(field_name) = token {
|
||||||
if !field_name.is_empty() {
|
if !field_name.is_empty() {
|
||||||
count_field_name = Option::Some(field_name);
|
count_field_name = Option::Some(field_name);
|
||||||
}
|
}
|
||||||
@@ -173,7 +173,7 @@ impl AggegationConditionCompiler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let AggregationConditionToken::KEYWORD(keyword) = after_by.unwrap() {
|
if let AggregationConditionToken::Keyword(keyword) = after_by.unwrap() {
|
||||||
by_field_name = Option::Some(keyword);
|
by_field_name = Option::Some(keyword);
|
||||||
token_ite.next()
|
token_ite.next()
|
||||||
} else {
|
} else {
|
||||||
@@ -200,14 +200,14 @@ impl AggegationConditionCompiler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let token = token_ite.next().unwrap_or(AggregationConditionToken::SPACE);
|
let token = token_ite.next().unwrap_or(AggregationConditionToken::Space);
|
||||||
let cmp_number = if let AggregationConditionToken::KEYWORD(number) = token {
|
let cmp_number = if let AggregationConditionToken::Keyword(number) = token {
|
||||||
let number: Result<i64, _> = number.parse();
|
let number: Result<i64, _> = number.parse();
|
||||||
if number.is_err() {
|
if let Ok(num) = number {
|
||||||
|
num
|
||||||
|
} else {
|
||||||
// 比較演算子の後に数値が無い。
|
// 比較演算子の後に数値が無い。
|
||||||
return Result::Err("The compare operator needs a number like '> 3'.".to_string());
|
return Result::Err("The compare operator needs a number like '> 3'.".to_string());
|
||||||
} else {
|
|
||||||
number.unwrap()
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 比較演算子の後に数値が無い。
|
// 比較演算子の後に数値が無い。
|
||||||
@@ -224,7 +224,7 @@ impl AggegationConditionCompiler {
|
|||||||
_cmp_op: cmp_token,
|
_cmp_op: cmp_token,
|
||||||
_cmp_num: cmp_number,
|
_cmp_num: cmp_number,
|
||||||
};
|
};
|
||||||
return Result::Ok(Option::Some(info));
|
Result::Ok(Option::Some(info))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 文字列をConditionTokenに変換する。
|
/// 文字列をConditionTokenに変換する。
|
||||||
@@ -234,23 +234,23 @@ impl AggegationConditionCompiler {
|
|||||||
.replacen("count(", "", 1)
|
.replacen("count(", "", 1)
|
||||||
.replacen(")", "", 1)
|
.replacen(")", "", 1)
|
||||||
.replace(" ", "");
|
.replace(" ", "");
|
||||||
return AggregationConditionToken::COUNT(count_field);
|
AggregationConditionToken::Count(count_field)
|
||||||
} else if token == " " {
|
} else if token == " " {
|
||||||
return AggregationConditionToken::SPACE;
|
AggregationConditionToken::Space
|
||||||
} else if token == "by" {
|
} else if token == "by" {
|
||||||
return AggregationConditionToken::BY;
|
AggregationConditionToken::BY
|
||||||
} else if token == "==" {
|
} else if token == "==" {
|
||||||
return AggregationConditionToken::EQ;
|
AggregationConditionToken::EQ
|
||||||
} else if token == "<=" {
|
} else if token == "<=" {
|
||||||
return AggregationConditionToken::LE;
|
AggregationConditionToken::LE
|
||||||
} else if token == ">=" {
|
} else if token == ">=" {
|
||||||
return AggregationConditionToken::GE;
|
AggregationConditionToken::GE
|
||||||
} else if token == "<" {
|
} else if token == "<" {
|
||||||
return AggregationConditionToken::LT;
|
AggregationConditionToken::LT
|
||||||
} else if token == ">" {
|
} else if token == ">" {
|
||||||
return AggregationConditionToken::GT;
|
AggregationConditionToken::GT
|
||||||
} else {
|
} else {
|
||||||
return AggregationConditionToken::KEYWORD(token);
|
AggregationConditionToken::Keyword(token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,9 +266,9 @@ mod tests {
|
|||||||
// countが無いパターン
|
// countが無いパターン
|
||||||
let compiler = AggegationConditionCompiler::new();
|
let compiler = AggegationConditionCompiler::new();
|
||||||
let result = compiler.compile("select1 and select2".to_string());
|
let result = compiler.compile("select1 and select2".to_string());
|
||||||
assert_eq!(true, result.is_ok());
|
assert!(result.is_ok());
|
||||||
let result = result.unwrap();
|
let result = result.unwrap();
|
||||||
assert_eq!(true, result.is_none());
|
assert!(result.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -276,43 +276,23 @@ mod tests {
|
|||||||
// 正常系 countの中身にフィールドが無い 各種演算子を試す
|
// 正常系 countの中身にフィールドが無い 各種演算子を試す
|
||||||
let token =
|
let token =
|
||||||
check_aggregation_condition_ope("select1 and select2|count() > 32".to_string(), 32);
|
check_aggregation_condition_ope("select1 and select2|count() > 32".to_string(), 32);
|
||||||
let is_gt = match token {
|
assert!(matches!(token, AggregationConditionToken::GT));
|
||||||
AggregationConditionToken::GT => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
assert_eq!(is_gt, true);
|
|
||||||
|
|
||||||
let token =
|
let token =
|
||||||
check_aggregation_condition_ope("select1 and select2|count() >= 43".to_string(), 43);
|
check_aggregation_condition_ope("select1 and select2|count() >= 43".to_string(), 43);
|
||||||
let is_gt = match token {
|
assert!(matches!(token, AggregationConditionToken::GE));
|
||||||
AggregationConditionToken::GE => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
assert_eq!(is_gt, true);
|
|
||||||
|
|
||||||
let token =
|
let token =
|
||||||
check_aggregation_condition_ope("select1 and select2|count() < 59".to_string(), 59);
|
check_aggregation_condition_ope("select1 and select2|count() < 59".to_string(), 59);
|
||||||
let is_gt = match token {
|
assert!(matches!(token, AggregationConditionToken::LT));
|
||||||
AggregationConditionToken::LT => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
assert_eq!(is_gt, true);
|
|
||||||
|
|
||||||
let token =
|
let token =
|
||||||
check_aggregation_condition_ope("select1 and select2|count() <= 12".to_string(), 12);
|
check_aggregation_condition_ope("select1 and select2|count() <= 12".to_string(), 12);
|
||||||
let is_gt = match token {
|
assert!(matches!(token, AggregationConditionToken::LE));
|
||||||
AggregationConditionToken::LE => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
assert_eq!(is_gt, true);
|
|
||||||
|
|
||||||
let token =
|
let token =
|
||||||
check_aggregation_condition_ope("select1 and select2|count() == 28".to_string(), 28);
|
check_aggregation_condition_ope("select1 and select2|count() == 28".to_string(), 28);
|
||||||
let is_gt = match token {
|
assert!(matches!(token, AggregationConditionToken::EQ));
|
||||||
AggregationConditionToken::EQ => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
assert_eq!(is_gt, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -320,19 +300,15 @@ mod tests {
|
|||||||
let compiler = AggegationConditionCompiler::new();
|
let compiler = AggegationConditionCompiler::new();
|
||||||
let result = compiler.compile("select1 or select2 | count() by iiibbb > 27".to_string());
|
let result = compiler.compile("select1 or select2 | count() by iiibbb > 27".to_string());
|
||||||
|
|
||||||
assert_eq!(true, result.is_ok());
|
assert!(result.is_ok());
|
||||||
let result = result.unwrap();
|
let result = result.unwrap();
|
||||||
assert_eq!(true, result.is_some());
|
assert!(result.is_some());
|
||||||
|
|
||||||
let result = result.unwrap();
|
let result = result.unwrap();
|
||||||
assert_eq!("iiibbb".to_string(), result._by_field_name.unwrap());
|
assert_eq!("iiibbb".to_string(), result._by_field_name.unwrap());
|
||||||
assert_eq!(true, result._field_name.is_none());
|
assert!(result._field_name.is_none());
|
||||||
assert_eq!(27, result._cmp_num);
|
assert_eq!(27, result._cmp_num);
|
||||||
let is_ok = match result._cmp_op {
|
assert!(matches!(result._cmp_op, AggregationConditionToken::GT));
|
||||||
AggregationConditionToken::GT => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
assert_eq!(true, is_ok);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -340,19 +316,15 @@ mod tests {
|
|||||||
let compiler = AggegationConditionCompiler::new();
|
let compiler = AggegationConditionCompiler::new();
|
||||||
let result = compiler.compile("select1 or select2 | count( hogehoge ) > 3".to_string());
|
let result = compiler.compile("select1 or select2 | count( hogehoge ) > 3".to_string());
|
||||||
|
|
||||||
assert_eq!(true, result.is_ok());
|
assert!(result.is_ok());
|
||||||
let result = result.unwrap();
|
let result = result.unwrap();
|
||||||
assert_eq!(true, result.is_some());
|
assert!(result.is_some());
|
||||||
|
|
||||||
let result = result.unwrap();
|
let result = result.unwrap();
|
||||||
assert_eq!(true, result._by_field_name.is_none());
|
assert!(result._by_field_name.is_none());
|
||||||
assert_eq!("hogehoge", result._field_name.unwrap());
|
assert_eq!("hogehoge", result._field_name.unwrap());
|
||||||
assert_eq!(3, result._cmp_num);
|
assert_eq!(3, result._cmp_num);
|
||||||
let is_ok = match result._cmp_op {
|
assert!(matches!(result._cmp_op, AggregationConditionToken::GT));
|
||||||
AggregationConditionToken::GT => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
assert_eq!(true, is_ok);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -361,19 +333,15 @@ mod tests {
|
|||||||
let result =
|
let result =
|
||||||
compiler.compile("select1 or select2 | count( hogehoge) by snsn > 3".to_string());
|
compiler.compile("select1 or select2 | count( hogehoge) by snsn > 3".to_string());
|
||||||
|
|
||||||
assert_eq!(true, result.is_ok());
|
assert!(result.is_ok());
|
||||||
let result = result.unwrap();
|
let result = result.unwrap();
|
||||||
assert_eq!(true, result.is_some());
|
assert!(result.is_some());
|
||||||
|
|
||||||
let result = result.unwrap();
|
let result = result.unwrap();
|
||||||
assert_eq!("snsn".to_string(), result._by_field_name.unwrap());
|
assert_eq!("snsn".to_string(), result._by_field_name.unwrap());
|
||||||
assert_eq!("hogehoge", result._field_name.unwrap());
|
assert_eq!("hogehoge", result._field_name.unwrap());
|
||||||
assert_eq!(3, result._cmp_num);
|
assert_eq!(3, result._cmp_num);
|
||||||
let is_ok = match result._cmp_op {
|
assert!(matches!(result._cmp_op, AggregationConditionToken::GT));
|
||||||
AggregationConditionToken::GT => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
assert_eq!(true, is_ok);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -381,7 +349,7 @@ mod tests {
|
|||||||
let compiler = AggegationConditionCompiler::new();
|
let compiler = AggegationConditionCompiler::new();
|
||||||
let result = compiler.compile("select1 or select2 |".to_string());
|
let result = compiler.compile("select1 or select2 |".to_string());
|
||||||
|
|
||||||
assert_eq!(true, result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"An aggregation condition parse error has occurred. There are no strings after the pipe(|)."
|
"An aggregation condition parse error has occurred. There are no strings after the pipe(|)."
|
||||||
.to_string(),
|
.to_string(),
|
||||||
@@ -395,7 +363,7 @@ mod tests {
|
|||||||
let result =
|
let result =
|
||||||
compiler.compile("select1 or select2 | count( hogeess ) by ii-i > 33".to_string());
|
compiler.compile("select1 or select2 | count( hogeess ) by ii-i > 33".to_string());
|
||||||
|
|
||||||
assert_eq!(true, result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"An aggregation condition parse error has occurred. An unusable character was found."
|
"An aggregation condition parse error has occurred. An unusable character was found."
|
||||||
.to_string(),
|
.to_string(),
|
||||||
@@ -410,7 +378,7 @@ mod tests {
|
|||||||
let result =
|
let result =
|
||||||
compiler.compile("select1 or select2 | by count( hogehoge) by snsn > 3".to_string());
|
compiler.compile("select1 or select2 | by count( hogehoge) by snsn > 3".to_string());
|
||||||
|
|
||||||
assert_eq!(true, result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!("An aggregation condition parse error has occurred. The aggregation condition can only use count.".to_string(),result.unwrap_err());
|
assert_eq!("An aggregation condition parse error has occurred. The aggregation condition can only use count.".to_string(),result.unwrap_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,7 +388,7 @@ mod tests {
|
|||||||
let compiler = AggegationConditionCompiler::new();
|
let compiler = AggegationConditionCompiler::new();
|
||||||
let result = compiler.compile("select1 or select2 | count( hogehoge) 3".to_string());
|
let result = compiler.compile("select1 or select2 | count( hogehoge) 3".to_string());
|
||||||
|
|
||||||
assert_eq!(true, result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!("An aggregation condition parse error has occurred. The count keyword needs a compare operator and number like '> 3'".to_string(),result.unwrap_err());
|
assert_eq!("An aggregation condition parse error has occurred. The count keyword needs a compare operator and number like '> 3'".to_string(),result.unwrap_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +398,7 @@ mod tests {
|
|||||||
let compiler = AggegationConditionCompiler::new();
|
let compiler = AggegationConditionCompiler::new();
|
||||||
let result = compiler.compile("select1 or select2 | count( hogehoge) by".to_string());
|
let result = compiler.compile("select1 or select2 | count( hogehoge) by".to_string());
|
||||||
|
|
||||||
assert_eq!(true, result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!("An aggregation condition parse error has occurred. The by keyword needs a field name like 'by EventID'".to_string(),result.unwrap_err());
|
assert_eq!("An aggregation condition parse error has occurred. The by keyword needs a field name like 'by EventID'".to_string(),result.unwrap_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,7 +409,7 @@ mod tests {
|
|||||||
let result =
|
let result =
|
||||||
compiler.compile("select1 or select2 | count( hogehoge ) by hoe >".to_string());
|
compiler.compile("select1 or select2 | count( hogehoge ) by hoe >".to_string());
|
||||||
|
|
||||||
assert_eq!(true, result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!("An aggregation condition parse error has occurred. The compare operator needs a number like '> 3'.".to_string(),result.unwrap_err());
|
assert_eq!("An aggregation condition parse error has occurred. The compare operator needs a number like '> 3'.".to_string(),result.unwrap_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,7 +420,7 @@ mod tests {
|
|||||||
let result =
|
let result =
|
||||||
compiler.compile("select1 or select2 | count( hogehoge ) by hoe > 3 33".to_string());
|
compiler.compile("select1 or select2 | count( hogehoge ) by hoe > 3 33".to_string());
|
||||||
|
|
||||||
assert_eq!(true, result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"An aggregation condition parse error has occurred. An unnecessary word was found."
|
"An aggregation condition parse error has occurred. An unnecessary word was found."
|
||||||
.to_string(),
|
.to_string(),
|
||||||
@@ -464,14 +432,14 @@ mod tests {
|
|||||||
let compiler = AggegationConditionCompiler::new();
|
let compiler = AggegationConditionCompiler::new();
|
||||||
let result = compiler.compile(expr);
|
let result = compiler.compile(expr);
|
||||||
|
|
||||||
assert_eq!(true, result.is_ok());
|
assert!(result.is_ok());
|
||||||
let result = result.unwrap();
|
let result = result.unwrap();
|
||||||
assert_eq!(true, result.is_some());
|
assert!(result.is_some());
|
||||||
|
|
||||||
let result = result.unwrap();
|
let result = result.unwrap();
|
||||||
assert_eq!(true, result._by_field_name.is_none());
|
assert!(result._by_field_name.is_none());
|
||||||
assert_eq!(true, result._field_name.is_none());
|
assert!(result._field_name.is_none());
|
||||||
assert_eq!(cmp_num, result._cmp_num);
|
assert_eq!(cmp_num, result._cmp_num);
|
||||||
return result._cmp_op;
|
result._cmp_op
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ impl IntoIterator for ConditionToken {
|
|||||||
|
|
||||||
impl ConditionToken {
|
impl ConditionToken {
|
||||||
fn replace_subtoken(&self, sub_tokens: Vec<ConditionToken>) -> ConditionToken {
|
fn replace_subtoken(&self, sub_tokens: Vec<ConditionToken>) -> ConditionToken {
|
||||||
return match self {
|
match self {
|
||||||
ConditionToken::ParenthesisContainer(_) => {
|
ConditionToken::ParenthesisContainer(_) => {
|
||||||
ConditionToken::ParenthesisContainer(sub_tokens)
|
ConditionToken::ParenthesisContainer(sub_tokens)
|
||||||
}
|
}
|
||||||
@@ -74,12 +74,12 @@ impl ConditionToken {
|
|||||||
ConditionToken::SelectionReference(name) => {
|
ConditionToken::SelectionReference(name) => {
|
||||||
ConditionToken::SelectionReference(name.clone())
|
ConditionToken::SelectionReference(name.clone())
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sub_tokens<'a>(&'a self) -> Vec<ConditionToken> {
|
pub fn sub_tokens(&self) -> Vec<ConditionToken> {
|
||||||
// TODO ここでcloneを使わずに実装できるようにしたい。
|
// TODO ここでcloneを使わずに実装できるようにしたい。
|
||||||
return match self {
|
match self {
|
||||||
ConditionToken::ParenthesisContainer(sub_tokens) => sub_tokens.clone(),
|
ConditionToken::ParenthesisContainer(sub_tokens) => sub_tokens.clone(),
|
||||||
ConditionToken::AndContainer(sub_tokens) => sub_tokens.clone(),
|
ConditionToken::AndContainer(sub_tokens) => sub_tokens.clone(),
|
||||||
ConditionToken::OrContainer(sub_tokens) => sub_tokens.clone(),
|
ConditionToken::OrContainer(sub_tokens) => sub_tokens.clone(),
|
||||||
@@ -92,14 +92,14 @@ impl ConditionToken {
|
|||||||
ConditionToken::And => vec![],
|
ConditionToken::And => vec![],
|
||||||
ConditionToken::Or => vec![],
|
ConditionToken::Or => vec![],
|
||||||
ConditionToken::SelectionReference(_) => vec![],
|
ConditionToken::SelectionReference(_) => vec![],
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sub_tokens_without_parenthesis<'a>(&'a self) -> Vec<ConditionToken> {
|
pub fn sub_tokens_without_parenthesis(&self) -> Vec<ConditionToken> {
|
||||||
return match self {
|
match self {
|
||||||
ConditionToken::ParenthesisContainer(_) => vec![],
|
ConditionToken::ParenthesisContainer(_) => vec![],
|
||||||
_ => self.sub_tokens(),
|
_ => self.sub_tokens(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,8 +119,8 @@ impl ConditionCompiler {
|
|||||||
) -> Result<Box<dyn SelectionNode>, String> {
|
) -> Result<Box<dyn SelectionNode>, String> {
|
||||||
// パイプはここでは処理しない
|
// パイプはここでは処理しない
|
||||||
let captured = self::RE_PIPE.captures(&condition_str);
|
let captured = self::RE_PIPE.captures(&condition_str);
|
||||||
let condition_str = if captured.is_some() {
|
let condition_str = if let Some(cap) = captured {
|
||||||
let captured = captured.unwrap().get(0).unwrap().as_str().to_string();
|
let captured = cap.get(0).unwrap().as_str().to_string();
|
||||||
condition_str.replacen(&captured, "", 1)
|
condition_str.replacen(&captured, "", 1)
|
||||||
} else {
|
} else {
|
||||||
condition_str
|
condition_str
|
||||||
@@ -128,9 +128,9 @@ impl ConditionCompiler {
|
|||||||
|
|
||||||
let result = self.compile_condition_body(condition_str, name_2_node);
|
let result = self.compile_condition_body(condition_str, name_2_node);
|
||||||
if let Result::Err(msg) = result {
|
if let Result::Err(msg) = result {
|
||||||
return Result::Err(format!("A condition parse error has occured. {}", msg));
|
Result::Err(format!("A condition parse error has occured. {}", msg))
|
||||||
} else {
|
} else {
|
||||||
return result;
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ impl ConditionCompiler {
|
|||||||
|
|
||||||
let parsed = self.parse(tokens)?;
|
let parsed = self.parse(tokens)?;
|
||||||
|
|
||||||
return self.to_selectnode(parsed, name_2_node);
|
self.to_selectnode(parsed, name_2_node)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 構文解析を実行する。
|
/// 構文解析を実行する。
|
||||||
@@ -161,7 +161,7 @@ impl ConditionCompiler {
|
|||||||
let token = self.parse_operand_container(tokens)?;
|
let token = self.parse_operand_container(tokens)?;
|
||||||
|
|
||||||
// 括弧で囲まれている部分を探して、もしあればその部分を再帰的に構文解析します。
|
// 括弧で囲まれている部分を探して、もしあればその部分を再帰的に構文解析します。
|
||||||
return self.parse_rest_parenthesis(token);
|
self.parse_rest_parenthesis(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 括弧で囲まれている部分を探して、もしあればその部分を再帰的に構文解析します。
|
/// 括弧で囲まれている部分を探して、もしあればその部分を再帰的に構文解析します。
|
||||||
@@ -172,7 +172,7 @@ impl ConditionCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let sub_tokens = token.sub_tokens();
|
let sub_tokens = token.sub_tokens();
|
||||||
if sub_tokens.len() == 0 {
|
if sub_tokens.is_empty() {
|
||||||
return Result::Ok(token);
|
return Result::Ok(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,15 +181,15 @@ impl ConditionCompiler {
|
|||||||
let new_token = self.parse_rest_parenthesis(sub_token)?;
|
let new_token = self.parse_rest_parenthesis(sub_token)?;
|
||||||
new_sub_tokens.push(new_token);
|
new_sub_tokens.push(new_token);
|
||||||
}
|
}
|
||||||
return Result::Ok(token.replace_subtoken(new_sub_tokens));
|
Result::Ok(token.replace_subtoken(new_sub_tokens))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 字句解析を行う
|
/// 字句解析を行う
|
||||||
fn tokenize(&self, condition_str: &String) -> Result<Vec<ConditionToken>, String> {
|
fn tokenize(&self, condition_str: &str) -> Result<Vec<ConditionToken>, String> {
|
||||||
let mut cur_condition_str = condition_str.clone();
|
let mut cur_condition_str = condition_str.to_string();
|
||||||
|
|
||||||
let mut tokens = Vec::new();
|
let mut tokens = Vec::new();
|
||||||
while cur_condition_str.len() != 0 {
|
while !cur_condition_str.is_empty() {
|
||||||
let captured = self::CONDITION_REGEXMAP.iter().find_map(|regex| {
|
let captured = self::CONDITION_REGEXMAP.iter().find_map(|regex| {
|
||||||
return regex.captures(cur_condition_str.as_str());
|
return regex.captures(cur_condition_str.as_str());
|
||||||
});
|
});
|
||||||
@@ -210,25 +210,25 @@ impl ConditionCompiler {
|
|||||||
cur_condition_str = cur_condition_str.replacen(mached_str, "", 1);
|
cur_condition_str = cur_condition_str.replacen(mached_str, "", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result::Ok(tokens);
|
Result::Ok(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 文字列をConditionTokenに変換する。
|
/// 文字列をConditionTokenに変換する。
|
||||||
fn to_enum(&self, token: String) -> ConditionToken {
|
fn to_enum(&self, token: String) -> ConditionToken {
|
||||||
if token == "(" {
|
if token == "(" {
|
||||||
return ConditionToken::LeftParenthesis;
|
ConditionToken::LeftParenthesis
|
||||||
} else if token == ")" {
|
} else if token == ")" {
|
||||||
return ConditionToken::RightParenthesis;
|
ConditionToken::RightParenthesis
|
||||||
} else if token == " " {
|
} else if token == " " {
|
||||||
return ConditionToken::Space;
|
ConditionToken::Space
|
||||||
} else if token == "not" {
|
} else if token == "not" {
|
||||||
return ConditionToken::Not;
|
ConditionToken::Not
|
||||||
} else if token == "and" {
|
} else if token == "and" {
|
||||||
return ConditionToken::And;
|
ConditionToken::And
|
||||||
} else if token == "or" {
|
} else if token == "or" {
|
||||||
return ConditionToken::Or;
|
ConditionToken::Or
|
||||||
} else {
|
} else {
|
||||||
return ConditionToken::SelectionReference(token.clone());
|
ConditionToken::SelectionReference(token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,10 +241,7 @@ impl ConditionCompiler {
|
|||||||
let mut token_ite = tokens.into_iter();
|
let mut token_ite = tokens.into_iter();
|
||||||
while let Some(token) = token_ite.next() {
|
while let Some(token) = token_ite.next() {
|
||||||
// まず、左括弧を探す。
|
// まず、左括弧を探す。
|
||||||
let is_left = match token {
|
let is_left = matches!(token, ConditionToken::LeftParenthesis);
|
||||||
ConditionToken::LeftParenthesis => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
if !is_left {
|
if !is_left {
|
||||||
ret.push(token);
|
ret.push(token);
|
||||||
continue;
|
continue;
|
||||||
@@ -254,7 +251,7 @@ impl ConditionCompiler {
|
|||||||
let mut left_cnt = 1;
|
let mut left_cnt = 1;
|
||||||
let mut right_cnt = 0;
|
let mut right_cnt = 0;
|
||||||
let mut sub_tokens = vec![];
|
let mut sub_tokens = vec![];
|
||||||
while let Some(token) = token_ite.next() {
|
for token in token_ite.by_ref() {
|
||||||
if let ConditionToken::LeftParenthesis = token {
|
if let ConditionToken::LeftParenthesis = token {
|
||||||
left_cnt += 1;
|
left_cnt += 1;
|
||||||
} else if let ConditionToken::RightParenthesis = token {
|
} else if let ConditionToken::RightParenthesis = token {
|
||||||
@@ -275,22 +272,19 @@ impl ConditionCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// この時点で右括弧が残っている場合は右括弧の数が左括弧よりも多いことを表している。
|
// この時点で右括弧が残っている場合は右括弧の数が左括弧よりも多いことを表している。
|
||||||
let is_right_left = ret.iter().any(|token| {
|
let is_right_left = ret
|
||||||
return match token {
|
.iter()
|
||||||
ConditionToken::RightParenthesis => true,
|
.any(|token| matches!(token, ConditionToken::RightParenthesis));
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
if is_right_left {
|
if is_right_left {
|
||||||
return Result::Err("'(' was expected but not found.".to_string());
|
return Result::Err("'(' was expected but not found.".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result::Ok(ret);
|
Result::Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// AND, ORをパースする。
|
/// AND, ORをパースする。
|
||||||
fn parse_and_or_operator(&self, tokens: Vec<ConditionToken>) -> Result<ConditionToken, String> {
|
fn parse_and_or_operator(&self, tokens: Vec<ConditionToken>) -> Result<ConditionToken, String> {
|
||||||
if tokens.len() == 0 {
|
if tokens.is_empty() {
|
||||||
// 長さ0は呼び出してはいけない
|
// 長さ0は呼び出してはいけない
|
||||||
return Result::Err("Unknown error.".to_string());
|
return Result::Err("Unknown error.".to_string());
|
||||||
}
|
}
|
||||||
@@ -339,7 +333,7 @@ impl ConditionCompiler {
|
|||||||
|
|
||||||
// 次にOrでつながっている部分をまとめる
|
// 次にOrでつながっている部分をまとめる
|
||||||
let or_contaienr = ConditionToken::OrContainer(operands);
|
let or_contaienr = ConditionToken::OrContainer(operands);
|
||||||
return Result::Ok(or_contaienr);
|
Result::Ok(or_contaienr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// OperandContainerの中身をパースする。現状はNotをパースするためだけに存在している。
|
/// OperandContainerの中身をパースする。現状はNotをパースするためだけに存在している。
|
||||||
@@ -360,7 +354,7 @@ impl ConditionCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 0はありえないはず
|
// 0はありえないはず
|
||||||
if sub_tokens.len() == 0 {
|
if sub_tokens.is_empty() {
|
||||||
return Result::Err("Unknown error.".to_string());
|
return Result::Err("Unknown error.".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,20 +374,20 @@ impl ConditionCompiler {
|
|||||||
let second_token = sub_tokens_ite.next().unwrap();
|
let second_token = sub_tokens_ite.next().unwrap();
|
||||||
if let ConditionToken::Not = first_token {
|
if let ConditionToken::Not = first_token {
|
||||||
if let ConditionToken::Not = second_token {
|
if let ConditionToken::Not = second_token {
|
||||||
return Result::Err("Not is continuous.".to_string());
|
Result::Err("Not is continuous.".to_string())
|
||||||
} else {
|
} else {
|
||||||
let not_container = ConditionToken::NotContainer(vec![second_token]);
|
let not_container = ConditionToken::NotContainer(vec![second_token]);
|
||||||
return Result::Ok(not_container);
|
Result::Ok(not_container)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Result::Err(
|
Result::Err(
|
||||||
"Unknown error. Maybe it is because there are multiple names of selection nodes."
|
"Unknown error. Maybe it is because there are multiple names of selection nodes."
|
||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let sub_tokens = parent_token.sub_tokens_without_parenthesis();
|
let sub_tokens = parent_token.sub_tokens_without_parenthesis();
|
||||||
if sub_tokens.len() == 0 {
|
if sub_tokens.is_empty() {
|
||||||
return Result::Ok(parent_token);
|
return Result::Ok(parent_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,7 +397,7 @@ impl ConditionCompiler {
|
|||||||
new_sub_tokens.push(new_sub_token);
|
new_sub_tokens.push(new_sub_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result::Ok(parent_token.replace_subtoken(new_sub_tokens));
|
Result::Ok(parent_token.replace_subtoken(new_sub_tokens))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,14 +410,14 @@ impl ConditionCompiler {
|
|||||||
// RefSelectionNodeに変換
|
// RefSelectionNodeに変換
|
||||||
if let ConditionToken::SelectionReference(selection_name) = token {
|
if let ConditionToken::SelectionReference(selection_name) = token {
|
||||||
let selection_node = name_2_node.get(&selection_name);
|
let selection_node = name_2_node.get(&selection_name);
|
||||||
if selection_node.is_none() {
|
if let Some(select_node) = selection_node {
|
||||||
let err_msg = format!("{} is not defined.", selection_name);
|
let selection_node = select_node;
|
||||||
return Result::Err(err_msg);
|
|
||||||
} else {
|
|
||||||
let selection_node = selection_node.unwrap();
|
|
||||||
let selection_node = Arc::clone(selection_node);
|
let selection_node = Arc::clone(selection_node);
|
||||||
let ref_node = RefSelectionNode::new(selection_node);
|
let ref_node = RefSelectionNode::new(selection_node);
|
||||||
return Result::Ok(Box::new(ref_node));
|
return Result::Ok(Box::new(ref_node));
|
||||||
|
} else {
|
||||||
|
let err_msg = format!("{} is not defined.", selection_name);
|
||||||
|
return Result::Err(err_msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,16 +453,12 @@ impl ConditionCompiler {
|
|||||||
return Result::Ok(Box::new(select_not_node));
|
return Result::Ok(Box::new(select_not_node));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result::Err("Unknown error".to_string());
|
Result::Err("Unknown error".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ConditionTokenがAndまたはOrTokenならばTrue
|
/// ConditionTokenがAndまたはOrTokenならばTrue
|
||||||
fn is_logical(&self, token: &ConditionToken) -> bool {
|
fn is_logical(&self, token: &ConditionToken) -> bool {
|
||||||
return match token {
|
matches!(token, ConditionToken::And | ConditionToken::Or)
|
||||||
ConditionToken::And => true,
|
|
||||||
ConditionToken::Or => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ConditionToken::OperandContainerに変換できる部分があれば変換する。
|
/// ConditionToken::OperandContainerに変換できる部分があれば変換する。
|
||||||
@@ -478,8 +468,7 @@ impl ConditionCompiler {
|
|||||||
) -> Result<Vec<ConditionToken>, String> {
|
) -> Result<Vec<ConditionToken>, String> {
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
let mut grouped_operands = vec![]; // ANDとORの間にあるトークンを表す。ANDとORをOperatorとしたときのOperand
|
let mut grouped_operands = vec![]; // ANDとORの間にあるトークンを表す。ANDとORをOperatorとしたときのOperand
|
||||||
let mut token_ite = tokens.into_iter();
|
for token in tokens.into_iter() {
|
||||||
while let Some(token) = token_ite.next() {
|
|
||||||
if self.is_logical(&token) {
|
if self.is_logical(&token) {
|
||||||
// ここに来るのはエラーのはずだが、後でエラー出力するので、ここではエラー出さない。
|
// ここに来るのはエラーのはずだが、後でエラー出力するので、ここではエラー出さない。
|
||||||
if grouped_operands.is_empty() {
|
if grouped_operands.is_empty() {
|
||||||
@@ -498,7 +487,7 @@ impl ConditionCompiler {
|
|||||||
ret.push(ConditionToken::OperandContainer(grouped_operands));
|
ret.push(ConditionToken::OperandContainer(grouped_operands));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result::Ok(ret);
|
Result::Ok(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,7 +531,7 @@ mod tests {
|
|||||||
assert_eq!(rule_node.select(&recinfo), expect_select);
|
assert_eq!(rule_node.select(&recinfo), expect_select);
|
||||||
}
|
}
|
||||||
Err(_rec) => {
|
Err(_rec) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -582,10 +571,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_rec) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -626,10 +615,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_rec) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ lazy_static! {
|
|||||||
// LeafSelectionNodeのget_matchersクラスの戻り値の配列に新規作成したクラスのインスタンスを追加する。
|
// LeafSelectionNodeのget_matchersクラスの戻り値の配列に新規作成したクラスのインスタンスを追加する。
|
||||||
pub trait LeafMatcher: mopa::Any {
|
pub trait LeafMatcher: mopa::Any {
|
||||||
/// 指定されたkey_listにマッチするLeafMatcherであるかどうか判定する。
|
/// 指定されたkey_listにマッチするLeafMatcherであるかどうか判定する。
|
||||||
fn is_target_key(&self, key_list: &Vec<String>) -> bool;
|
fn is_target_key(&self, key_list: &[String]) -> bool;
|
||||||
|
|
||||||
/// 引数に指定されたJSON形式のデータがマッチするかどうか判定する。
|
/// 引数に指定されたJSON形式のデータがマッチするかどうか判定する。
|
||||||
/// main.rsでWindows Event LogをJSON形式に変換していて、そのJSON形式のWindowsのイベントログデータがここには来る
|
/// main.rsでWindows Event LogをJSON形式に変換していて、そのJSON形式のWindowsのイベントログデータがここには来る
|
||||||
@@ -26,7 +26,7 @@ pub trait LeafMatcher: mopa::Any {
|
|||||||
|
|
||||||
/// 初期化ロジックをここに記載します。
|
/// 初期化ロジックをここに記載します。
|
||||||
/// ルールファイルの書き方が間違っている等の原因により、正しくルールファイルからパースできない場合、戻り値のResult型でエラーを返してください。
|
/// ルールファイルの書き方が間違っている等の原因により、正しくルールファイルからパースできない場合、戻り値のResult型でエラーを返してください。
|
||||||
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>>;
|
fn init(&mut self, key_list: &[String], select_value: &Yaml) -> Result<(), Vec<String>>;
|
||||||
}
|
}
|
||||||
mopafy!(LeafMatcher);
|
mopafy!(LeafMatcher);
|
||||||
|
|
||||||
@@ -37,12 +37,12 @@ pub struct MinlengthMatcher {
|
|||||||
|
|
||||||
impl MinlengthMatcher {
|
impl MinlengthMatcher {
|
||||||
pub fn new() -> MinlengthMatcher {
|
pub fn new() -> MinlengthMatcher {
|
||||||
return MinlengthMatcher { min_len: 0 };
|
MinlengthMatcher { min_len: 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LeafMatcher for MinlengthMatcher {
|
impl LeafMatcher for MinlengthMatcher {
|
||||||
fn is_target_key(&self, key_list: &Vec<String>) -> bool {
|
fn is_target_key(&self, key_list: &[String]) -> bool {
|
||||||
if key_list.len() != 2 {
|
if key_list.len() != 2 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,7 @@ impl LeafMatcher for MinlengthMatcher {
|
|||||||
return key_list.get(1).unwrap() == "min_length";
|
return key_list.get(1).unwrap() == "min_length";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>> {
|
fn init(&mut self, key_list: &[String], select_value: &Yaml) -> Result<(), Vec<String>> {
|
||||||
let min_length = select_value.as_i64();
|
let min_length = select_value.as_i64();
|
||||||
if min_length.is_none() {
|
if min_length.is_none() {
|
||||||
let errmsg = format!(
|
let errmsg = format!(
|
||||||
@@ -61,14 +61,14 @@ impl LeafMatcher for MinlengthMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.min_len = min_length.unwrap();
|
self.min_len = min_length.unwrap();
|
||||||
return Result::Ok(());
|
Result::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool {
|
fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool {
|
||||||
return match event_value {
|
match event_value {
|
||||||
Some(s) => s.len() as i64 >= self.min_len,
|
Some(s) => s.len() as i64 >= self.min_len,
|
||||||
None => false,
|
None => false,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,20 +80,20 @@ pub struct RegexesFileMatcher {
|
|||||||
|
|
||||||
impl RegexesFileMatcher {
|
impl RegexesFileMatcher {
|
||||||
pub fn new() -> RegexesFileMatcher {
|
pub fn new() -> RegexesFileMatcher {
|
||||||
return RegexesFileMatcher { regexes: vec![] };
|
RegexesFileMatcher { regexes: vec![] }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LeafMatcher for RegexesFileMatcher {
|
impl LeafMatcher for RegexesFileMatcher {
|
||||||
fn is_target_key(&self, key_list: &Vec<String>) -> bool {
|
fn is_target_key(&self, key_list: &[String]) -> bool {
|
||||||
if key_list.len() != 2 {
|
if key_list.len() != 2 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return key_list.get(1).unwrap() == "regexes";
|
key_list.get(1).unwrap() == "regexes"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>> {
|
fn init(&mut self, key_list: &[String], select_value: &Yaml) -> Result<(), Vec<String>> {
|
||||||
let value = match select_value {
|
let value = match select_value {
|
||||||
Yaml::String(s) => Option::Some(s.to_owned()),
|
Yaml::String(s) => Option::Some(s.to_owned()),
|
||||||
Yaml::Integer(i) => Option::Some(i.to_string()),
|
Yaml::Integer(i) => Option::Some(i.to_string()),
|
||||||
@@ -118,14 +118,14 @@ impl LeafMatcher for RegexesFileMatcher {
|
|||||||
.map(|regex_str| Regex::new(®ex_str).unwrap())
|
.map(|regex_str| Regex::new(®ex_str).unwrap())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
return Result::Ok(());
|
Result::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool {
|
fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool {
|
||||||
return match event_value {
|
match event_value {
|
||||||
Some(s) => utils::check_regex(s, &self.regexes),
|
Some(s) => utils::check_regex(s, &self.regexes),
|
||||||
None => false,
|
None => false,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,12 +137,12 @@ pub struct AllowlistFileMatcher {
|
|||||||
|
|
||||||
impl AllowlistFileMatcher {
|
impl AllowlistFileMatcher {
|
||||||
pub fn new() -> AllowlistFileMatcher {
|
pub fn new() -> AllowlistFileMatcher {
|
||||||
return AllowlistFileMatcher { regexes: vec![] };
|
AllowlistFileMatcher { regexes: vec![] }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LeafMatcher for AllowlistFileMatcher {
|
impl LeafMatcher for AllowlistFileMatcher {
|
||||||
fn is_target_key(&self, key_list: &Vec<String>) -> bool {
|
fn is_target_key(&self, key_list: &[String]) -> bool {
|
||||||
if key_list.len() != 2 {
|
if key_list.len() != 2 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -150,7 +150,7 @@ impl LeafMatcher for AllowlistFileMatcher {
|
|||||||
return key_list.get(1).unwrap() == "allowlist";
|
return key_list.get(1).unwrap() == "allowlist";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>> {
|
fn init(&mut self, key_list: &[String], select_value: &Yaml) -> Result<(), Vec<String>> {
|
||||||
let value = match select_value {
|
let value = match select_value {
|
||||||
Yaml::String(s) => Option::Some(s.to_owned()),
|
Yaml::String(s) => Option::Some(s.to_owned()),
|
||||||
Yaml::Integer(i) => Option::Some(i.to_string()),
|
Yaml::Integer(i) => Option::Some(i.to_string()),
|
||||||
@@ -175,14 +175,14 @@ impl LeafMatcher for AllowlistFileMatcher {
|
|||||||
.map(|regex_str| Regex::new(®ex_str).unwrap())
|
.map(|regex_str| Regex::new(®ex_str).unwrap())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
return Result::Ok(());
|
Result::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool {
|
fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool {
|
||||||
return match event_value {
|
match event_value {
|
||||||
Some(s) => !utils::check_allowlist(s, &self.regexes),
|
Some(s) => !utils::check_allowlist(s, &self.regexes),
|
||||||
None => true,
|
None => true,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,39 +196,34 @@ pub struct DefaultMatcher {
|
|||||||
|
|
||||||
impl DefaultMatcher {
|
impl DefaultMatcher {
|
||||||
pub fn new() -> DefaultMatcher {
|
pub fn new() -> DefaultMatcher {
|
||||||
return DefaultMatcher {
|
DefaultMatcher {
|
||||||
re: Option::None,
|
re: Option::None,
|
||||||
pipes: Vec::new(),
|
pipes: Vec::new(),
|
||||||
key_list: Vec::new(),
|
key_list: Vec::new(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// このmatcherの正規表現とマッチするかどうか判定します。
|
/// このmatcherの正規表現とマッチするかどうか判定します。
|
||||||
/// 判定対象の文字列とこのmatcherが保持する正規表現が完全にマッチした場合のTRUEを返します。
|
/// 判定対象の文字列とこのmatcherが保持する正規表現が完全にマッチした場合のTRUEを返します。
|
||||||
/// 例えば、判定対象文字列が"abc"で、正規表現が"ab"の場合、正規表現は判定対象文字列の一部分にしか一致していないので、この関数はfalseを返します。
|
/// 例えば、判定対象文字列が"abc"で、正規表現が"ab"の場合、正規表現は判定対象文字列の一部分にしか一致していないので、この関数はfalseを返します。
|
||||||
fn is_regex_fullmatch(&self, value: &String) -> bool {
|
fn is_regex_fullmatch(&self, value: &str) -> bool {
|
||||||
return self
|
return self.re.as_ref().unwrap().find_iter(value).any(|match_obj| {
|
||||||
.re
|
return match_obj.as_str() == value;
|
||||||
.as_ref()
|
});
|
||||||
.unwrap()
|
|
||||||
.find_iter(&value)
|
|
||||||
.any(|match_obj| {
|
|
||||||
return match_obj.as_str() == value;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// YEAのルールファイルのフィールド名とそれに続いて指定されるパイプを、正規表現形式の文字列に変換します。
|
/// YEAのルールファイルのフィールド名とそれに続いて指定されるパイプを、正規表現形式の文字列に変換します。
|
||||||
/// ワイルドカードの文字列を正規表現にする処理もこのメソッドに実装されています。patternにワイルドカードの文字列を指定して、pipesにPipeElement::Wildcardを指定すればOK!!
|
/// ワイルドカードの文字列を正規表現にする処理もこのメソッドに実装されています。patternにワイルドカードの文字列を指定して、pipesにPipeElement::Wildcardを指定すればOK!!
|
||||||
fn from_pattern_to_regex_str(pattern: String, pipes: &Vec<PipeElement>) -> String {
|
fn from_pattern_to_regex_str(pattern: String, pipes: &[PipeElement]) -> String {
|
||||||
// パターンをPipeで処理する。
|
// パターンをPipeで処理する。
|
||||||
return pipes.iter().fold(pattern, |acc, pipe| {
|
pipes
|
||||||
return pipe.pipe_pattern(acc);
|
.iter()
|
||||||
});
|
.fold(pattern, |acc, pipe| pipe.pipe_pattern(acc))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LeafMatcher for DefaultMatcher {
|
impl LeafMatcher for DefaultMatcher {
|
||||||
fn is_target_key(&self, key_list: &Vec<String>) -> bool {
|
fn is_target_key(&self, key_list: &[String]) -> bool {
|
||||||
if key_list.len() <= 1 {
|
if key_list.len() <= 1 {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -236,7 +231,7 @@ impl LeafMatcher for DefaultMatcher {
|
|||||||
return key_list.get(1).unwrap_or(&"".to_string()) == "value";
|
return key_list.get(1).unwrap_or(&"".to_string()) == "value";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>> {
|
fn init(&mut self, key_list: &[String], select_value: &Yaml) -> Result<(), Vec<String>> {
|
||||||
self.key_list = key_list.to_vec();
|
self.key_list = key_list.to_vec();
|
||||||
if select_value.is_null() {
|
if select_value.is_null() {
|
||||||
return Result::Ok(());
|
return Result::Ok(());
|
||||||
@@ -261,7 +256,7 @@ impl LeafMatcher for DefaultMatcher {
|
|||||||
|
|
||||||
// Pipeが指定されていればパースする
|
// Pipeが指定されていればパースする
|
||||||
let emp = String::default();
|
let emp = String::default();
|
||||||
let mut keys: VecDeque<&str> = key_list.get(0).unwrap_or(&emp).split("|").collect(); // key_listが空はあり得ない
|
let mut keys: VecDeque<&str> = key_list.get(0).unwrap_or(&emp).split('|').collect(); // key_listが空はあり得ない
|
||||||
keys.pop_front(); // 一つ目はただのキーで、2つめ以降がpipe
|
keys.pop_front(); // 一つ目はただのキーで、2つめ以降がpipe
|
||||||
while !keys.is_empty() {
|
while !keys.is_empty() {
|
||||||
let key = keys.pop_front().unwrap();
|
let key = keys.pop_front().unwrap();
|
||||||
@@ -290,12 +285,10 @@ impl LeafMatcher for DefaultMatcher {
|
|||||||
);
|
);
|
||||||
return Result::Err(vec![errmsg]);
|
return Result::Err(vec![errmsg]);
|
||||||
}
|
}
|
||||||
let is_re = &self.pipes.iter().any(|pipe_element| {
|
let is_re = &self
|
||||||
return match pipe_element {
|
.pipes
|
||||||
PipeElement::Re => true,
|
.iter()
|
||||||
_ => false,
|
.any(|pipe_element| matches!(pipe_element, PipeElement::Re));
|
||||||
};
|
|
||||||
});
|
|
||||||
// 正規表現ではない場合、ワイルドカードであることを表す。
|
// 正規表現ではない場合、ワイルドカードであることを表す。
|
||||||
// ワイルドカードは正規表現でマッチングするので、ワイルドカードを正規表現に変換するPipeを内部的に追加することにする。
|
// ワイルドカードは正規表現でマッチングするので、ワイルドカードを正規表現に変換するPipeを内部的に追加することにする。
|
||||||
if !is_re {
|
if !is_re {
|
||||||
@@ -316,7 +309,7 @@ impl LeafMatcher for DefaultMatcher {
|
|||||||
}
|
}
|
||||||
self.re = re_result.ok();
|
self.re = re_result.ok();
|
||||||
|
|
||||||
return Result::Ok(());
|
Result::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool {
|
fn is_match(&self, event_value: Option<&String>, _recinfo: &EvtxRecordInfo) -> bool {
|
||||||
@@ -333,10 +326,10 @@ impl LeafMatcher for DefaultMatcher {
|
|||||||
let event_value_str = event_value.unwrap();
|
let event_value_str = event_value.unwrap();
|
||||||
if self.key_list.is_empty() {
|
if self.key_list.is_empty() {
|
||||||
// この場合ただのgrep検索なので、ただ正規表現に一致するかどうか調べればよいだけ
|
// この場合ただのgrep検索なので、ただ正規表現に一致するかどうか調べればよいだけ
|
||||||
return self.re.as_ref().unwrap().is_match(&event_value_str);
|
return self.re.as_ref().unwrap().is_match(event_value_str);
|
||||||
} else {
|
} else {
|
||||||
// 通常の検索はこっち
|
// 通常の検索はこっち
|
||||||
return self.is_regex_fullmatch(&event_value_str);
|
self.is_regex_fullmatch(event_value_str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -356,28 +349,28 @@ impl PipeElement {
|
|||||||
// enumでポリモーフィズムを実装すると、一つのメソッドに全部の型の実装をする感じになる。Java使い的にはキモイ感じがする。
|
// enumでポリモーフィズムを実装すると、一つのメソッドに全部の型の実装をする感じになる。Java使い的にはキモイ感じがする。
|
||||||
let fn_add_asterisk_end = |patt: String| {
|
let fn_add_asterisk_end = |patt: String| {
|
||||||
if patt.ends_with("//*") {
|
if patt.ends_with("//*") {
|
||||||
return patt;
|
patt
|
||||||
} else if patt.ends_with("/*") {
|
} else if patt.ends_with("/*") {
|
||||||
return patt + "*";
|
patt + "*"
|
||||||
} else if patt.ends_with("*") {
|
} else if patt.ends_with('*') {
|
||||||
return patt;
|
patt
|
||||||
} else {
|
} else {
|
||||||
return patt + "*";
|
patt + "*"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let fn_add_asterisk_begin = |patt: String| {
|
let fn_add_asterisk_begin = |patt: String| {
|
||||||
if patt.starts_with("//*") {
|
if patt.starts_with("//*") {
|
||||||
return patt;
|
patt
|
||||||
} else if patt.starts_with("/*") {
|
} else if patt.starts_with("/*") {
|
||||||
return "*".to_string() + &patt;
|
"*".to_string() + &patt
|
||||||
} else if patt.starts_with("*") {
|
} else if patt.starts_with('*') {
|
||||||
return patt;
|
patt
|
||||||
} else {
|
} else {
|
||||||
return "*".to_string() + &patt;
|
"*".to_string() + &patt
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let val: String = match self {
|
match self {
|
||||||
// startswithの場合はpatternの最後にwildcardを足すことで対応する
|
// startswithの場合はpatternの最後にwildcardを足すことで対応する
|
||||||
PipeElement::Startswith => fn_add_asterisk_end(pattern),
|
PipeElement::Startswith => fn_add_asterisk_end(pattern),
|
||||||
// endswithの場合はpatternの最初にwildcardを足すことで対応する
|
// endswithの場合はpatternの最初にwildcardを足すことで対応する
|
||||||
@@ -388,8 +381,7 @@ impl PipeElement {
|
|||||||
PipeElement::Re => pattern,
|
PipeElement::Re => pattern,
|
||||||
// WildCardは正規表現に変換する。
|
// WildCardは正規表現に変換する。
|
||||||
PipeElement::Wildcard => PipeElement::pipe_pattern_wildcard(pattern),
|
PipeElement::Wildcard => PipeElement::pipe_pattern_wildcard(pattern),
|
||||||
};
|
}
|
||||||
return val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PipeElement::Wildcardのパイプ処理です。
|
/// PipeElement::Wildcardのパイプ処理です。
|
||||||
@@ -456,11 +448,7 @@ impl PipeElement {
|
|||||||
regex::escape(pattern)
|
regex::escape(pattern)
|
||||||
} else {
|
} else {
|
||||||
// wildcardの場合、"*"は".*"という正規表現に変換し、"?"は"."に変換する。
|
// wildcardの場合、"*"は".*"という正規表現に変換し、"?"は"."に変換する。
|
||||||
let wildcard_regex_value = if pattern.to_string() == "*" {
|
let wildcard_regex_value = if *pattern == "*" { ".*" } else { "." };
|
||||||
".*"
|
|
||||||
} else {
|
|
||||||
"."
|
|
||||||
};
|
|
||||||
wildcard_regex_value.to_string()
|
wildcard_regex_value.to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -470,7 +458,7 @@ impl PipeElement {
|
|||||||
|
|
||||||
// sigmaのwildcardはcase insensitive
|
// sigmaのwildcardはcase insensitive
|
||||||
// なので、正規表現の先頭にcase insensitiveであることを表す記号を付与
|
// なので、正規表現の先頭にcase insensitiveであることを表す記号を付与
|
||||||
return "(?i)".to_string() + &ret;
|
"(?i)".to_string() + &ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -523,20 +511,20 @@ mod tests {
|
|||||||
// Channel
|
// Channel
|
||||||
{
|
{
|
||||||
// LeafSelectionNodeが正しく読み込めることを確認
|
// LeafSelectionNodeが正しく読み込めることを確認
|
||||||
let child_node = detection_childs[0].as_ref() as &dyn SelectionNode; // TODO キャストしないとエラーでるけど、このキャストよく分からん。
|
let child_node = detection_childs[0] as &dyn SelectionNode; // TODO キャストしないとエラーでるけど、このキャストよく分からん。
|
||||||
assert_eq!(child_node.is::<LeafSelectionNode>(), true);
|
assert!(child_node.is::<LeafSelectionNode>());
|
||||||
let child_node = child_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
let child_node = child_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
||||||
assert_eq!(child_node.get_key(), "Channel");
|
assert_eq!(child_node.get_key(), "Channel");
|
||||||
assert_eq!(child_node.get_childs().len(), 0);
|
assert_eq!(child_node.get_childs().len(), 0);
|
||||||
|
|
||||||
// 比較する正規表現が正しいことを確認
|
// 比較する正規表現が正しいことを確認
|
||||||
let matcher = &child_node.matcher;
|
let matcher = &child_node.matcher;
|
||||||
assert_eq!(matcher.is_some(), true);
|
assert!(matcher.is_some());
|
||||||
let matcher = child_node.matcher.as_ref().unwrap();
|
let matcher = child_node.matcher.as_ref().unwrap();
|
||||||
assert_eq!(matcher.is::<DefaultMatcher>(), true);
|
assert!(matcher.is::<DefaultMatcher>());
|
||||||
let matcher = matcher.downcast_ref::<DefaultMatcher>().unwrap();
|
let matcher = matcher.downcast_ref::<DefaultMatcher>().unwrap();
|
||||||
|
|
||||||
assert_eq!(matcher.re.is_some(), true);
|
assert!(matcher.re.is_some());
|
||||||
let re = matcher.re.as_ref();
|
let re = matcher.re.as_ref();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
re.unwrap().as_str(),
|
re.unwrap().as_str(),
|
||||||
@@ -547,20 +535,20 @@ mod tests {
|
|||||||
// EventID
|
// EventID
|
||||||
{
|
{
|
||||||
// LeafSelectionNodeが正しく読み込めることを確認
|
// LeafSelectionNodeが正しく読み込めることを確認
|
||||||
let child_node = detection_childs[1].as_ref() as &dyn SelectionNode;
|
let child_node = detection_childs[1] as &dyn SelectionNode;
|
||||||
assert_eq!(child_node.is::<LeafSelectionNode>(), true);
|
assert!(child_node.is::<LeafSelectionNode>());
|
||||||
let child_node = child_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
let child_node = child_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
||||||
assert_eq!(child_node.get_key(), "EventID");
|
assert_eq!(child_node.get_key(), "EventID");
|
||||||
assert_eq!(child_node.get_childs().len(), 0);
|
assert_eq!(child_node.get_childs().len(), 0);
|
||||||
|
|
||||||
// 比較する正規表現が正しいことを確認
|
// 比較する正規表現が正しいことを確認
|
||||||
let matcher = &child_node.matcher;
|
let matcher = &child_node.matcher;
|
||||||
assert_eq!(matcher.is_some(), true);
|
assert!(matcher.is_some());
|
||||||
let matcher = child_node.matcher.as_ref().unwrap();
|
let matcher = child_node.matcher.as_ref().unwrap();
|
||||||
assert_eq!(matcher.is::<DefaultMatcher>(), true);
|
assert!(matcher.is::<DefaultMatcher>());
|
||||||
let matcher = matcher.downcast_ref::<DefaultMatcher>().unwrap();
|
let matcher = matcher.downcast_ref::<DefaultMatcher>().unwrap();
|
||||||
|
|
||||||
assert_eq!(matcher.re.is_some(), true);
|
assert!(matcher.re.is_some());
|
||||||
let re = matcher.re.as_ref();
|
let re = matcher.re.as_ref();
|
||||||
assert_eq!(re.unwrap().as_str(), "(?i)4103");
|
assert_eq!(re.unwrap().as_str(), "(?i)4103");
|
||||||
}
|
}
|
||||||
@@ -568,38 +556,38 @@ mod tests {
|
|||||||
// ContextInfo
|
// ContextInfo
|
||||||
{
|
{
|
||||||
// OrSelectionNodeを正しく読み込めることを確認
|
// OrSelectionNodeを正しく読み込めることを確認
|
||||||
let child_node = detection_childs[2].as_ref() as &dyn SelectionNode;
|
let child_node = detection_childs[2] as &dyn SelectionNode;
|
||||||
assert_eq!(child_node.is::<OrSelectionNode>(), true);
|
assert!(child_node.is::<OrSelectionNode>());
|
||||||
let child_node = child_node.downcast_ref::<OrSelectionNode>().unwrap();
|
let child_node = child_node.downcast_ref::<OrSelectionNode>().unwrap();
|
||||||
let ancestors = child_node.get_childs();
|
let ancestors = child_node.get_childs();
|
||||||
assert_eq!(ancestors.len(), 2);
|
assert_eq!(ancestors.len(), 2);
|
||||||
|
|
||||||
// OrSelectionNodeの下にLeafSelectionNodeがあるパターンをテスト
|
// OrSelectionNodeの下にLeafSelectionNodeがあるパターンをテスト
|
||||||
// LeafSelectionNodeである、Host Applicationノードが正しいことを確認
|
// LeafSelectionNodeである、Host Applicationノードが正しいことを確認
|
||||||
let hostapp_en_node = ancestors[0].as_ref() as &dyn SelectionNode;
|
let hostapp_en_node = ancestors[0] as &dyn SelectionNode;
|
||||||
assert_eq!(hostapp_en_node.is::<LeafSelectionNode>(), true);
|
assert!(hostapp_en_node.is::<LeafSelectionNode>());
|
||||||
let hostapp_en_node = hostapp_en_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
let hostapp_en_node = hostapp_en_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
||||||
|
|
||||||
let hostapp_en_matcher = &hostapp_en_node.matcher;
|
let hostapp_en_matcher = &hostapp_en_node.matcher;
|
||||||
assert_eq!(hostapp_en_matcher.is_some(), true);
|
assert!(hostapp_en_matcher.is_some());
|
||||||
let hostapp_en_matcher = hostapp_en_matcher.as_ref().unwrap();
|
let hostapp_en_matcher = hostapp_en_matcher.as_ref().unwrap();
|
||||||
assert_eq!(hostapp_en_matcher.is::<DefaultMatcher>(), true);
|
assert!(hostapp_en_matcher.is::<DefaultMatcher>());
|
||||||
let hostapp_en_matcher = hostapp_en_matcher.downcast_ref::<DefaultMatcher>().unwrap();
|
let hostapp_en_matcher = hostapp_en_matcher.downcast_ref::<DefaultMatcher>().unwrap();
|
||||||
assert_eq!(hostapp_en_matcher.re.is_some(), true);
|
assert!(hostapp_en_matcher.re.is_some());
|
||||||
let re = hostapp_en_matcher.re.as_ref();
|
let re = hostapp_en_matcher.re.as_ref();
|
||||||
assert_eq!(re.unwrap().as_str(), "(?i)Host Application");
|
assert_eq!(re.unwrap().as_str(), "(?i)Host Application");
|
||||||
|
|
||||||
// LeafSelectionNodeである、ホスト アプリケーションノードが正しいことを確認
|
// LeafSelectionNodeである、ホスト アプリケーションノードが正しいことを確認
|
||||||
let hostapp_jp_node = ancestors[1].as_ref() as &dyn SelectionNode;
|
let hostapp_jp_node = ancestors[1] as &dyn SelectionNode;
|
||||||
assert_eq!(hostapp_jp_node.is::<LeafSelectionNode>(), true);
|
assert!(hostapp_jp_node.is::<LeafSelectionNode>());
|
||||||
let hostapp_jp_node = hostapp_jp_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
let hostapp_jp_node = hostapp_jp_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
||||||
|
|
||||||
let hostapp_jp_matcher = &hostapp_jp_node.matcher;
|
let hostapp_jp_matcher = &hostapp_jp_node.matcher;
|
||||||
assert_eq!(hostapp_jp_matcher.is_some(), true);
|
assert!(hostapp_jp_matcher.is_some());
|
||||||
let hostapp_jp_matcher = hostapp_jp_matcher.as_ref().unwrap();
|
let hostapp_jp_matcher = hostapp_jp_matcher.as_ref().unwrap();
|
||||||
assert_eq!(hostapp_jp_matcher.is::<DefaultMatcher>(), true);
|
assert!(hostapp_jp_matcher.is::<DefaultMatcher>());
|
||||||
let hostapp_jp_matcher = hostapp_jp_matcher.downcast_ref::<DefaultMatcher>().unwrap();
|
let hostapp_jp_matcher = hostapp_jp_matcher.downcast_ref::<DefaultMatcher>().unwrap();
|
||||||
assert_eq!(hostapp_jp_matcher.re.is_some(), true);
|
assert!(hostapp_jp_matcher.re.is_some());
|
||||||
let re = hostapp_jp_matcher.re.as_ref();
|
let re = hostapp_jp_matcher.re.as_ref();
|
||||||
assert_eq!(re.unwrap().as_str(), "(?i)ホスト アプリケーション");
|
assert_eq!(re.unwrap().as_str(), "(?i)ホスト アプリケーション");
|
||||||
}
|
}
|
||||||
@@ -607,36 +595,36 @@ mod tests {
|
|||||||
// ImagePath
|
// ImagePath
|
||||||
{
|
{
|
||||||
// AndSelectionNodeを正しく読み込めることを確認
|
// AndSelectionNodeを正しく読み込めることを確認
|
||||||
let child_node = detection_childs[3].as_ref() as &dyn SelectionNode;
|
let child_node = detection_childs[3] as &dyn SelectionNode;
|
||||||
assert_eq!(child_node.is::<AndSelectionNode>(), true);
|
assert!(child_node.is::<AndSelectionNode>());
|
||||||
let child_node = child_node.downcast_ref::<AndSelectionNode>().unwrap();
|
let child_node = child_node.downcast_ref::<AndSelectionNode>().unwrap();
|
||||||
let ancestors = child_node.get_childs();
|
let ancestors = child_node.get_childs();
|
||||||
assert_eq!(ancestors.len(), 3);
|
assert_eq!(ancestors.len(), 3);
|
||||||
|
|
||||||
// min-lenが正しく読み込めることを確認
|
// min-lenが正しく読み込めることを確認
|
||||||
{
|
{
|
||||||
let ancestor_node = ancestors[0].as_ref() as &dyn SelectionNode;
|
let ancestor_node = ancestors[0] as &dyn SelectionNode;
|
||||||
assert_eq!(ancestor_node.is::<LeafSelectionNode>(), true);
|
assert!(ancestor_node.is::<LeafSelectionNode>());
|
||||||
let ancestor_node = ancestor_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
let ancestor_node = ancestor_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
||||||
|
|
||||||
let ancestor_node = &ancestor_node.matcher;
|
let ancestor_node = &ancestor_node.matcher;
|
||||||
assert_eq!(ancestor_node.is_some(), true);
|
assert!(ancestor_node.is_some());
|
||||||
let ancestor_matcher = ancestor_node.as_ref().unwrap();
|
let ancestor_matcher = ancestor_node.as_ref().unwrap();
|
||||||
assert_eq!(ancestor_matcher.is::<MinlengthMatcher>(), true);
|
assert!(ancestor_matcher.is::<MinlengthMatcher>());
|
||||||
let ancestor_matcher = ancestor_matcher.downcast_ref::<MinlengthMatcher>().unwrap();
|
let ancestor_matcher = ancestor_matcher.downcast_ref::<MinlengthMatcher>().unwrap();
|
||||||
assert_eq!(ancestor_matcher.min_len, 1234321);
|
assert_eq!(ancestor_matcher.min_len, 1234321);
|
||||||
}
|
}
|
||||||
|
|
||||||
// regexesが正しく読み込めることを確認
|
// regexesが正しく読み込めることを確認
|
||||||
{
|
{
|
||||||
let ancestor_node = ancestors[1].as_ref() as &dyn SelectionNode;
|
let ancestor_node = ancestors[1] as &dyn SelectionNode;
|
||||||
assert_eq!(ancestor_node.is::<LeafSelectionNode>(), true);
|
assert!(ancestor_node.is::<LeafSelectionNode>());
|
||||||
let ancestor_node = ancestor_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
let ancestor_node = ancestor_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
||||||
|
|
||||||
let ancestor_node = &ancestor_node.matcher;
|
let ancestor_node = &ancestor_node.matcher;
|
||||||
assert_eq!(ancestor_node.is_some(), true);
|
assert!(ancestor_node.is_some());
|
||||||
let ancestor_matcher = ancestor_node.as_ref().unwrap();
|
let ancestor_matcher = ancestor_node.as_ref().unwrap();
|
||||||
assert_eq!(ancestor_matcher.is::<RegexesFileMatcher>(), true);
|
assert!(ancestor_matcher.is::<RegexesFileMatcher>());
|
||||||
let ancestor_matcher = ancestor_matcher
|
let ancestor_matcher = ancestor_matcher
|
||||||
.downcast_ref::<RegexesFileMatcher>()
|
.downcast_ref::<RegexesFileMatcher>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -657,14 +645,14 @@ mod tests {
|
|||||||
|
|
||||||
// allowlist.txtが読み込めることを確認
|
// allowlist.txtが読み込めることを確認
|
||||||
{
|
{
|
||||||
let ancestor_node = ancestors[2].as_ref() as &dyn SelectionNode;
|
let ancestor_node = ancestors[2] as &dyn SelectionNode;
|
||||||
assert_eq!(ancestor_node.is::<LeafSelectionNode>(), true);
|
assert!(ancestor_node.is::<LeafSelectionNode>());
|
||||||
let ancestor_node = ancestor_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
let ancestor_node = ancestor_node.downcast_ref::<LeafSelectionNode>().unwrap();
|
||||||
|
|
||||||
let ancestor_node = &ancestor_node.matcher;
|
let ancestor_node = &ancestor_node.matcher;
|
||||||
assert_eq!(ancestor_node.is_some(), true);
|
assert!(ancestor_node.is_some());
|
||||||
let ancestor_matcher = ancestor_node.as_ref().unwrap();
|
let ancestor_matcher = ancestor_node.as_ref().unwrap();
|
||||||
assert_eq!(ancestor_matcher.is::<AllowlistFileMatcher>(), true);
|
assert!(ancestor_matcher.is::<AllowlistFileMatcher>());
|
||||||
let ancestor_matcher = ancestor_matcher
|
let ancestor_matcher = ancestor_matcher
|
||||||
.downcast_ref::<AllowlistFileMatcher>()
|
.downcast_ref::<AllowlistFileMatcher>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -707,10 +695,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "failed to parse json record.");
|
panic!("failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -737,10 +725,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -767,10 +755,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "failed to parse json record.");
|
panic!("failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -798,10 +786,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -829,10 +817,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -859,10 +847,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -889,10 +877,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -920,10 +908,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -951,10 +939,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -982,10 +970,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1013,10 +1001,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1044,10 +1032,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1074,10 +1062,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1108,10 +1096,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1142,10 +1130,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1175,10 +1163,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1217,10 +1205,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_rec) => {
|
Err(_rec) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1259,10 +1247,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_rec) => {
|
Err(_rec) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1301,10 +1289,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_rec) => {
|
Err(_rec) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1343,10 +1331,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_rec) => {
|
Err(_rec) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1385,10 +1373,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_rec) => {
|
Err(_rec) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1427,10 +1415,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_rec) => {
|
Err(_rec) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1457,10 +1445,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1487,10 +1475,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1517,10 +1505,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1605,10 +1593,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1635,10 +1623,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1667,10 +1655,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1699,10 +1687,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use self::count::{AggRecordTimeInfo, TimeFrameInfo};
|
|||||||
use super::detection::EvtxRecordInfo;
|
use super::detection::EvtxRecordInfo;
|
||||||
|
|
||||||
pub fn create_rule(rulepath: String, yaml: Yaml) -> RuleNode {
|
pub fn create_rule(rulepath: String, yaml: Yaml) -> RuleNode {
|
||||||
return RuleNode::new(rulepath, yaml);
|
RuleNode::new(rulepath, yaml)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ruleファイルを表すノード
|
/// Ruleファイルを表すノード
|
||||||
@@ -34,7 +34,7 @@ pub struct RuleNode {
|
|||||||
|
|
||||||
impl Debug for RuleNode {
|
impl Debug for RuleNode {
|
||||||
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
return Result::Ok(());
|
Result::Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,13 +42,13 @@ unsafe impl Sync for RuleNode {}
|
|||||||
unsafe impl Send for RuleNode {}
|
unsafe impl Send for RuleNode {}
|
||||||
|
|
||||||
impl RuleNode {
|
impl RuleNode {
|
||||||
pub fn new(rulepath: String, yaml: Yaml) -> RuleNode {
|
pub fn new(rule_path: String, yaml_data: Yaml) -> RuleNode {
|
||||||
return RuleNode {
|
RuleNode {
|
||||||
rulepath: rulepath,
|
rulepath: rule_path,
|
||||||
yaml: yaml,
|
yaml: yaml_data,
|
||||||
detection: DetectionNode::new(),
|
detection: DetectionNode::new(),
|
||||||
countdata: HashMap::new(),
|
countdata: HashMap::new(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(&mut self) -> Result<(), Vec<String>> {
|
pub fn init(&mut self) -> Result<(), Vec<String>> {
|
||||||
@@ -56,14 +56,14 @@ impl RuleNode {
|
|||||||
|
|
||||||
// detection node initialization
|
// detection node initialization
|
||||||
let detection_result = self.detection.init(&self.yaml["detection"]);
|
let detection_result = self.detection.init(&self.yaml["detection"]);
|
||||||
if detection_result.is_err() {
|
if let Err(err_detail) = detection_result {
|
||||||
errmsgs.extend(detection_result.unwrap_err());
|
errmsgs.extend(err_detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
if errmsgs.is_empty() {
|
if errmsgs.is_empty() {
|
||||||
return Result::Ok(());
|
Result::Ok(())
|
||||||
} else {
|
} else {
|
||||||
return Result::Err(errmsgs);
|
Result::Err(errmsgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,11 +72,11 @@ impl RuleNode {
|
|||||||
if result && self.has_agg_condition() {
|
if result && self.has_agg_condition() {
|
||||||
count::count(self, &event_record.record);
|
count::count(self, &event_record.record);
|
||||||
}
|
}
|
||||||
return result;
|
result
|
||||||
}
|
}
|
||||||
/// aggregation conditionが存在するかを返す関数
|
/// aggregation conditionが存在するかを返す関数
|
||||||
pub fn has_agg_condition(&self) -> bool {
|
pub fn has_agg_condition(&self) -> bool {
|
||||||
return self.detection.aggregation_condition.is_some();
|
self.detection.aggregation_condition.is_some()
|
||||||
}
|
}
|
||||||
/// Aggregation Conditionの結果を配列で返却する関数
|
/// Aggregation Conditionの結果を配列で返却する関数
|
||||||
pub fn judge_satisfy_aggcondition(&self) -> Vec<AggResult> {
|
pub fn judge_satisfy_aggcondition(&self) -> Vec<AggResult> {
|
||||||
@@ -84,21 +84,17 @@ impl RuleNode {
|
|||||||
if !self.has_agg_condition() {
|
if !self.has_agg_condition() {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
ret.append(&mut count::aggregation_condition_select(&self));
|
ret.append(&mut count::aggregation_condition_select(self));
|
||||||
return ret;
|
ret
|
||||||
}
|
}
|
||||||
pub fn check_exist_countdata(&self) -> bool {
|
pub fn check_exist_countdata(&self) -> bool {
|
||||||
self.countdata.len() > 0
|
!self.countdata.is_empty()
|
||||||
}
|
}
|
||||||
/// ルール内のAggregationParseInfo(Aggregation Condition)を取得する関数
|
/// ルール内のAggregationParseInfo(Aggregation Condition)を取得する関数
|
||||||
pub fn get_agg_condition(&self) -> Option<&AggregationParseInfo> {
|
pub fn get_agg_condition(&self) -> Option<&AggregationParseInfo> {
|
||||||
match self.detection.aggregation_condition.as_ref() {
|
match self.detection.aggregation_condition.as_ref() {
|
||||||
None => {
|
None => None,
|
||||||
return None;
|
Some(agg_parse_info) => Some(agg_parse_info),
|
||||||
}
|
|
||||||
Some(agg_parse_info) => {
|
|
||||||
return Some(agg_parse_info);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,12 +116,12 @@ pub fn get_detection_keys(node: &RuleNode) -> Vec<String> {
|
|||||||
if key.is_empty() {
|
if key.is_empty() {
|
||||||
return Option::None;
|
return Option::None;
|
||||||
}
|
}
|
||||||
return Option::Some(key.to_string());
|
Option::Some(key.to_string())
|
||||||
});
|
});
|
||||||
ret.extend(keys);
|
ret.extend(keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ruleファイルのdetectionを表すノード
|
/// Ruleファイルのdetectionを表すノード
|
||||||
@@ -138,12 +134,12 @@ struct DetectionNode {
|
|||||||
|
|
||||||
impl DetectionNode {
|
impl DetectionNode {
|
||||||
fn new() -> DetectionNode {
|
fn new() -> DetectionNode {
|
||||||
return DetectionNode {
|
DetectionNode {
|
||||||
name_to_selection: HashMap::new(),
|
name_to_selection: HashMap::new(),
|
||||||
condition: Option::None,
|
condition: Option::None,
|
||||||
aggregation_condition: Option::None,
|
aggregation_condition: Option::None,
|
||||||
timeframe: Option::None,
|
timeframe: Option::None,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self, detection_yaml: &Yaml) -> Result<(), Vec<String>> {
|
fn init(&mut self, detection_yaml: &Yaml) -> Result<(), Vec<String>> {
|
||||||
@@ -169,7 +165,7 @@ impl DetectionNode {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
keys.nth(0).unwrap().to_string()
|
keys.next().unwrap().to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
// conditionをパースして、SelectionNodeに変換する
|
// conditionをパースして、SelectionNodeに変換する
|
||||||
@@ -193,9 +189,9 @@ impl DetectionNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err_msgs.is_empty() {
|
if err_msgs.is_empty() {
|
||||||
return Result::Ok(());
|
Result::Ok(())
|
||||||
} else {
|
} else {
|
||||||
return Result::Err(err_msgs);
|
Result::Err(err_msgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +201,7 @@ impl DetectionNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let condition = &self.condition.as_ref().unwrap();
|
let condition = &self.condition.as_ref().unwrap();
|
||||||
return condition.select(event_record);
|
condition.select(event_record)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// selectionノードをパースします。
|
/// selectionノードをパースします。
|
||||||
@@ -221,7 +217,7 @@ impl DetectionNode {
|
|||||||
let mut err_msgs = vec![];
|
let mut err_msgs = vec![];
|
||||||
for key in keys {
|
for key in keys {
|
||||||
let name = key.as_str().unwrap_or("");
|
let name = key.as_str().unwrap_or("");
|
||||||
if name.len() == 0 {
|
if name.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// condition等、特殊なキーワードを無視する。
|
// condition等、特殊なキーワードを無視する。
|
||||||
@@ -231,11 +227,11 @@ impl DetectionNode {
|
|||||||
|
|
||||||
// パースして、エラーメッセージがあれば配列にためて、戻り値で返す。
|
// パースして、エラーメッセージがあれば配列にためて、戻り値で返す。
|
||||||
let selection_node = self.parse_selection(&detection_hash[key]);
|
let selection_node = self.parse_selection(&detection_hash[key]);
|
||||||
if selection_node.is_some() {
|
if let Some(node) = selection_node {
|
||||||
let mut selection_node = selection_node.unwrap();
|
let mut selection_node = node;
|
||||||
let init_result = selection_node.init();
|
let init_result = selection_node.init();
|
||||||
if init_result.is_err() {
|
if let Err(err_detail) = init_result {
|
||||||
err_msgs.extend(init_result.unwrap_err());
|
err_msgs.extend(err_detail);
|
||||||
} else {
|
} else {
|
||||||
let rc_selection = Arc::new(selection_node);
|
let rc_selection = Arc::new(selection_node);
|
||||||
self.name_to_selection
|
self.name_to_selection
|
||||||
@@ -248,18 +244,18 @@ impl DetectionNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// selectionノードが無いのはエラー
|
// selectionノードが無いのはエラー
|
||||||
if self.name_to_selection.len() == 0 {
|
if self.name_to_selection.is_empty() {
|
||||||
return Result::Err(vec![
|
return Result::Err(vec![
|
||||||
"There is no selection node under detection.".to_string()
|
"There is no selection node under detection.".to_string()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result::Ok(());
|
Result::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// selectionをパースします。
|
/// selectionをパースします。
|
||||||
fn parse_selection(&self, selection_yaml: &Yaml) -> Option<Box<dyn SelectionNode>> {
|
fn parse_selection(&self, selection_yaml: &Yaml) -> Option<Box<dyn SelectionNode>> {
|
||||||
return Option::Some(self.parse_selection_recursively(vec![], selection_yaml));
|
Option::Some(self.parse_selection_recursively(vec![], selection_yaml))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// selectionをパースします。
|
/// selectionをパースします。
|
||||||
@@ -280,7 +276,7 @@ impl DetectionNode {
|
|||||||
let child_node = self.parse_selection_recursively(child_key_list, child_yaml);
|
let child_node = self.parse_selection_recursively(child_key_list, child_yaml);
|
||||||
and_node.child_nodes.push(child_node);
|
and_node.child_nodes.push(child_node);
|
||||||
});
|
});
|
||||||
return Box::new(and_node);
|
Box::new(and_node)
|
||||||
} else if yaml.as_vec().is_some() {
|
} else if yaml.as_vec().is_some() {
|
||||||
// 配列はOR条件と解釈する。
|
// 配列はOR条件と解釈する。
|
||||||
let mut or_node = selectionnodes::OrSelectionNode::new();
|
let mut or_node = selectionnodes::OrSelectionNode::new();
|
||||||
@@ -289,13 +285,13 @@ impl DetectionNode {
|
|||||||
or_node.child_nodes.push(child_node);
|
or_node.child_nodes.push(child_node);
|
||||||
});
|
});
|
||||||
|
|
||||||
return Box::new(or_node);
|
Box::new(or_node)
|
||||||
} else {
|
} else {
|
||||||
// 連想配列と配列以外は末端ノード
|
// 連想配列と配列以外は末端ノード
|
||||||
return Box::new(selectionnodes::LeafSelectionNode::new(
|
Box::new(selectionnodes::LeafSelectionNode::new(
|
||||||
key_list,
|
key_list,
|
||||||
yaml.clone(),
|
yaml.clone(),
|
||||||
));
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,19 +313,19 @@ pub struct AggResult {
|
|||||||
|
|
||||||
impl AggResult {
|
impl AggResult {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
data: i64,
|
count_data: i64,
|
||||||
key: String,
|
key_name: String,
|
||||||
field_values: Vec<String>,
|
field_value: Vec<String>,
|
||||||
start_timedate: DateTime<Utc>,
|
event_start_timedate: DateTime<Utc>,
|
||||||
condition_op_num: String,
|
condition_op_number: String,
|
||||||
) -> AggResult {
|
) -> AggResult {
|
||||||
return AggResult {
|
AggResult {
|
||||||
data: data,
|
data: count_data,
|
||||||
key: key,
|
key: key_name,
|
||||||
field_values: field_values,
|
field_values: field_value,
|
||||||
start_timedate: start_timedate,
|
start_timedate: event_start_timedate,
|
||||||
condition_op_num: condition_op_num,
|
condition_op_num: condition_op_number,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,12 +337,12 @@ mod tests {
|
|||||||
|
|
||||||
pub fn parse_rule_from_str(rule_str: &str) -> RuleNode {
|
pub fn parse_rule_from_str(rule_str: &str) -> RuleNode {
|
||||||
let rule_yaml = YamlLoader::load_from_str(rule_str);
|
let rule_yaml = YamlLoader::load_from_str(rule_str);
|
||||||
assert_eq!(rule_yaml.is_ok(), true);
|
assert!(rule_yaml.is_ok());
|
||||||
let rule_yamls = rule_yaml.unwrap();
|
let rule_yamls = rule_yaml.unwrap();
|
||||||
let mut rule_yaml = rule_yamls.into_iter();
|
let mut rule_yaml = rule_yamls.into_iter();
|
||||||
let mut rule_node = create_rule("testpath".to_string(), rule_yaml.next().unwrap());
|
let mut rule_node = create_rule("testpath".to_string(), rule_yaml.next().unwrap());
|
||||||
assert_eq!(rule_node.init().is_ok(), true);
|
assert!(rule_node.init().is_ok());
|
||||||
return rule_node;
|
rule_node
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -371,10 +367,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -401,10 +397,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -431,10 +427,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -514,10 +510,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -573,10 +569,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -639,10 +635,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -683,10 +679,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -728,10 +724,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -792,10 +788,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -856,10 +852,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -902,10 +898,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_rec) => {
|
Err(_rec) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -961,15 +957,15 @@ mod tests {
|
|||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
let result = rule_node.select(&recinfo);
|
let result = rule_node.select(&recinfo);
|
||||||
assert_eq!(rule_node.detection.aggregation_condition.is_some(), true);
|
assert!(rule_node.detection.aggregation_condition.is_some());
|
||||||
assert_eq!(result, true);
|
assert!(result);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*&rule_node.countdata.get(key).unwrap().len() as i32,
|
rule_node.countdata.get(key).unwrap().len() as i32,
|
||||||
expect_count
|
expect_count
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(_rec) => {
|
Err(_rec) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ pub trait SelectionNode: mopa::Any {
|
|||||||
fn init(&mut self) -> Result<(), Vec<String>>;
|
fn init(&mut self) -> Result<(), Vec<String>>;
|
||||||
|
|
||||||
// 子ノードを取得する(グラフ理論のchildと同じ意味)
|
// 子ノードを取得する(グラフ理論のchildと同じ意味)
|
||||||
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>>;
|
fn get_childs(&self) -> Vec<&dyn SelectionNode>;
|
||||||
|
|
||||||
// 子孫ノードを取得する(グラフ理論のdescendantと同じ意味)
|
// 子孫ノードを取得する(グラフ理論のdescendantと同じ意味)
|
||||||
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>>;
|
fn get_descendants(&self) -> Vec<&dyn SelectionNode>;
|
||||||
}
|
}
|
||||||
mopafy!(SelectionNode);
|
mopafy!(SelectionNode);
|
||||||
|
|
||||||
@@ -33,17 +33,17 @@ pub struct AndSelectionNode {
|
|||||||
|
|
||||||
impl AndSelectionNode {
|
impl AndSelectionNode {
|
||||||
pub fn new() -> AndSelectionNode {
|
pub fn new() -> AndSelectionNode {
|
||||||
return AndSelectionNode {
|
AndSelectionNode {
|
||||||
child_nodes: vec![],
|
child_nodes: vec![],
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectionNode for AndSelectionNode {
|
impl SelectionNode for AndSelectionNode {
|
||||||
fn select(&self, event_record: &EvtxRecordInfo) -> bool {
|
fn select(&self, event_record: &EvtxRecordInfo) -> bool {
|
||||||
return self.child_nodes.iter().all(|child_node| {
|
self.child_nodes
|
||||||
return child_node.select(event_record);
|
.iter()
|
||||||
});
|
.all(|child_node| child_node.select(event_record))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self) -> Result<(), Vec<String>> {
|
fn init(&mut self) -> Result<(), Vec<String>> {
|
||||||
@@ -52,50 +52,48 @@ impl SelectionNode for AndSelectionNode {
|
|||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|child_node| {
|
.map(|child_node| {
|
||||||
let res = child_node.init();
|
let res = child_node.init();
|
||||||
if res.is_err() {
|
if let Err(err) = res {
|
||||||
return res.unwrap_err();
|
err
|
||||||
} else {
|
} else {
|
||||||
return vec![];
|
vec![]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fold(
|
.fold(
|
||||||
vec![],
|
vec![],
|
||||||
|mut acc: Vec<String>, cur: Vec<String>| -> Vec<String> {
|
|mut acc: Vec<String>, cur: Vec<String>| -> Vec<String> {
|
||||||
acc.extend(cur.into_iter());
|
acc.extend(cur.into_iter());
|
||||||
return acc;
|
acc
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if err_msgs.is_empty() {
|
if err_msgs.is_empty() {
|
||||||
return Result::Ok(());
|
Result::Ok(())
|
||||||
} else {
|
} else {
|
||||||
return Result::Err(err_msgs);
|
Result::Err(err_msgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>> {
|
fn get_childs(&self) -> Vec<&dyn SelectionNode> {
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
self.child_nodes.iter().for_each(|child_node| {
|
self.child_nodes.iter().for_each(|child_node| {
|
||||||
ret.push(child_node);
|
ret.push(child_node.as_ref());
|
||||||
});
|
});
|
||||||
|
|
||||||
return ret;
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>> {
|
fn get_descendants(&self) -> Vec<&dyn SelectionNode> {
|
||||||
let mut ret = self.get_childs();
|
let mut ret = self.get_childs();
|
||||||
|
|
||||||
self.child_nodes
|
self.child_nodes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|child_node| {
|
.map(|child_node| child_node.get_descendants())
|
||||||
return child_node.get_descendants();
|
|
||||||
})
|
|
||||||
.flatten()
|
.flatten()
|
||||||
.for_each(|descendant_node| {
|
.for_each(|descendant_node| {
|
||||||
ret.push(descendant_node);
|
ret.push(descendant_node);
|
||||||
});
|
});
|
||||||
|
|
||||||
return ret;
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,17 +104,17 @@ pub struct OrSelectionNode {
|
|||||||
|
|
||||||
impl OrSelectionNode {
|
impl OrSelectionNode {
|
||||||
pub fn new() -> OrSelectionNode {
|
pub fn new() -> OrSelectionNode {
|
||||||
return OrSelectionNode {
|
OrSelectionNode {
|
||||||
child_nodes: vec![],
|
child_nodes: vec![],
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectionNode for OrSelectionNode {
|
impl SelectionNode for OrSelectionNode {
|
||||||
fn select(&self, event_record: &EvtxRecordInfo) -> bool {
|
fn select(&self, event_record: &EvtxRecordInfo) -> bool {
|
||||||
return self.child_nodes.iter().any(|child_node| {
|
self.child_nodes
|
||||||
return child_node.select(event_record);
|
.iter()
|
||||||
});
|
.any(|child_node| child_node.select(event_record))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self) -> Result<(), Vec<String>> {
|
fn init(&mut self) -> Result<(), Vec<String>> {
|
||||||
@@ -125,50 +123,48 @@ impl SelectionNode for OrSelectionNode {
|
|||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|child_node| {
|
.map(|child_node| {
|
||||||
let res = child_node.init();
|
let res = child_node.init();
|
||||||
if res.is_err() {
|
if let Err(err) = res {
|
||||||
return res.unwrap_err();
|
err
|
||||||
} else {
|
} else {
|
||||||
return vec![];
|
vec![]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fold(
|
.fold(
|
||||||
vec![],
|
vec![],
|
||||||
|mut acc: Vec<String>, cur: Vec<String>| -> Vec<String> {
|
|mut acc: Vec<String>, cur: Vec<String>| -> Vec<String> {
|
||||||
acc.extend(cur.into_iter());
|
acc.extend(cur.into_iter());
|
||||||
return acc;
|
acc
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if err_msgs.is_empty() {
|
if err_msgs.is_empty() {
|
||||||
return Result::Ok(());
|
Result::Ok(())
|
||||||
} else {
|
} else {
|
||||||
return Result::Err(err_msgs);
|
Result::Err(err_msgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>> {
|
fn get_childs(&self) -> Vec<&dyn SelectionNode> {
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
self.child_nodes.iter().for_each(|child_node| {
|
self.child_nodes.iter().for_each(|child_node| {
|
||||||
ret.push(child_node);
|
ret.push(child_node.as_ref());
|
||||||
});
|
});
|
||||||
|
|
||||||
return ret;
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>> {
|
fn get_descendants(&self) -> Vec<&dyn SelectionNode> {
|
||||||
let mut ret = self.get_childs();
|
let mut ret = self.get_childs();
|
||||||
|
|
||||||
self.child_nodes
|
self.child_nodes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|child_node| {
|
.map(|child_node| child_node.get_descendants())
|
||||||
return child_node.get_descendants();
|
|
||||||
})
|
|
||||||
.flatten()
|
.flatten()
|
||||||
.for_each(|descendant_node| {
|
.for_each(|descendant_node| {
|
||||||
ret.push(descendant_node);
|
ret.push(descendant_node);
|
||||||
});
|
});
|
||||||
|
|
||||||
return ret;
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,26 +174,26 @@ pub struct NotSelectionNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NotSelectionNode {
|
impl NotSelectionNode {
|
||||||
pub fn new(node: Box<dyn SelectionNode>) -> NotSelectionNode {
|
pub fn new(select_node: Box<dyn SelectionNode>) -> NotSelectionNode {
|
||||||
return NotSelectionNode { node: node };
|
NotSelectionNode { node: select_node }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectionNode for NotSelectionNode {
|
impl SelectionNode for NotSelectionNode {
|
||||||
fn select(&self, event_record: &EvtxRecordInfo) -> bool {
|
fn select(&self, event_record: &EvtxRecordInfo) -> bool {
|
||||||
return !self.node.select(event_record);
|
!self.node.select(event_record)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self) -> Result<(), Vec<String>> {
|
fn init(&mut self) -> Result<(), Vec<String>> {
|
||||||
return Result::Ok(());
|
Result::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>> {
|
fn get_childs(&self) -> Vec<&dyn SelectionNode> {
|
||||||
return vec![];
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>> {
|
fn get_descendants(&self) -> Vec<&dyn SelectionNode> {
|
||||||
return self.get_childs();
|
self.get_childs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,28 +206,28 @@ pub struct RefSelectionNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RefSelectionNode {
|
impl RefSelectionNode {
|
||||||
pub fn new(selection_node: Arc<Box<dyn SelectionNode>>) -> RefSelectionNode {
|
pub fn new(select_node: Arc<Box<dyn SelectionNode>>) -> RefSelectionNode {
|
||||||
return RefSelectionNode {
|
RefSelectionNode {
|
||||||
selection_node: selection_node,
|
selection_node: select_node,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectionNode for RefSelectionNode {
|
impl SelectionNode for RefSelectionNode {
|
||||||
fn select(&self, event_record: &EvtxRecordInfo) -> bool {
|
fn select(&self, event_record: &EvtxRecordInfo) -> bool {
|
||||||
return self.selection_node.select(event_record);
|
self.selection_node.select(event_record)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self) -> Result<(), Vec<String>> {
|
fn init(&mut self) -> Result<(), Vec<String>> {
|
||||||
return Result::Ok(());
|
Result::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>> {
|
fn get_childs(&self) -> Vec<&dyn SelectionNode> {
|
||||||
return vec![&self.selection_node];
|
vec![self.selection_node.as_ref().as_ref()]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>> {
|
fn get_descendants(&self) -> Vec<&dyn SelectionNode> {
|
||||||
return self.get_childs();
|
self.get_childs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,17 +240,17 @@ pub struct LeafSelectionNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LeafSelectionNode {
|
impl LeafSelectionNode {
|
||||||
pub fn new(key_list: Vec<String>, value_yaml: Yaml) -> LeafSelectionNode {
|
pub fn new(keys: Vec<String>, value_yaml: Yaml) -> LeafSelectionNode {
|
||||||
return LeafSelectionNode {
|
LeafSelectionNode {
|
||||||
key: String::default(),
|
key: String::default(),
|
||||||
key_list: key_list,
|
key_list: keys,
|
||||||
select_value: value_yaml,
|
select_value: value_yaml,
|
||||||
matcher: Option::None,
|
matcher: Option::None,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_key(&self) -> &String {
|
pub fn get_key(&self) -> &String {
|
||||||
return &self.key;
|
&self.key
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _create_key(&self) -> String {
|
fn _create_key(&self) -> String {
|
||||||
@@ -263,8 +259,8 @@ impl LeafSelectionNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let topkey = self.key_list[0].to_string();
|
let topkey = self.key_list[0].to_string();
|
||||||
let values: Vec<&str> = topkey.split("|").collect();
|
let values: Vec<&str> = topkey.split('|').collect();
|
||||||
return values[0].to_string();
|
values[0].to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// JSON形式のEventJSONから値を取得する関数 aliasも考慮されている。
|
/// JSON形式のEventJSONから値を取得する関数 aliasも考慮されている。
|
||||||
@@ -274,18 +270,18 @@ impl LeafSelectionNode {
|
|||||||
return Option::Some(&record.data_string);
|
return Option::Some(&record.data_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
return record.get_value(self.get_key());
|
record.get_value(self.get_key())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// matchers::LeafMatcherの一覧を取得する。
|
/// matchers::LeafMatcherの一覧を取得する。
|
||||||
/// 上から順番に調べて、一番始めに一致したMatcherが適用される
|
/// 上から順番に調べて、一番始めに一致したMatcherが適用される
|
||||||
fn get_matchers(&self) -> Vec<Box<dyn matchers::LeafMatcher>> {
|
fn get_matchers(&self) -> Vec<Box<dyn matchers::LeafMatcher>> {
|
||||||
return vec![
|
vec![
|
||||||
Box::new(matchers::MinlengthMatcher::new()),
|
Box::new(matchers::MinlengthMatcher::new()),
|
||||||
Box::new(matchers::RegexesFileMatcher::new()),
|
Box::new(matchers::RegexesFileMatcher::new()),
|
||||||
Box::new(matchers::AllowlistFileMatcher::new()),
|
Box::new(matchers::AllowlistFileMatcher::new()),
|
||||||
Box::new(matchers::DefaultMatcher::new()),
|
Box::new(matchers::DefaultMatcher::new()),
|
||||||
];
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,7 +366,7 @@ impl SelectionNode for LeafSelectionNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let replaced_str =
|
let replaced_str =
|
||||||
utils::replace_target_character(self.get_event_value(&event_record), filter_rule);
|
utils::replace_target_character(self.get_event_value(event_record), filter_rule);
|
||||||
|
|
||||||
return self
|
return self
|
||||||
.matcher
|
.matcher
|
||||||
@@ -409,12 +405,12 @@ impl SelectionNode for LeafSelectionNode {
|
|||||||
.init(&match_key_list, &self.select_value);
|
.init(&match_key_list, &self.select_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_childs(&self) -> Vec<&Box<dyn SelectionNode>> {
|
fn get_childs(&self) -> Vec<&dyn SelectionNode> {
|
||||||
return vec![];
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_descendants(&self) -> Vec<&Box<dyn SelectionNode>> {
|
fn get_descendants(&self) -> Vec<&dyn SelectionNode> {
|
||||||
return vec![];
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,10 +441,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -478,10 +474,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -510,10 +506,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -542,10 +538,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), true);
|
assert!(rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -574,10 +570,10 @@ mod tests {
|
|||||||
Ok(record) => {
|
Ok(record) => {
|
||||||
let keys = detections::rule::get_detection_keys(&rule_node);
|
let keys = detections::rule::get_detection_keys(&rule_node);
|
||||||
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
let recinfo = utils::create_rec_info(record, "testpath".to_owned(), &keys);
|
||||||
assert_eq!(rule_node.select(&recinfo), false);
|
assert!(!rule_node.select(&recinfo));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
assert!(false, "Failed to parse json record.");
|
panic!("Failed to parse json record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,25 +19,25 @@ use std::string::String;
|
|||||||
|
|
||||||
use super::detection::EvtxRecordInfo;
|
use super::detection::EvtxRecordInfo;
|
||||||
|
|
||||||
pub fn concat_selection_key(key_list: &Vec<String>) -> String {
|
pub fn concat_selection_key(key_list: &[String]) -> String {
|
||||||
return key_list
|
return key_list
|
||||||
.iter()
|
.iter()
|
||||||
.fold("detection -> selection".to_string(), |mut acc, cur| {
|
.fold("detection -> selection".to_string(), |mut acc, cur| {
|
||||||
acc = acc + " -> " + cur;
|
acc = acc + " -> " + cur;
|
||||||
return acc;
|
acc
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_regex(string: &str, regex_list: &Vec<Regex>) -> bool {
|
pub fn check_regex(string: &str, regex_list: &[Regex]) -> bool {
|
||||||
for regex in regex_list {
|
for regex in regex_list {
|
||||||
if regex.is_match(string) == false {
|
if !regex.is_match(string) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// replace string from all defined regex in input to replace_str
|
/// replace string from all defined regex in input to replace_str
|
||||||
@@ -45,9 +45,7 @@ pub fn replace_target_character<'a>(
|
|||||||
input_str: Option<&'a String>,
|
input_str: Option<&'a String>,
|
||||||
replace_rule: Option<&'a DataFilterRule>,
|
replace_rule: Option<&'a DataFilterRule>,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
if input_str.is_none() {
|
input_str?;
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if replace_rule.is_none() {
|
if replace_rule.is_none() {
|
||||||
return Some(input_str.unwrap().to_string());
|
return Some(input_str.unwrap().to_string());
|
||||||
}
|
}
|
||||||
@@ -55,32 +53,32 @@ pub fn replace_target_character<'a>(
|
|||||||
let replace_regex_rule = &replace_rule.unwrap().regex_rule;
|
let replace_regex_rule = &replace_rule.unwrap().regex_rule;
|
||||||
let replace_str = &replace_rule.unwrap().replace_str;
|
let replace_str = &replace_rule.unwrap().replace_str;
|
||||||
|
|
||||||
return Some(
|
Some(
|
||||||
replace_regex_rule
|
replace_regex_rule
|
||||||
.replace_all(input_str.unwrap(), replace_str)
|
.replace_all(input_str.unwrap(), replace_str)
|
||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_allowlist(target: &str, regexes: &Vec<Regex>) -> bool {
|
pub fn check_allowlist(target: &str, regexes: &[Regex]) -> bool {
|
||||||
for regex in regexes {
|
for regex in regexes {
|
||||||
if regex.is_match(target) {
|
if regex.is_match(target) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value_to_string(value: &Value) -> Option<String> {
|
pub fn value_to_string(value: &Value) -> Option<String> {
|
||||||
return match value {
|
match value {
|
||||||
Value::Null => Option::None,
|
Value::Null => Option::None,
|
||||||
Value::Bool(b) => Option::Some(b.to_string()),
|
Value::Bool(b) => Option::Some(b.to_string()),
|
||||||
Value::Number(n) => Option::Some(n.to_string()),
|
Value::Number(n) => Option::Some(n.to_string()),
|
||||||
Value::String(s) => Option::Some(s.to_string()),
|
Value::String(s) => Option::Some(s.to_string()),
|
||||||
Value::Array(_) => Option::None,
|
Value::Array(_) => Option::None,
|
||||||
Value::Object(_) => Option::None,
|
Value::Object(_) => Option::None,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_txt(filename: &str) -> Result<Vec<String>, String> {
|
pub fn read_txt(filename: &str) -> Result<Vec<String>, String> {
|
||||||
@@ -90,12 +88,12 @@ pub fn read_txt(filename: &str) -> Result<Vec<String>, String> {
|
|||||||
return Result::Err(errmsg);
|
return Result::Err(errmsg);
|
||||||
}
|
}
|
||||||
let reader = BufReader::new(f.unwrap());
|
let reader = BufReader::new(f.unwrap());
|
||||||
return Result::Ok(
|
Result::Ok(
|
||||||
reader
|
reader
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.unwrap_or(String::default()))
|
.map(|line| line.unwrap_or_default())
|
||||||
.collect(),
|
.collect(),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_csv(filename: &str) -> Result<Vec<Vec<String>>, String> {
|
pub fn read_csv(filename: &str) -> Result<Vec<Vec<String>>, String> {
|
||||||
@@ -106,8 +104,8 @@ pub fn read_csv(filename: &str) -> Result<Vec<Vec<String>>, String> {
|
|||||||
let mut contents: String = String::new();
|
let mut contents: String = String::new();
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
let read_res = f.unwrap().read_to_string(&mut contents);
|
let read_res = f.unwrap().read_to_string(&mut contents);
|
||||||
if read_res.is_err() {
|
if let Err(e) = read_res {
|
||||||
return Result::Err(read_res.unwrap_err().to_string());
|
return Result::Err(e.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut rdr = csv::Reader::from_reader(contents.as_bytes());
|
let mut rdr = csv::Reader::from_reader(contents.as_bytes());
|
||||||
@@ -122,19 +120,19 @@ pub fn read_csv(filename: &str) -> Result<Vec<Vec<String>>, String> {
|
|||||||
ret.push(v);
|
ret.push(v);
|
||||||
});
|
});
|
||||||
|
|
||||||
return Result::Ok(ret);
|
Result::Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_target_event_id(s: &String) -> bool {
|
pub fn is_target_event_id(s: &str) -> bool {
|
||||||
return configs::CONFIG.read().unwrap().target_eventids.is_target(s);
|
configs::CONFIG.read().unwrap().target_eventids.is_target(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_event_id_key() -> String {
|
pub fn get_event_id_key() -> String {
|
||||||
return "Event.System.EventID".to_string();
|
"Event.System.EventID".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_event_time() -> String {
|
pub fn get_event_time() -> String {
|
||||||
return "Event.System.TimeCreated_attributes.SystemTime".to_string();
|
"Event.System.TimeCreated_attributes.SystemTime".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn str_time_to_datetime(system_time_str: &str) -> Option<DateTime<Utc>> {
|
pub fn str_time_to_datetime(system_time_str: &str) -> Option<DateTime<Utc>> {
|
||||||
@@ -146,30 +144,24 @@ pub fn str_time_to_datetime(system_time_str: &str) -> Option<DateTime<Utc>> {
|
|||||||
if rfc3339_time.is_err() {
|
if rfc3339_time.is_err() {
|
||||||
return Option::None;
|
return Option::None;
|
||||||
}
|
}
|
||||||
let datetime = Utc
|
Utc.from_local_datetime(&rfc3339_time.unwrap().naive_utc())
|
||||||
.from_local_datetime(&rfc3339_time.unwrap().naive_utc())
|
.single()
|
||||||
.single();
|
|
||||||
if datetime.is_none() {
|
|
||||||
return Option::None;
|
|
||||||
} else {
|
|
||||||
return Option::Some(datetime.unwrap());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// serde:Valueの型を確認し、文字列を返します。
|
/// serde:Valueの型を確認し、文字列を返します。
|
||||||
pub fn get_serde_number_to_string(value: &serde_json::Value) -> Option<String> {
|
pub fn get_serde_number_to_string(value: &serde_json::Value) -> Option<String> {
|
||||||
if value.is_string() {
|
if value.is_string() {
|
||||||
return Option::Some(value.as_str().unwrap_or("").to_string());
|
Option::Some(value.as_str().unwrap_or("").to_string())
|
||||||
} else if value.is_object() {
|
} else if value.is_object() {
|
||||||
// Object type is not specified record value.
|
// Object type is not specified record value.
|
||||||
return Option::None;
|
Option::None
|
||||||
} else {
|
} else {
|
||||||
return Option::Some(value.to_string());
|
Option::Some(value.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_event_value<'a>(key: &String, event_value: &'a Value) -> Option<&'a Value> {
|
pub fn get_event_value<'a>(key: &str, event_value: &'a Value) -> Option<&'a Value> {
|
||||||
if key.len() == 0 {
|
if key.is_empty() {
|
||||||
return Option::None;
|
return Option::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,28 +172,28 @@ pub fn get_event_value<'a>(key: &String, event_value: &'a Value) -> Option<&'a V
|
|||||||
let splits = configs::EVENTKEY_ALIAS.get_event_key_split(key);
|
let splits = configs::EVENTKEY_ALIAS.get_event_key_split(key);
|
||||||
let mut start_idx = 0;
|
let mut start_idx = 0;
|
||||||
for key in splits.unwrap() {
|
for key in splits.unwrap() {
|
||||||
if ret.is_object() == false {
|
if !ret.is_object() {
|
||||||
return Option::None;
|
return Option::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let val = &event_key[start_idx..(*key + start_idx)];
|
let val = &event_key[start_idx..(*key + start_idx)];
|
||||||
ret = &ret[val];
|
ret = &ret[val];
|
||||||
start_idx = *key + start_idx;
|
start_idx += *key;
|
||||||
start_idx += 1;
|
start_idx += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Option::Some(ret);
|
Option::Some(ret)
|
||||||
} else {
|
} else {
|
||||||
let mut ret: &Value = event_value;
|
let mut ret: &Value = event_value;
|
||||||
let event_key = key;
|
let event_key = key;
|
||||||
for key in event_key.split(".") {
|
for key in event_key.split('.') {
|
||||||
if ret.is_object() == false {
|
if !ret.is_object() {
|
||||||
return Option::None;
|
return Option::None;
|
||||||
}
|
}
|
||||||
ret = &ret[key];
|
ret = &ret[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
return Option::Some(ret);
|
Option::Some(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,20 +203,20 @@ pub fn get_thread_num() -> usize {
|
|||||||
let threadnum = &conf
|
let threadnum = &conf
|
||||||
.args
|
.args
|
||||||
.value_of("thread-number")
|
.value_of("thread-number")
|
||||||
.unwrap_or(def_thread_num_str.as_str());
|
.unwrap_or_else(|| def_thread_num_str.as_str());
|
||||||
return threadnum.parse::<usize>().unwrap().clone();
|
threadnum.parse::<usize>().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_tokio_runtime() -> Runtime {
|
pub fn create_tokio_runtime() -> Runtime {
|
||||||
return Builder::new_multi_thread()
|
Builder::new_multi_thread()
|
||||||
.worker_threads(get_thread_num())
|
.worker_threads(get_thread_num())
|
||||||
.thread_name("yea-thread")
|
.thread_name("yea-thread")
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
// EvtxRecordInfoを作成します。
|
// EvtxRecordInfoを作成します。
|
||||||
pub fn create_rec_info(data: Value, path: String, keys: &Vec<String>) -> EvtxRecordInfo {
|
pub fn create_rec_info(data: Value, path: String, keys: &[String]) -> EvtxRecordInfo {
|
||||||
// EvtxRecordInfoを作る
|
// EvtxRecordInfoを作る
|
||||||
let data_str = data.to_string();
|
let data_str = data.to_string();
|
||||||
let mut rec = EvtxRecordInfo {
|
let mut rec = EvtxRecordInfo {
|
||||||
@@ -255,7 +247,7 @@ pub fn create_rec_info(data: Value, path: String, keys: &Vec<String>) -> EvtxRec
|
|||||||
rec.key_2_value.insert(key.to_string(), val.unwrap());
|
rec.key_2_value.insert(key.to_string(), val.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
return rec;
|
rec
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -267,30 +259,32 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_regex() {
|
fn test_check_regex() {
|
||||||
let regexes = utils::read_txt("./rules/config/regex/detectlist_suspicous_services.txt")
|
let regexes: Vec<Regex> =
|
||||||
.unwrap()
|
utils::read_txt("./rules/config/regex/detectlist_suspicous_services.txt")
|
||||||
.into_iter()
|
.unwrap()
|
||||||
.map(|regex_str| Regex::new(®ex_str).unwrap())
|
.into_iter()
|
||||||
.collect();
|
.map(|regex_str| Regex::new(®ex_str).unwrap())
|
||||||
|
.collect();
|
||||||
let regextext = utils::check_regex("\\cvtres.exe", ®exes);
|
let regextext = utils::check_regex("\\cvtres.exe", ®exes);
|
||||||
assert!(regextext == true);
|
assert!(regextext);
|
||||||
|
|
||||||
let regextext = utils::check_regex("\\hogehoge.exe", ®exes);
|
let regextext = utils::check_regex("\\hogehoge.exe", ®exes);
|
||||||
assert!(regextext == false);
|
assert!(!regextext);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_allowlist() {
|
fn test_check_allowlist() {
|
||||||
let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate.exe\"";
|
let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate.exe\"";
|
||||||
let allowlist = utils::read_txt("./rules/config/regex/allowlist_legitimate_services.txt")
|
let allowlist: Vec<Regex> =
|
||||||
.unwrap()
|
utils::read_txt("./rules/config/regex/allowlist_legitimate_services.txt")
|
||||||
.into_iter()
|
.unwrap()
|
||||||
.map(|allow_str| Regex::new(&allow_str).unwrap())
|
.into_iter()
|
||||||
.collect();
|
.map(|allow_str| Regex::new(&allow_str).unwrap())
|
||||||
assert!(true == utils::check_allowlist(commandline, &allowlist));
|
.collect();
|
||||||
|
assert!(utils::check_allowlist(commandline, &allowlist));
|
||||||
|
|
||||||
let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate2.exe\"";
|
let commandline = "\"C:\\Program Files\\Google\\Update\\GoogleUpdate2.exe\"";
|
||||||
assert!(false == utils::check_allowlist(commandline, &allowlist));
|
assert!(!utils::check_allowlist(commandline, &allowlist));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -360,15 +354,9 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let none_test_str: Option<&String> = None;
|
let none_test_str: Option<&String> = None;
|
||||||
|
|
||||||
assert_eq!(
|
assert!(utils::replace_target_character(none_test_str, None).is_none());
|
||||||
utils::replace_target_character(none_test_str, None).is_none(),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
assert!(utils::replace_target_character(none_test_str, Some(&test_filter_rule)).is_none());
|
||||||
utils::replace_target_character(none_test_str, Some(&test_filter_rule)).is_none(),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
let tmp = "h\ra\ny\ta\tb\nu\r\nsa".to_string();
|
let tmp = "h\ra\ny\ta\tb\nu\r\nsa".to_string();
|
||||||
let test_str: Option<&String> = Some(&tmp);
|
let test_str: Option<&String> = Some(&tmp);
|
||||||
|
|||||||
@@ -52,17 +52,16 @@ fn load_record_filters() -> HashMap<String, DataFilterRule> {
|
|||||||
let key = line.get(0).unwrap_or(empty).trim();
|
let key = line.get(0).unwrap_or(empty).trim();
|
||||||
let regex_str = line.get(1).unwrap_or(empty).trim();
|
let regex_str = line.get(1).unwrap_or(empty).trim();
|
||||||
let replaced_str = line.get(2).unwrap_or(empty).trim();
|
let replaced_str = line.get(2).unwrap_or(empty).trim();
|
||||||
if key.len() == 0 || regex_str.len() == 0 {
|
if key.is_empty() || regex_str.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let regex_rule: Option<Regex> = match Regex::new(regex_str) {
|
let regex_rule: Option<Regex> = match Regex::new(regex_str) {
|
||||||
Ok(regex) => Some(regex),
|
Ok(regex) => Some(regex),
|
||||||
Err(_err) => {
|
Err(_err) => {
|
||||||
let errmsg = format!("failed to read regex filter in record_data_filter.txt");
|
let errmsg = "failed to read regex filter in record_data_filter.txt";
|
||||||
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
if configs::CONFIG.read().unwrap().args.is_present("verbose") {
|
||||||
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), &errmsg)
|
AlertMessage::alert(&mut BufWriter::new(std::io::stderr().lock()), errmsg).ok();
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
if !*QUIET_ERRORS_FLAG {
|
if !*QUIET_ERRORS_FLAG {
|
||||||
ERROR_LOG_STACK
|
ERROR_LOG_STACK
|
||||||
@@ -85,7 +84,7 @@ fn load_record_filters() -> HashMap<String, DataFilterRule> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return ret;
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -109,7 +108,7 @@ pub fn exclude_ids() -> RuleExclude {
|
|||||||
|
|
||||||
exclude_ids.insert_ids("./rules/config/exclude_rules.txt");
|
exclude_ids.insert_ids("./rules/config/exclude_rules.txt");
|
||||||
|
|
||||||
return exclude_ids;
|
exclude_ids
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RuleExclude {
|
impl RuleExclude {
|
||||||
@@ -129,11 +128,10 @@ impl RuleExclude {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.push(format!("{} does not exist", filename));
|
.push(format!("{} does not exist", filename));
|
||||||
}
|
}
|
||||||
return ();
|
|
||||||
}
|
}
|
||||||
let reader = BufReader::new(f.unwrap());
|
let reader = BufReader::new(f.unwrap());
|
||||||
for v in reader.lines() {
|
for v in reader.lines() {
|
||||||
let v = v.unwrap().split("#").collect::<Vec<&str>>()[0]
|
let v = v.unwrap().split('#').collect::<Vec<&str>>()[0]
|
||||||
.trim()
|
.trim()
|
||||||
.to_string();
|
.to_string();
|
||||||
if v.is_empty() || !IDS_REGEX.is_match(&v) {
|
if v.is_empty() || !IDS_REGEX.is_match(&v) {
|
||||||
|
|||||||
98
src/main.rs
98
src/main.rs
@@ -16,7 +16,7 @@ use hayabusa::detections::rule::{get_detection_keys, RuleNode};
|
|||||||
use hayabusa::filter;
|
use hayabusa::filter;
|
||||||
use hayabusa::omikuji::Omikuji;
|
use hayabusa::omikuji::Omikuji;
|
||||||
use hayabusa::{afterfact::after_fact, detections::utils};
|
use hayabusa::{afterfact::after_fact, detections::utils};
|
||||||
use hayabusa::{detections::configs, timeline::timeline::Timeline};
|
use hayabusa::{detections::configs, timeline::timelines::Timeline};
|
||||||
use hhmmss::Hhmmss;
|
use hhmmss::Hhmmss;
|
||||||
use pbr::ProgressBar;
|
use pbr::ProgressBar;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@@ -53,19 +53,25 @@ pub struct App {
|
|||||||
rule_keys: Vec<String>,
|
rule_keys: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for App {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new() -> App {
|
pub fn new() -> App {
|
||||||
return App {
|
App {
|
||||||
rt: utils::create_tokio_runtime(),
|
rt: utils::create_tokio_runtime(),
|
||||||
rule_keys: Vec::new(),
|
rule_keys: Vec::new(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(&mut self) {
|
fn exec(&mut self) {
|
||||||
let analysis_start_time: DateTime<Local> = Local::now();
|
let analysis_start_time: DateTime<Local> = Local::now();
|
||||||
if !configs::CONFIG.read().unwrap().args.is_present("quiet") {
|
if !configs::CONFIG.read().unwrap().args.is_present("quiet") {
|
||||||
self.output_logo();
|
self.output_logo();
|
||||||
println!("");
|
println!();
|
||||||
self.output_eggs(&format!(
|
self.output_eggs(&format!(
|
||||||
"{:02}/{:02}",
|
"{:02}/{:02}",
|
||||||
&analysis_start_time.month().to_owned(),
|
&analysis_start_time.month().to_owned(),
|
||||||
@@ -98,12 +104,12 @@ impl App {
|
|||||||
.ok();
|
.ok();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if configs::CONFIG.read().unwrap().args.args.len() == 0 {
|
if configs::CONFIG.read().unwrap().args.args.is_empty() {
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
configs::CONFIG.read().unwrap().args.usage().to_string()
|
configs::CONFIG.read().unwrap().args.usage().to_string()
|
||||||
);
|
);
|
||||||
println!("");
|
println!();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Some(csv_path) = configs::CONFIG.read().unwrap().args.value_of("output") {
|
if let Some(csv_path) = configs::CONFIG.read().unwrap().args.value_of("output") {
|
||||||
@@ -121,7 +127,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
if *STATISTICS_FLAG {
|
if *STATISTICS_FLAG {
|
||||||
println!("Generating Event ID Statistics");
|
println!("Generating Event ID Statistics");
|
||||||
println!("");
|
println!();
|
||||||
}
|
}
|
||||||
if configs::CONFIG
|
if configs::CONFIG
|
||||||
.read()
|
.read()
|
||||||
@@ -138,11 +144,11 @@ impl App {
|
|||||||
if !filepath.ends_with(".evtx")
|
if !filepath.ends_with(".evtx")
|
||||||
|| Path::new(filepath)
|
|| Path::new(filepath)
|
||||||
.file_stem()
|
.file_stem()
|
||||||
.unwrap_or(OsStr::new("."))
|
.unwrap_or_else(|| OsStr::new("."))
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.trim()
|
.trim()
|
||||||
.starts_with(".")
|
.starts_with('.')
|
||||||
{
|
{
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
&mut BufWriter::new(std::io::stderr().lock()),
|
||||||
@@ -153,8 +159,8 @@ impl App {
|
|||||||
}
|
}
|
||||||
self.analysis_files(vec![PathBuf::from(filepath)]);
|
self.analysis_files(vec![PathBuf::from(filepath)]);
|
||||||
} else if let Some(directory) = configs::CONFIG.read().unwrap().args.value_of("directory") {
|
} else if let Some(directory) = configs::CONFIG.read().unwrap().args.value_of("directory") {
|
||||||
let evtx_files = self.collect_evtxfiles(&directory);
|
let evtx_files = self.collect_evtxfiles(directory);
|
||||||
if evtx_files.len() == 0 {
|
if evtx_files.is_empty() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
&mut BufWriter::new(std::io::stderr().lock()),
|
||||||
&"No .evtx files were found.".to_string(),
|
&"No .evtx files were found.".to_string(),
|
||||||
@@ -174,9 +180,9 @@ impl App {
|
|||||||
}
|
}
|
||||||
let analysis_end_time: DateTime<Local> = Local::now();
|
let analysis_end_time: DateTime<Local> = Local::now();
|
||||||
let analysis_duration = analysis_end_time.signed_duration_since(analysis_start_time);
|
let analysis_duration = analysis_end_time.signed_duration_since(analysis_start_time);
|
||||||
println!("");
|
println!();
|
||||||
println!("Elapsed Time: {}", &analysis_duration.hhmmssxxx());
|
println!("Elapsed Time: {}", &analysis_duration.hhmmssxxx());
|
||||||
println!("");
|
println!();
|
||||||
|
|
||||||
// Qオプションを付けた場合もしくはパースのエラーがない場合はerrorのstackが9となるのでエラーログファイル自体が生成されない。
|
// Qオプションを付けた場合もしくはパースのエラーがない場合はerrorのstackが9となるのでエラーログファイル自体が生成されない。
|
||||||
if ERROR_LOG_STACK.lock().unwrap().len() > 0 {
|
if ERROR_LOG_STACK.lock().unwrap().len() > 0 {
|
||||||
@@ -200,7 +206,7 @@ impl App {
|
|||||||
let log_dir = env::var("windir").expect("windir is not found");
|
let log_dir = env::var("windir").expect("windir is not found");
|
||||||
let evtx_files =
|
let evtx_files =
|
||||||
self.collect_evtxfiles(&[log_dir, "System32\\winevt\\Logs".to_string()].join("/"));
|
self.collect_evtxfiles(&[log_dir, "System32\\winevt\\Logs".to_string()].join("/"));
|
||||||
if evtx_files.len() == 0 {
|
if evtx_files.is_empty() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
&mut BufWriter::new(std::io::stderr().lock()),
|
||||||
&"No .evtx files were found.".to_string(),
|
&"No .evtx files were found.".to_string(),
|
||||||
@@ -208,14 +214,14 @@ impl App {
|
|||||||
.ok();
|
.ok();
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
return Some(evtx_files);
|
Some(evtx_files)
|
||||||
} else {
|
} else {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
&mut BufWriter::new(std::io::stderr().lock()),
|
||||||
&"-l / --liveanalysis needs to be run as Administrator on Windows.\r\n".to_string(),
|
&"-l / --liveanalysis needs to be run as Administrator on Windows.\r\n".to_string(),
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
return None;
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,27 +249,27 @@ impl App {
|
|||||||
|
|
||||||
let path = e.unwrap().path();
|
let path = e.unwrap().path();
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
path.to_str().and_then(|path_str| {
|
path.to_str().map(|path_str| {
|
||||||
let subdir_ret = self.collect_evtxfiles(path_str);
|
let subdir_ret = self.collect_evtxfiles(path_str);
|
||||||
ret.extend(subdir_ret);
|
ret.extend(subdir_ret);
|
||||||
return Option::Some(());
|
Option::Some(())
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let path_str = path.to_str().unwrap_or("");
|
let path_str = path.to_str().unwrap_or("");
|
||||||
if path_str.ends_with(".evtx")
|
if path_str.ends_with(".evtx")
|
||||||
&& !Path::new(path_str)
|
&& !Path::new(path_str)
|
||||||
.file_stem()
|
.file_stem()
|
||||||
.unwrap_or(OsStr::new("."))
|
.unwrap_or_else(|| OsStr::new("."))
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.starts_with(".")
|
.starts_with('.')
|
||||||
{
|
{
|
||||||
ret.push(path);
|
ret.push(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_contributors(&self) {
|
fn print_contributors(&self) {
|
||||||
@@ -295,7 +301,7 @@ impl App {
|
|||||||
&filter::exclude_ids(),
|
&filter::exclude_ids(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if rule_files.len() == 0 {
|
if rule_files.is_empty() {
|
||||||
AlertMessage::alert(
|
AlertMessage::alert(
|
||||||
&mut BufWriter::new(std::io::stderr().lock()),
|
&mut BufWriter::new(std::io::stderr().lock()),
|
||||||
&"No rules were loaded. Please download the latest rules with the --update-rules option.\r\n".to_string(),
|
&"No rules were loaded. Please download the latest rules with the --update-rules option.\r\n".to_string(),
|
||||||
@@ -369,14 +375,14 @@ impl App {
|
|||||||
|
|
||||||
// target_eventids.txtでフィルタする。
|
// target_eventids.txtでフィルタする。
|
||||||
let data = record_result.unwrap().data;
|
let data = record_result.unwrap().data;
|
||||||
if self._is_target_event_id(&data) == false {
|
if !self._is_target_event_id(&data) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// EvtxRecordInfo構造体に変更
|
// EvtxRecordInfo構造体に変更
|
||||||
records_per_detect.push(data);
|
records_per_detect.push(data);
|
||||||
}
|
}
|
||||||
if records_per_detect.len() == 0 {
|
if records_per_detect.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,7 +403,7 @@ impl App {
|
|||||||
|
|
||||||
tl.tm_stats_dsp_msg();
|
tl.tm_stats_dsp_msg();
|
||||||
|
|
||||||
return detection;
|
detection
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_rec_infos(
|
async fn create_rec_infos(
|
||||||
@@ -407,28 +413,28 @@ impl App {
|
|||||||
) -> Vec<EvtxRecordInfo> {
|
) -> Vec<EvtxRecordInfo> {
|
||||||
let path = Arc::new(path.to_string());
|
let path = Arc::new(path.to_string());
|
||||||
let rule_keys = Arc::new(rule_keys);
|
let rule_keys = Arc::new(rule_keys);
|
||||||
let threads: Vec<JoinHandle<EvtxRecordInfo>> = records_per_detect
|
let threads: Vec<JoinHandle<EvtxRecordInfo>> = {
|
||||||
.into_iter()
|
let this = records_per_detect
|
||||||
.map(|rec| {
|
.into_iter()
|
||||||
let arc_rule_keys = Arc::clone(&rule_keys);
|
.map(|rec| -> JoinHandle<EvtxRecordInfo> {
|
||||||
let arc_path = Arc::clone(&path);
|
let arc_rule_keys = Arc::clone(&rule_keys);
|
||||||
return spawn(async move {
|
let arc_path = Arc::clone(&path);
|
||||||
let rec_info =
|
spawn(async move {
|
||||||
utils::create_rec_info(rec, arc_path.to_string(), &arc_rule_keys);
|
utils::create_rec_info(rec, arc_path.to_string(), &arc_rule_keys)
|
||||||
return rec_info;
|
})
|
||||||
});
|
});
|
||||||
})
|
FromIterator::from_iter(this)
|
||||||
.collect();
|
};
|
||||||
|
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
for thread in threads.into_iter() {
|
for thread in threads.into_iter() {
|
||||||
ret.push(thread.await.unwrap());
|
ret.push(thread.await.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_all_keys(&self, rules: &Vec<RuleNode>) -> Vec<String> {
|
fn get_all_keys(&self, rules: &[RuleNode]) -> Vec<String> {
|
||||||
let mut key_set = HashSet::new();
|
let mut key_set = HashSet::new();
|
||||||
for rule in rules {
|
for rule in rules {
|
||||||
let keys = get_detection_keys(rule);
|
let keys = get_detection_keys(rule);
|
||||||
@@ -436,7 +442,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let ret: Vec<String> = key_set.into_iter().collect();
|
let ret: Vec<String> = key_set.into_iter().collect();
|
||||||
return ret;
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// target_eventids.txtの設定を元にフィルタする。
|
// target_eventids.txtの設定を元にフィルタする。
|
||||||
@@ -446,11 +452,11 @@ impl App {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return match eventid.unwrap() {
|
match eventid.unwrap() {
|
||||||
Value::String(s) => utils::is_target_event_id(s),
|
Value::String(s) => utils::is_target_event_id(s),
|
||||||
Value::Number(n) => utils::is_target_event_id(&n.to_string()),
|
Value::Number(n) => utils::is_target_event_id(&n.to_string()),
|
||||||
_ => true, // レコードからEventIdが取得できない場合は、特にフィルタしない
|
_ => true, // レコードからEventIdが取得できない場合は、特にフィルタしない
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evtx_to_jsons(&self, evtx_filepath: PathBuf) -> Option<EvtxParser<File>> {
|
fn evtx_to_jsons(&self, evtx_filepath: PathBuf) -> Option<EvtxParser<File>> {
|
||||||
@@ -462,11 +468,11 @@ impl App {
|
|||||||
parse_config = parse_config.num_threads(0); // 設定しないと遅かったので、設定しておく。
|
parse_config = parse_config.num_threads(0); // 設定しないと遅かったので、設定しておく。
|
||||||
|
|
||||||
let evtx_parser = evtx_parser.with_configuration(parse_config);
|
let evtx_parser = evtx_parser.with_configuration(parse_config);
|
||||||
return Option::Some(evtx_parser);
|
Option::Some(evtx_parser)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
return Option::None;
|
Option::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -479,8 +485,8 @@ impl App {
|
|||||||
|
|
||||||
/// output logo
|
/// output logo
|
||||||
fn output_logo(&self) {
|
fn output_logo(&self) {
|
||||||
let fp = &format!("art/logo.txt");
|
let fp = &"art/logo.txt".to_string();
|
||||||
let content = fs::read_to_string(fp).unwrap_or("".to_owned());
|
let content = fs::read_to_string(fp).unwrap_or_default();
|
||||||
println!("{}", content);
|
println!("{}", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,7 +501,7 @@ impl App {
|
|||||||
match eggs.get(exec_datestr) {
|
match eggs.get(exec_datestr) {
|
||||||
None => {}
|
None => {}
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
let content = fs::read_to_string(path).unwrap_or("".to_owned());
|
let content = fs::read_to_string(path).unwrap_or_default();
|
||||||
println!("{}", content);
|
println!("{}", content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ impl SlackNotify {
|
|||||||
eprintln!("WEBHOOK_URL not found");
|
eprintln!("WEBHOOK_URL not found");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// send message to slack.
|
// send message to slack.
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
pub mod statistics;
|
pub mod statistics;
|
||||||
pub mod timeline;
|
pub mod timelines;
|
||||||
|
|||||||
@@ -20,16 +20,16 @@ impl EventStatistics {
|
|||||||
end_time: String,
|
end_time: String,
|
||||||
stats_list: HashMap<String, usize>,
|
stats_list: HashMap<String, usize>,
|
||||||
) -> EventStatistics {
|
) -> EventStatistics {
|
||||||
return EventStatistics {
|
EventStatistics {
|
||||||
total,
|
total,
|
||||||
filepath,
|
filepath,
|
||||||
start_time,
|
start_time,
|
||||||
end_time,
|
end_time,
|
||||||
stats_list,
|
stats_list,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&mut self, records: &Vec<EvtxRecordInfo>) {
|
pub fn start(&mut self, records: &[EvtxRecordInfo]) {
|
||||||
// 引数でstatisticsオプションが指定されている時だけ、統計情報を出力する。
|
// 引数でstatisticsオプションが指定されている時だけ、統計情報を出力する。
|
||||||
if !configs::CONFIG
|
if !configs::CONFIG
|
||||||
.read()
|
.read()
|
||||||
@@ -49,8 +49,8 @@ impl EventStatistics {
|
|||||||
self.stats_eventid(records);
|
self.stats_eventid(records);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stats_time_cnt(&mut self, records: &Vec<EvtxRecordInfo>) {
|
fn stats_time_cnt(&mut self, records: &[EvtxRecordInfo]) {
|
||||||
if records.len() == 0 {
|
if records.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.filepath = records[0].evtx_filepath.as_str().to_owned();
|
self.filepath = records[0].evtx_filepath.as_str().to_owned();
|
||||||
@@ -62,18 +62,16 @@ impl EventStatistics {
|
|||||||
&"Event.System.TimeCreated_attributes.SystemTime".to_string(),
|
&"Event.System.TimeCreated_attributes.SystemTime".to_string(),
|
||||||
&record.record,
|
&record.record,
|
||||||
)
|
)
|
||||||
.and_then(|evt_value| {
|
.map(|evt_value| evt_value.to_string());
|
||||||
return Option::Some(evt_value.to_string());
|
|
||||||
});
|
|
||||||
if evttime.is_none() {
|
if evttime.is_none() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let evttime = evttime.unwrap();
|
let evttime = evttime.unwrap();
|
||||||
if self.start_time.len() == 0 || evttime < self.start_time {
|
if self.start_time.is_empty() || evttime < self.start_time {
|
||||||
self.start_time = evttime.to_string();
|
self.start_time = evttime.to_string();
|
||||||
}
|
}
|
||||||
if self.end_time.len() == 0 || evttime > self.end_time {
|
if self.end_time.is_empty() || evttime > self.end_time {
|
||||||
self.end_time = evttime;
|
self.end_time = evttime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +79,7 @@ impl EventStatistics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EventIDで集計
|
// EventIDで集計
|
||||||
fn stats_eventid(&mut self, records: &Vec<EvtxRecordInfo>) {
|
fn stats_eventid(&mut self, records: &[EvtxRecordInfo]) {
|
||||||
// let mut evtstat_map = HashMap::new();
|
// let mut evtstat_map = HashMap::new();
|
||||||
for record in records.iter() {
|
for record in records.iter() {
|
||||||
let evtid = utils::get_event_value(&"EventID".to_string(), &record.record);
|
let evtid = utils::get_event_value(&"EventID".to_string(), &record.record);
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ pub struct Timeline {
|
|||||||
pub stats: EventStatistics,
|
pub stats: EventStatistics,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Timeline {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Timeline {
|
impl Timeline {
|
||||||
pub fn new() -> Timeline {
|
pub fn new() -> Timeline {
|
||||||
let totalcnt = 0;
|
let totalcnt = 0;
|
||||||
@@ -17,10 +23,10 @@ impl Timeline {
|
|||||||
let statslst = HashMap::new();
|
let statslst = HashMap::new();
|
||||||
|
|
||||||
let statistic = EventStatistics::new(totalcnt, filepath, starttm, endtm, statslst);
|
let statistic = EventStatistics::new(totalcnt, filepath, starttm, endtm, statslst);
|
||||||
return Timeline { stats: statistic };
|
Timeline { stats: statistic }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&mut self, records: &Vec<EvtxRecordInfo>) {
|
pub fn start(&mut self, records: &[EvtxRecordInfo]) {
|
||||||
self.stats.start(records);
|
self.stats.start(records);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +52,7 @@ impl Timeline {
|
|||||||
|
|
||||||
// 集計件数でソート
|
// 集計件数でソート
|
||||||
let mut mapsorted: Vec<_> = self.stats.stats_list.iter().collect();
|
let mut mapsorted: Vec<_> = self.stats.stats_list.iter().collect();
|
||||||
mapsorted.sort_by(|x, y| y.1.cmp(&x.1));
|
mapsorted.sort_by(|x, y| y.1.cmp(x.1));
|
||||||
|
|
||||||
// イベントID毎の出力メッセージ生成
|
// イベントID毎の出力メッセージ生成
|
||||||
let stats_msges: Vec<String> = self.tm_stats_set_msg(mapsorted);
|
let stats_msges: Vec<String> = self.tm_stats_set_msg(mapsorted);
|
||||||
@@ -95,6 +101,6 @@ impl Timeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
msges.push("---------------------------------------".to_string());
|
msges.push("---------------------------------------".to_string());
|
||||||
return msges;
|
msges
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
44
src/yaml.rs
44
src/yaml.rs
@@ -23,6 +23,12 @@ pub struct ParseYaml {
|
|||||||
pub errorrule_count: u128,
|
pub errorrule_count: u128,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for ParseYaml {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ParseYaml {
|
impl ParseYaml {
|
||||||
pub fn new() -> ParseYaml {
|
pub fn new() -> ParseYaml {
|
||||||
ParseYaml {
|
ParseYaml {
|
||||||
@@ -37,7 +43,7 @@ impl ParseYaml {
|
|||||||
let mut file_content = String::new();
|
let mut file_content = String::new();
|
||||||
|
|
||||||
let mut fr = fs::File::open(path)
|
let mut fr = fs::File::open(path)
|
||||||
.map(|f| BufReader::new(f))
|
.map(BufReader::new)
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
fr.read_to_string(&mut file_content)
|
fr.read_to_string(&mut file_content)
|
||||||
@@ -76,7 +82,7 @@ impl ParseYaml {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.to_path_buf()
|
.to_path_buf()
|
||||||
.extension()
|
.extension()
|
||||||
.unwrap_or(OsStr::new(""))
|
.unwrap_or_else(|| OsStr::new(""))
|
||||||
!= "yml"
|
!= "yml"
|
||||||
{
|
{
|
||||||
return io::Result::Ok(String::default());
|
return io::Result::Ok(String::default());
|
||||||
@@ -126,7 +132,7 @@ impl ParseYaml {
|
|||||||
|
|
||||||
yaml_docs.extend(yaml_contents.unwrap().into_iter().map(|yaml_content| {
|
yaml_docs.extend(yaml_contents.unwrap().into_iter().map(|yaml_content| {
|
||||||
let filepath = format!("{}", path.as_ref().to_path_buf().display());
|
let filepath = format!("{}", path.as_ref().to_path_buf().display());
|
||||||
return (filepath, yaml_content);
|
(filepath, yaml_content)
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
let mut entries = fs::read_dir(path)?;
|
let mut entries = fs::read_dir(path)?;
|
||||||
@@ -144,7 +150,7 @@ impl ParseYaml {
|
|||||||
|
|
||||||
// 拡張子がymlでないファイルは無視
|
// 拡張子がymlでないファイルは無視
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
if path.extension().unwrap_or(OsStr::new("")) != "yml" {
|
if path.extension().unwrap_or_else(|| OsStr::new("")) != "yml" {
|
||||||
return io::Result::Ok(ret);
|
return io::Result::Ok(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,10 +198,10 @@ impl ParseYaml {
|
|||||||
|
|
||||||
let yaml_contents = yaml_contents.unwrap().into_iter().map(|yaml_content| {
|
let yaml_contents = yaml_contents.unwrap().into_iter().map(|yaml_content| {
|
||||||
let filepath = format!("{}", entry.path().display());
|
let filepath = format!("{}", entry.path().display());
|
||||||
return (filepath, yaml_content);
|
(filepath, yaml_content)
|
||||||
});
|
});
|
||||||
ret.extend(yaml_contents);
|
ret.extend(yaml_contents);
|
||||||
return io::Result::Ok(ret);
|
io::Result::Ok(ret)
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,11 +260,11 @@ impl ParseYaml {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Option::Some((filepath, yaml_doc));
|
Option::Some((filepath, yaml_doc))
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
self.files.extend(files);
|
self.files.extend(files);
|
||||||
return io::Result::Ok(String::default());
|
io::Result::Ok(String::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,7 +335,7 @@ mod tests {
|
|||||||
let path = Path::new("test_files/rules/yaml/error.yml");
|
let path = Path::new("test_files/rules/yaml/error.yml");
|
||||||
let ret = yaml.read_file(path.to_path_buf()).unwrap();
|
let ret = yaml.read_file(path.to_path_buf()).unwrap();
|
||||||
let rule = YamlLoader::load_from_str(&ret);
|
let rule = YamlLoader::load_from_str(&ret);
|
||||||
assert_eq!(rule.is_err(), true);
|
assert!(rule.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -337,7 +343,7 @@ mod tests {
|
|||||||
fn test_default_level_read_yaml() {
|
fn test_default_level_read_yaml() {
|
||||||
let mut yaml = yaml::ParseYaml::new();
|
let mut yaml = yaml::ParseYaml::new();
|
||||||
let path = Path::new("test_files/rules/level_yaml");
|
let path = Path::new("test_files/rules/level_yaml");
|
||||||
yaml.read_dir(path.to_path_buf(), &"", &filter::exclude_ids())
|
yaml.read_dir(path.to_path_buf(), "", &filter::exclude_ids())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(yaml.files.len(), 5);
|
assert_eq!(yaml.files.len(), 5);
|
||||||
}
|
}
|
||||||
@@ -346,7 +352,7 @@ mod tests {
|
|||||||
fn test_info_level_read_yaml() {
|
fn test_info_level_read_yaml() {
|
||||||
let mut yaml = yaml::ParseYaml::new();
|
let mut yaml = yaml::ParseYaml::new();
|
||||||
let path = Path::new("test_files/rules/level_yaml");
|
let path = Path::new("test_files/rules/level_yaml");
|
||||||
yaml.read_dir(path.to_path_buf(), &"informational", &filter::exclude_ids())
|
yaml.read_dir(path.to_path_buf(), "informational", &filter::exclude_ids())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(yaml.files.len(), 5);
|
assert_eq!(yaml.files.len(), 5);
|
||||||
}
|
}
|
||||||
@@ -354,7 +360,7 @@ mod tests {
|
|||||||
fn test_low_level_read_yaml() {
|
fn test_low_level_read_yaml() {
|
||||||
let mut yaml = yaml::ParseYaml::new();
|
let mut yaml = yaml::ParseYaml::new();
|
||||||
let path = Path::new("test_files/rules/level_yaml");
|
let path = Path::new("test_files/rules/level_yaml");
|
||||||
yaml.read_dir(path.to_path_buf(), &"LOW", &filter::exclude_ids())
|
yaml.read_dir(path.to_path_buf(), "LOW", &filter::exclude_ids())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(yaml.files.len(), 4);
|
assert_eq!(yaml.files.len(), 4);
|
||||||
}
|
}
|
||||||
@@ -362,7 +368,7 @@ mod tests {
|
|||||||
fn test_medium_level_read_yaml() {
|
fn test_medium_level_read_yaml() {
|
||||||
let mut yaml = yaml::ParseYaml::new();
|
let mut yaml = yaml::ParseYaml::new();
|
||||||
let path = Path::new("test_files/rules/level_yaml");
|
let path = Path::new("test_files/rules/level_yaml");
|
||||||
yaml.read_dir(path.to_path_buf(), &"MEDIUM", &filter::exclude_ids())
|
yaml.read_dir(path.to_path_buf(), "MEDIUM", &filter::exclude_ids())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(yaml.files.len(), 3);
|
assert_eq!(yaml.files.len(), 3);
|
||||||
}
|
}
|
||||||
@@ -370,7 +376,7 @@ mod tests {
|
|||||||
fn test_high_level_read_yaml() {
|
fn test_high_level_read_yaml() {
|
||||||
let mut yaml = yaml::ParseYaml::new();
|
let mut yaml = yaml::ParseYaml::new();
|
||||||
let path = Path::new("test_files/rules/level_yaml");
|
let path = Path::new("test_files/rules/level_yaml");
|
||||||
yaml.read_dir(path.to_path_buf(), &"HIGH", &filter::exclude_ids())
|
yaml.read_dir(path.to_path_buf(), "HIGH", &filter::exclude_ids())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(yaml.files.len(), 2);
|
assert_eq!(yaml.files.len(), 2);
|
||||||
}
|
}
|
||||||
@@ -378,7 +384,7 @@ mod tests {
|
|||||||
fn test_critical_level_read_yaml() {
|
fn test_critical_level_read_yaml() {
|
||||||
let mut yaml = yaml::ParseYaml::new();
|
let mut yaml = yaml::ParseYaml::new();
|
||||||
let path = Path::new("test_files/rules/level_yaml");
|
let path = Path::new("test_files/rules/level_yaml");
|
||||||
yaml.read_dir(path.to_path_buf(), &"CRITICAL", &filter::exclude_ids())
|
yaml.read_dir(path.to_path_buf(), "CRITICAL", &filter::exclude_ids())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(yaml.files.len(), 1);
|
assert_eq!(yaml.files.len(), 1);
|
||||||
}
|
}
|
||||||
@@ -388,7 +394,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut yaml = yaml::ParseYaml::new();
|
let mut yaml = yaml::ParseYaml::new();
|
||||||
let path = Path::new("test_files/rules/yaml");
|
let path = Path::new("test_files/rules/yaml");
|
||||||
yaml.read_dir(path.to_path_buf(), &"", &filter::exclude_ids())
|
yaml.read_dir(path.to_path_buf(), "", &filter::exclude_ids())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(yaml.ignorerule_count, 10);
|
assert_eq!(yaml.ignorerule_count, 10);
|
||||||
}
|
}
|
||||||
@@ -401,8 +407,7 @@ mod tests {
|
|||||||
let exclude_ids = RuleExclude {
|
let exclude_ids = RuleExclude {
|
||||||
no_use_rule: HashSet::new(),
|
no_use_rule: HashSet::new(),
|
||||||
};
|
};
|
||||||
yaml.read_dir(path.to_path_buf(), &"", &exclude_ids)
|
yaml.read_dir(path.to_path_buf(), "", &exclude_ids).unwrap();
|
||||||
.unwrap();
|
|
||||||
assert_eq!(yaml.ignorerule_count, 0);
|
assert_eq!(yaml.ignorerule_count, 0);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@@ -412,8 +417,7 @@ mod tests {
|
|||||||
let exclude_ids = RuleExclude {
|
let exclude_ids = RuleExclude {
|
||||||
no_use_rule: HashSet::new(),
|
no_use_rule: HashSet::new(),
|
||||||
};
|
};
|
||||||
yaml.read_dir(path.to_path_buf(), &"", &exclude_ids)
|
yaml.read_dir(path.to_path_buf(), "", &exclude_ids).unwrap();
|
||||||
.unwrap();
|
|
||||||
assert_eq!(yaml.ignorerule_count, 1);
|
assert_eq!(yaml.ignorerule_count, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user