diff --git a/src/afterfact.rs b/src/afterfact.rs index 2dc3301d..157fa3a4 100644 --- a/src/afterfact.rs +++ b/src/afterfact.rs @@ -15,6 +15,7 @@ use std::process; pub struct CsvFormat<'a> { time: &'a str, filepath: &'a str, + rulepath: &'a str, level: &'a str, title: &'a str, message: &'a str, @@ -79,6 +80,7 @@ fn emit_csv(writer: &mut W) -> Result<(), Box> { wtr.serialize(CsvFormat { time: &format_time(time), filepath: &detect_info.filepath, + rulepath: &detect_info.rulepath, level: &detect_info.level, title: &detect_info.title, message: &detect_info.detail, @@ -118,6 +120,7 @@ fn test_emit_csv() { use serde_json::Value; use std::fs::{read_to_string, remove_file}; let testfilepath: &str = "test.evtx"; + let testrulepath: &str = "test-rule.yml"; let test_title = "test_title"; let test_level = "high"; let output = "pokepoke"; @@ -141,6 +144,7 @@ fn test_emit_csv() { let event: Value = serde_json::from_str(val).unwrap(); messages.insert( testfilepath.to_string(), + testrulepath.to_string(), &event, test_level.to_string(), test_title.to_string(), @@ -152,11 +156,13 @@ fn test_emit_csv() { .datetime_from_str("1996-02-27T01:05:01Z", "%Y-%m-%dT%H:%M:%SZ") .unwrap(); let expect_tz = expect_time.with_timezone(&Local); - let expect = "Time,Filepath,Level,Title,Message\n".to_string() + let expect = "Time,Filepath,Rulepath,Level,Title,Message\n".to_string() + &expect_tz.clone().format("%Y-%m-%dT%H:%M:%S%:z").to_string() + "," + testfilepath + "," + + testrulepath + + "," + test_level + "," + test_title diff --git a/src/detections/detection.rs b/src/detections/detection.rs index a43a94d2..a86aeb30 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -85,7 +85,7 @@ impl Detection { return rulefile_loader .files .into_iter() - .map(|rule_file| rule::create_rule(rule_file)) + .map(|rule_file_tuple| rule::create_rule(rule_file_tuple.0, rule_file_tuple.1)) .filter_map(return_if_success) .collect(); } @@ -156,6 +156,7 @@ impl Detection { fn insert_message(rule: &RuleNode, record_info: &EvtxRecordInfo) { MESSAGES.lock().unwrap().insert( record_info.evtx_filepath.to_string(), + rule.rulepath.to_string(), &record_info.record, rule.yaml["level"].as_str().unwrap_or("").to_string(), rule.yaml["title"].as_str().unwrap_or("").to_string(), @@ -168,6 +169,7 @@ impl Detection { let output = Detection::create_count_output(rule, &agg_result); MESSAGES.lock().unwrap().insert_message( agg_result.filepath, + rule.rulepath.to_string(), agg_result.start_timedate, rule.yaml["level"].as_str().unwrap_or("").to_string(), rule.yaml["title"].as_str().unwrap_or("").to_string(), diff --git a/src/detections/print.rs b/src/detections/print.rs index 136ba368..c9371f09 100644 --- a/src/detections/print.rs +++ b/src/detections/print.rs @@ -17,6 +17,7 @@ pub struct Message { #[derive(Debug, Clone)] pub struct DetectInfo { pub filepath: String, + pub rulepath: String, pub level: String, pub title: String, pub detail: String, @@ -38,6 +39,7 @@ impl Message { pub fn insert_message( &mut self, target_file: String, + rule_path: String, event_time: DateTime, level: String, event_title: String, @@ -45,6 +47,7 @@ impl Message { ) { let detect_info = DetectInfo { filepath: target_file, + rulepath: rule_path, level: level, title: event_title, detail: event_detail, @@ -65,6 +68,7 @@ impl Message { pub fn insert( &mut self, target_file: String, + rule_path: String, event_record: &Value, level: String, event_title: String, @@ -73,7 +77,14 @@ impl Message { let message = &self.parse_message(event_record, output); let default_time = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); let time = Message::get_event_time(event_record).unwrap_or(default_time); - self.insert_message(target_file, time, level, event_title, message.to_string()) + self.insert_message( + target_file, + rule_path, + time, + level, + event_title, + message.to_string(), + ) } fn parse_message(&mut self, event_record: &Value, output: String) -> String { @@ -200,6 +211,7 @@ mod tests { let event_record_1: Value = serde_json::from_str(json_str_1).unwrap(); message.insert( "a".to_string(), + "test_rule".to_string(), &event_record_1, "high".to_string(), "test1".to_string(), @@ -223,6 +235,7 @@ mod tests { let event_record_2: Value = serde_json::from_str(json_str_2).unwrap(); message.insert( "a".to_string(), + "test_rule2".to_string(), &event_record_2, "high".to_string(), "test2".to_string(), @@ -246,6 +259,7 @@ mod tests { let event_record_3: Value = serde_json::from_str(json_str_3).unwrap(); message.insert( "a".to_string(), + "test_rule3".to_string(), &event_record_3, "high".to_string(), "test3".to_string(), @@ -264,6 +278,7 @@ mod tests { let event_record_4: Value = serde_json::from_str(json_str_4).unwrap(); message.insert( "a".to_string(), + "test_rule4".to_string(), &event_record_4, "medium".to_string(), "test4".to_string(), @@ -272,7 +287,7 @@ mod tests { let display = format!("{}", format_args!("{:?}", message)); println!("display::::{}", display); - let expect = "Message { map: {1970-01-01T00:00:00Z: [DetectInfo { filepath: \"a\", level: \"medium\", title: \"test4\", detail: \"CommandLine4: hoge\" }], 1996-02-27T01:05:01Z: [DetectInfo { filepath: \"a\", level: \"high\", title: \"test1\", detail: \"CommandLine1: hoge\" }, DetectInfo { filepath: \"a\", level: \"high\", title: \"test2\", detail: \"CommandLine2: hoge\" }], 2000-01-21T09:06:01Z: [DetectInfo { filepath: \"a\", level: \"high\", title: \"test3\", detail: \"CommandLine3: hoge\" }]} }"; + let expect = "Message { map: {1970-01-01T00:00:00Z: [DetectInfo { filepath: \"a\", rulepath: \"test_rule4\", level: \"medium\", title: \"test4\", detail: \"CommandLine4: hoge\" }], 1996-02-27T01:05:01Z: [DetectInfo { filepath: \"a\", rulepath: \"test_rule\", level: \"high\", title: \"test1\", detail: \"CommandLine1: hoge\" }, DetectInfo { filepath: \"a\", rulepath: \"test_rule2\", level: \"high\", title: \"test2\", detail: \"CommandLine2: hoge\" }], 2000-01-21T09:06:01Z: [DetectInfo { filepath: \"a\", rulepath: \"test_rule3\", level: \"high\", title: \"test3\", detail: \"CommandLine3: hoge\" }]} }"; assert_eq!(display, expect); } diff --git a/src/detections/rule/condition_parser.rs b/src/detections/rule/condition_parser.rs index 3e17fec2..841504ae 100644 --- a/src/detections/rule/condition_parser.rs +++ b/src/detections/rule/condition_parser.rs @@ -528,7 +528,7 @@ mod tests { fn check_rule_parse_error(rule_str: &str, errmsgs: Vec) { let mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter(); - let mut rule_node = create_rule(rule_yaml.next().unwrap()); + let mut rule_node = create_rule("testpath".to_string(), rule_yaml.next().unwrap()); assert_eq!(rule_node.init(), Err(errmsgs)); } @@ -1183,7 +1183,7 @@ mod tests { "#; let mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter(); - let mut rule_node = create_rule(rule_yaml.next().unwrap()); + let mut rule_node = create_rule("testpath".to_string(), rule_yaml.next().unwrap()); assert_eq!( rule_node.init(), diff --git a/src/detections/rule/count.rs b/src/detections/rule/count.rs index 9030cb7e..fcc57c79 100644 --- a/src/detections/rule/count.rs +++ b/src/detections/rule/count.rs @@ -667,7 +667,7 @@ mod tests { "#; let mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter(); let test = rule_yaml.next().unwrap(); - let mut rule_node = create_rule(test); + let mut rule_node = create_rule("testpath".to_string(), test); let init_result = rule_node.init(); assert_eq!(init_result.is_ok(), true); let target = vec![SIMPLE_RECORD_STR, record_str]; @@ -754,7 +754,7 @@ mod tests { ) { let mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter(); let test = rule_yaml.next().unwrap(); - let mut rule_node = create_rule(test); + let mut rule_node = create_rule("testpath".to_string(), test); let error_checker = rule_node.init(); if error_checker.is_err() { assert!(false, "failed to init rulenode"); diff --git a/src/detections/rule/mod.rs b/src/detections/rule/mod.rs index 38a7eb50..bd882584 100644 --- a/src/detections/rule/mod.rs +++ b/src/detections/rule/mod.rs @@ -18,12 +18,13 @@ mod condition_parser; mod count; use self::count::TimeFrameInfo; -pub fn create_rule(yaml: Yaml) -> RuleNode { - return RuleNode::new(yaml); +pub fn create_rule(rulepath: String, yaml: Yaml) -> RuleNode { + return RuleNode::new(rulepath, yaml); } /// Ruleファイルを表すノード pub struct RuleNode { + pub rulepath: String, pub yaml: Yaml, detection: Option, countdata: HashMap>>>, @@ -38,8 +39,9 @@ impl Debug for RuleNode { unsafe impl Sync for RuleNode {} impl RuleNode { - pub fn new(yaml: Yaml) -> RuleNode { + pub fn new(rulepath: String, yaml: Yaml) -> RuleNode { return RuleNode { + rulepath: rulepath, yaml: yaml, detection: Option::None, countdata: HashMap::new(), @@ -321,7 +323,7 @@ mod tests { assert_eq!(rule_yaml.is_ok(), true); let rule_yamls = rule_yaml.unwrap(); let mut rule_yaml = rule_yamls.into_iter(); - let mut rule_node = create_rule(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); return rule_node; } @@ -877,7 +879,7 @@ mod tests { output: 'Rule parse test' "#; let mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter(); - let mut rule_node = create_rule(rule_yaml.next().unwrap()); + let mut rule_node = create_rule("testpath".to_string(), rule_yaml.next().unwrap()); assert_eq!( rule_node.init(), @@ -897,7 +899,7 @@ mod tests { output: 'Rule parse test' "#; let mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter(); - let mut rule_node = create_rule(rule_yaml.next().unwrap()); + let mut rule_node = create_rule("testpath".to_string(), rule_yaml.next().unwrap()); assert_eq!( rule_node.init(), @@ -909,7 +911,7 @@ mod tests { fn _check_count(rule_str: &str, record_str: &str, key: &str, expect_count: i32) { let mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter(); let test = rule_yaml.next().unwrap(); - let mut rule_node = create_rule(test); + let mut rule_node = create_rule("testpath".to_string(), test); let _init = rule_node.init(); match serde_json::from_str(record_str) { Ok(record) => { diff --git a/src/yaml.rs b/src/yaml.rs index cdc31d6f..8149b648 100644 --- a/src/yaml.rs +++ b/src/yaml.rs @@ -9,7 +9,7 @@ use std::path::{Path, PathBuf}; use yaml_rust::YamlLoader; pub struct ParseYaml { - pub files: Vec, + pub files: Vec<(String, yaml_rust::Yaml)>, } impl ParseYaml { @@ -46,7 +46,9 @@ impl ParseYaml { if i["ignore"].as_bool().unwrap_or(false) { continue; } - &self.files.push(i); + &self + .files + .push((format!("{}", entry.path().display()), i)); } } Err(e) => {