Feature/#158 add rulefilepath column (#168)

* add level csv column

* update

* Feature/output detect count151 (#167)

* add output process count of detects events #151

* add output process count of detects event when output stdio #151

* add format enter

* update

Co-authored-by: DustInDark <nextsasasa@gmail.com>
This commit is contained in:
James
2021-11-09 00:35:28 +09:00
committed by GitHub
parent c97cf7373a
commit e77a193c5c
7 changed files with 44 additions and 17 deletions

View File

@@ -15,6 +15,7 @@ use std::process;
pub struct CsvFormat<'a> { pub struct CsvFormat<'a> {
time: &'a str, time: &'a str,
filepath: &'a str, filepath: &'a str,
rulepath: &'a str,
level: &'a str, level: &'a str,
title: &'a str, title: &'a str,
message: &'a str, message: &'a str,
@@ -79,6 +80,7 @@ fn emit_csv<W: std::io::Write>(writer: &mut W) -> Result<(), Box<dyn Error>> {
wtr.serialize(CsvFormat { wtr.serialize(CsvFormat {
time: &format_time(time), time: &format_time(time),
filepath: &detect_info.filepath, filepath: &detect_info.filepath,
rulepath: &detect_info.rulepath,
level: &detect_info.level, level: &detect_info.level,
title: &detect_info.title, title: &detect_info.title,
message: &detect_info.detail, message: &detect_info.detail,
@@ -118,6 +120,7 @@ fn test_emit_csv() {
use serde_json::Value; use serde_json::Value;
use std::fs::{read_to_string, remove_file}; use std::fs::{read_to_string, remove_file};
let testfilepath: &str = "test.evtx"; let testfilepath: &str = "test.evtx";
let testrulepath: &str = "test-rule.yml";
let test_title = "test_title"; let test_title = "test_title";
let test_level = "high"; let test_level = "high";
let output = "pokepoke"; let output = "pokepoke";
@@ -141,6 +144,7 @@ fn test_emit_csv() {
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(), testfilepath.to_string(),
testrulepath.to_string(),
&event, &event,
test_level.to_string(), test_level.to_string(),
test_title.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") .datetime_from_str("1996-02-27T01:05:01Z", "%Y-%m-%dT%H:%M:%SZ")
.unwrap(); .unwrap();
let expect_tz = expect_time.with_timezone(&Local); 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() + &expect_tz.clone().format("%Y-%m-%dT%H:%M:%S%:z").to_string()
+ "," + ","
+ testfilepath + testfilepath
+ "," + ","
+ testrulepath
+ ","
+ test_level + test_level
+ "," + ","
+ test_title + test_title

View File

@@ -85,7 +85,7 @@ impl Detection {
return rulefile_loader return rulefile_loader
.files .files
.into_iter() .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) .filter_map(return_if_success)
.collect(); .collect();
} }
@@ -156,6 +156,7 @@ impl Detection {
fn insert_message(rule: &RuleNode, record_info: &EvtxRecordInfo) { fn insert_message(rule: &RuleNode, record_info: &EvtxRecordInfo) {
MESSAGES.lock().unwrap().insert( MESSAGES.lock().unwrap().insert(
record_info.evtx_filepath.to_string(), record_info.evtx_filepath.to_string(),
rule.rulepath.to_string(),
&record_info.record, &record_info.record,
rule.yaml["level"].as_str().unwrap_or("").to_string(), rule.yaml["level"].as_str().unwrap_or("").to_string(),
rule.yaml["title"].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); let output = Detection::create_count_output(rule, &agg_result);
MESSAGES.lock().unwrap().insert_message( MESSAGES.lock().unwrap().insert_message(
agg_result.filepath, agg_result.filepath,
rule.rulepath.to_string(),
agg_result.start_timedate, agg_result.start_timedate,
rule.yaml["level"].as_str().unwrap_or("").to_string(), rule.yaml["level"].as_str().unwrap_or("").to_string(),
rule.yaml["title"].as_str().unwrap_or("").to_string(), rule.yaml["title"].as_str().unwrap_or("").to_string(),

View File

@@ -17,6 +17,7 @@ pub struct Message {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DetectInfo { pub struct DetectInfo {
pub filepath: String, pub filepath: String,
pub rulepath: String,
pub level: String, pub level: String,
pub title: String, pub title: String,
pub detail: String, pub detail: String,
@@ -38,6 +39,7 @@ impl Message {
pub fn insert_message( pub fn insert_message(
&mut self, &mut self,
target_file: String, target_file: String,
rule_path: String,
event_time: DateTime<Utc>, event_time: DateTime<Utc>,
level: String, level: String,
event_title: String, event_title: String,
@@ -45,6 +47,7 @@ impl Message {
) { ) {
let detect_info = DetectInfo { let detect_info = DetectInfo {
filepath: target_file, filepath: target_file,
rulepath: rule_path,
level: level, level: level,
title: event_title, title: event_title,
detail: event_detail, detail: event_detail,
@@ -65,6 +68,7 @@ impl Message {
pub fn insert( pub fn insert(
&mut self, &mut self,
target_file: String, target_file: String,
rule_path: String,
event_record: &Value, event_record: &Value,
level: String, level: String,
event_title: String, event_title: String,
@@ -73,7 +77,14 @@ impl Message {
let message = &self.parse_message(event_record, output); 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(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 { 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(); let event_record_1: Value = serde_json::from_str(json_str_1).unwrap();
message.insert( message.insert(
"a".to_string(), "a".to_string(),
"test_rule".to_string(),
&event_record_1, &event_record_1,
"high".to_string(), "high".to_string(),
"test1".to_string(), "test1".to_string(),
@@ -223,6 +235,7 @@ 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(), "a".to_string(),
"test_rule2".to_string(),
&event_record_2, &event_record_2,
"high".to_string(), "high".to_string(),
"test2".to_string(), "test2".to_string(),
@@ -246,6 +259,7 @@ 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(), "a".to_string(),
"test_rule3".to_string(),
&event_record_3, &event_record_3,
"high".to_string(), "high".to_string(),
"test3".to_string(), "test3".to_string(),
@@ -264,6 +278,7 @@ 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(), "a".to_string(),
"test_rule4".to_string(),
&event_record_4, &event_record_4,
"medium".to_string(), "medium".to_string(),
"test4".to_string(), "test4".to_string(),
@@ -272,7 +287,7 @@ mod tests {
let display = format!("{}", format_args!("{:?}", message)); let display = format!("{}", format_args!("{:?}", message));
println!("display::::{}", display); 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); assert_eq!(display, expect);
} }

View File

@@ -528,7 +528,7 @@ mod tests {
fn check_rule_parse_error(rule_str: &str, errmsgs: Vec<String>) { fn check_rule_parse_error(rule_str: &str, errmsgs: Vec<String>) {
let mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter(); 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)); 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_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!( assert_eq!(
rule_node.init(), rule_node.init(),

View File

@@ -667,7 +667,7 @@ mod tests {
"#; "#;
let mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter(); let mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter();
let test = rule_yaml.next().unwrap(); 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(); let init_result = rule_node.init();
assert_eq!(init_result.is_ok(), true); assert_eq!(init_result.is_ok(), true);
let target = vec![SIMPLE_RECORD_STR, record_str]; 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 mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter();
let test = rule_yaml.next().unwrap(); 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(); let error_checker = rule_node.init();
if error_checker.is_err() { if error_checker.is_err() {
assert!(false, "failed to init rulenode"); assert!(false, "failed to init rulenode");

View File

@@ -18,12 +18,13 @@ mod condition_parser;
mod count; mod count;
use self::count::TimeFrameInfo; use self::count::TimeFrameInfo;
pub fn create_rule(yaml: Yaml) -> RuleNode { pub fn create_rule(rulepath: String, yaml: Yaml) -> RuleNode {
return RuleNode::new(yaml); return RuleNode::new(rulepath, yaml);
} }
/// Ruleファイルを表すード /// Ruleファイルを表すード
pub struct RuleNode { pub struct RuleNode {
pub rulepath: String,
pub yaml: Yaml, pub yaml: Yaml,
detection: Option<DetectionNode>, detection: Option<DetectionNode>,
countdata: HashMap<String, HashMap<String, Vec<DateTime<Utc>>>>, countdata: HashMap<String, HashMap<String, Vec<DateTime<Utc>>>>,
@@ -38,8 +39,9 @@ impl Debug for RuleNode {
unsafe impl Sync for RuleNode {} unsafe impl Sync for RuleNode {}
impl RuleNode { impl RuleNode {
pub fn new(yaml: Yaml) -> RuleNode { pub fn new(rulepath: String, yaml: Yaml) -> RuleNode {
return RuleNode { return RuleNode {
rulepath: rulepath,
yaml: yaml, yaml: yaml,
detection: Option::None, detection: Option::None,
countdata: HashMap::new(), countdata: HashMap::new(),
@@ -321,7 +323,7 @@ mod tests {
assert_eq!(rule_yaml.is_ok(), true); assert_eq!(rule_yaml.is_ok(), true);
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(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_eq!(rule_node.init().is_ok(), true);
return rule_node; return rule_node;
} }
@@ -877,7 +879,7 @@ mod tests {
output: 'Rule parse test' output: 'Rule parse test'
"#; "#;
let mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter(); 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!( assert_eq!(
rule_node.init(), rule_node.init(),
@@ -897,7 +899,7 @@ mod tests {
output: 'Rule parse test' output: 'Rule parse test'
"#; "#;
let mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter(); 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!( assert_eq!(
rule_node.init(), rule_node.init(),
@@ -909,7 +911,7 @@ mod tests {
fn _check_count(rule_str: &str, record_str: &str, key: &str, expect_count: i32) { 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 mut rule_yaml = YamlLoader::load_from_str(rule_str).unwrap().into_iter();
let test = rule_yaml.next().unwrap(); 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(); let _init = rule_node.init();
match serde_json::from_str(record_str) { match serde_json::from_str(record_str) {
Ok(record) => { Ok(record) => {

View File

@@ -9,7 +9,7 @@ use std::path::{Path, PathBuf};
use yaml_rust::YamlLoader; use yaml_rust::YamlLoader;
pub struct ParseYaml { pub struct ParseYaml {
pub files: Vec<yaml_rust::Yaml>, pub files: Vec<(String, yaml_rust::Yaml)>,
} }
impl ParseYaml { impl ParseYaml {
@@ -46,7 +46,9 @@ impl ParseYaml {
if i["ignore"].as_bool().unwrap_or(false) { if i["ignore"].as_bool().unwrap_or(false) {
continue; continue;
} }
&self.files.push(i); &self
.files
.push((format!("{}", entry.path().display()), i));
} }
} }
Err(e) => { Err(e) => {