Bugfix/yml alias not found all data output#227 (#241)
* removed no use alias #227 * changed case of object type return none #227 - serde json value is object type when alias key dont exist in detected record. * adjust serde_number_to_string function return value change #227 * adjust yml rule to change of aliaskey_alias.txt #227 * merged same regex as static * create new struct to reduce same output in rule and keyword warn message #227 * changed output position * removed regression warnings #227 * removed output wanring * Fixed a possible panic when None. #227 * added parse_message test #227 * added get_serde_number_to_string tests #227 * removed unnecessary test data part in get_serde_numuber_to_string test #227
This commit is contained in:
@@ -126,7 +126,7 @@ SubjectDomainName,Event.EventData.SubjectDomainName
|
|||||||
SubjectLogonId,Event.EventData.SubjectLogonId
|
SubjectLogonId,Event.EventData.SubjectLogonId
|
||||||
SubjectUserName,Event.EventData.SubjectUserName
|
SubjectUserName,Event.EventData.SubjectUserName
|
||||||
SubjectUserSid,Event.EventData.SubjectUserSid
|
SubjectUserSid,Event.EventData.SubjectUserSid
|
||||||
TargetDomainName,Event.EventData.TargetDomainName
|
TargetDomainName,Event.EventData.TargetDomainName
|
||||||
TargetFilename,Event.EventData.TargetFilename
|
TargetFilename,Event.EventData.TargetFilename
|
||||||
TargetImage,Event.EventData.TargetImage
|
TargetImage,Event.EventData.TargetImage
|
||||||
TargetLogonId,Event.EventData.TargetLogonId
|
TargetLogonId,Event.EventData.TargetLogonId
|
||||||
|
|||||||
@@ -206,7 +206,9 @@ impl Detection {
|
|||||||
record_info.record["Event"]["System"]["Computer"]
|
record_info.record["Event"]["System"]["Computer"]
|
||||||
.to_string()
|
.to_string()
|
||||||
.replace("\"", ""),
|
.replace("\"", ""),
|
||||||
get_serde_number_to_string(&record_info.record["Event"]["System"]["EventID"]),
|
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["title"].as_str().unwrap_or("").to_string(),
|
||||||
rule.yaml["output"].as_str().unwrap_or("").to_string(),
|
rule.yaml["output"].as_str().unwrap_or("").to_string(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ pub struct AlertMessage {}
|
|||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref MESSAGES: Mutex<Message> = Mutex::new(Message::new());
|
pub static ref MESSAGES: Mutex<Message> = Mutex::new(Message::new());
|
||||||
|
pub static ref ALIASREGEX: Regex = Regex::new(r"%[a-zA-Z0-9-_]+%").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
@@ -101,8 +102,7 @@ impl Message {
|
|||||||
fn parse_message(&mut self, event_record: &Value, output: String) -> String {
|
fn parse_message(&mut self, event_record: &Value, output: String) -> String {
|
||||||
let mut return_message: String = output;
|
let mut return_message: String = output;
|
||||||
let mut hash_map: HashMap<String, String> = HashMap::new();
|
let mut hash_map: HashMap<String, String> = HashMap::new();
|
||||||
let re = Regex::new(r"%[a-zA-Z0-9-_]+%").unwrap();
|
for caps in ALIASREGEX.captures_iter(&return_message) {
|
||||||
for caps in re.captures_iter(&return_message) {
|
|
||||||
let full_target_str = &caps[0];
|
let full_target_str = &caps[0];
|
||||||
let target_length = full_target_str.chars().count() - 2; // The meaning of 2 is two percent
|
let target_length = full_target_str.chars().count() - 2; // The meaning of 2 is two percent
|
||||||
let target_str = full_target_str
|
let target_str = full_target_str
|
||||||
@@ -118,16 +118,20 @@ impl Message {
|
|||||||
.get_event_key(target_str.to_string())
|
.get_event_key(target_str.to_string())
|
||||||
{
|
{
|
||||||
let split: Vec<&str> = array_str.split(".").collect();
|
let split: Vec<&str> = array_str.split(".").collect();
|
||||||
|
let mut is_exist_event_key = false;
|
||||||
let mut tmp_event_record: &Value = event_record.into();
|
let mut tmp_event_record: &Value = event_record.into();
|
||||||
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;
|
||||||
tmp_event_record = record;
|
tmp_event_record = record;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hash_map.insert(
|
if is_exist_event_key {
|
||||||
full_target_str.to_string(),
|
let hash_value = get_serde_number_to_string(tmp_event_record);
|
||||||
get_serde_number_to_string(tmp_event_record),
|
if hash_value.is_some() {
|
||||||
);
|
hash_map.insert(full_target_str.to_string(), hash_value.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,4 +336,87 @@ mod tests {
|
|||||||
let mut stdout = stdout.lock();
|
let mut stdout = stdout.lock();
|
||||||
AlertMessage::alert(&mut stdout, input.to_string()).expect("[WARN] TESTWarn!");
|
AlertMessage::alert(&mut stdout, input.to_string()).expect("[WARN] TESTWarn!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// outputで指定されているキー(eventkey_alias.txt内で設定済み)から対象のレコード内の情報でメッセージをパースしているか確認する関数
|
||||||
|
fn test_parse_message() {
|
||||||
|
let mut message = Message::new();
|
||||||
|
let json_str = r##"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"EventData": {
|
||||||
|
"CommandLine": "parsetest1"
|
||||||
|
},
|
||||||
|
"System": {
|
||||||
|
"Computer": "testcomputer1",
|
||||||
|
"TimeCreated_attributes": {
|
||||||
|
"SystemTime": "1996-02-27T01:05:01Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
let event_record: Value = serde_json::from_str(json_str).unwrap();
|
||||||
|
let expected = "commandline:parsetest1 computername:testcomputer1";
|
||||||
|
assert_eq!(
|
||||||
|
message.parse_message(
|
||||||
|
&event_record,
|
||||||
|
"commandline:%CommandLine% computername:%ComputerName%".to_owned()
|
||||||
|
),
|
||||||
|
expected,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
/// outputで指定されているキーが、eventkey_alias.txt内で設定されていない場合の出力テスト
|
||||||
|
fn test_parse_message_not_exist_key_in_output() {
|
||||||
|
let mut message = Message::new();
|
||||||
|
let json_str = r##"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"EventData": {
|
||||||
|
"CommandLine": "parsetest2"
|
||||||
|
},
|
||||||
|
"System": {
|
||||||
|
"TimeCreated_attributes": {
|
||||||
|
"SystemTime": "1996-02-27T01:05:01Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
let event_record: Value = serde_json::from_str(json_str).unwrap();
|
||||||
|
let expected = "NoExistKey:%TESTNoExistKey%";
|
||||||
|
assert_eq!(
|
||||||
|
message.parse_message(&event_record, "NoExistKey:%TESTNoExistKey%".to_owned()),
|
||||||
|
expected,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
/// outputで指定されているキー(eventkey_alias.txt内で設定済み)が対象のレコード内に該当する情報がない場合の出力テスト
|
||||||
|
fn test_parse_message_not_exist_value_in_record() {
|
||||||
|
let mut message = Message::new();
|
||||||
|
let json_str = r##"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"EventData": {
|
||||||
|
"CommandLine": "parsetest3"
|
||||||
|
},
|
||||||
|
"System": {
|
||||||
|
"TimeCreated_attributes": {
|
||||||
|
"SystemTime": "1996-02-27T01:05:01Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
let event_record: Value = serde_json::from_str(json_str).unwrap();
|
||||||
|
let expected = "commandline:parsetest3 computername:%ComputerName%";
|
||||||
|
assert_eq!(
|
||||||
|
message.parse_message(
|
||||||
|
&event_record,
|
||||||
|
"commandline:%CommandLine% computername:%ComputerName%".to_owned()
|
||||||
|
),
|
||||||
|
expected,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,11 +94,14 @@ pub fn get_event_id_key() -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// serde:Valueの型を確認し、文字列を返します。
|
/// serde:Valueの型を確認し、文字列を返します。
|
||||||
pub fn get_serde_number_to_string(value: &serde_json::Value) -> String {
|
pub fn get_serde_number_to_string(value: &serde_json::Value) -> Option<String> {
|
||||||
if value.is_string() {
|
if value.is_string() {
|
||||||
return value.as_str().unwrap_or("").to_string();
|
return Option::Some(value.as_str().unwrap_or("").to_string());
|
||||||
|
} else if value.is_object() {
|
||||||
|
// Object type is not specified record value.
|
||||||
|
return Option::None;
|
||||||
} else {
|
} else {
|
||||||
return value.to_string();
|
return Option::Some(value.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +166,7 @@ pub fn create_tokio_runtime() -> Runtime {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::detections::utils;
|
use crate::detections::utils;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_regex() {
|
fn test_check_regex() {
|
||||||
@@ -191,4 +195,62 @@ mod tests {
|
|||||||
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!(false == utils::check_allowlist(commandline, &allowlist));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Serde::Valueの数値型の値を文字列として返却することを確かめるテスト
|
||||||
|
fn test_get_serde_number_to_string() {
|
||||||
|
let json_str = r##"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"System": {
|
||||||
|
"EventID": 11111
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
let event_record: Value = serde_json::from_str(json_str).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
utils::get_serde_number_to_string(&event_record["Event"]["System"]["EventID"]).unwrap(),
|
||||||
|
"11111".to_owned()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Serde::Valueの文字列型の値を文字列として返却することを確かめるテスト
|
||||||
|
fn test_get_serde_number_serde_string_to_string() {
|
||||||
|
let json_str = r##"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"EventData": {
|
||||||
|
"ComputerName": "HayabusaComputer1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
let event_record: Value = serde_json::from_str(json_str).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
utils::get_serde_number_to_string(&event_record["Event"]["EventData"]["ComputerName"])
|
||||||
|
.unwrap(),
|
||||||
|
"HayabusaComputer1".to_owned()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Serde::Valueのオブジェクト型の内容を誤って渡した際にNoneを返却することを確かめるテスト
|
||||||
|
fn test_get_serde_number_serde_object_ret_none() {
|
||||||
|
let json_str = r##"
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"EventData": {
|
||||||
|
"ComputerName": "HayabusaComputer1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
let event_record: Value = serde_json::from_str(json_str).unwrap();
|
||||||
|
|
||||||
|
assert!(utils::get_serde_number_to_string(&event_record["Event"]["EventData"]).is_none());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user