under constructing
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
/samples
|
/samples
|
||||||
*.test
|
*.test
|
||||||
|
/.vscode/
|
||||||
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1192,6 +1192,7 @@ dependencies = [
|
|||||||
"evtx",
|
"evtx",
|
||||||
"flate2",
|
"flate2",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"linked-hash-map",
|
||||||
"quick-xml 0.17.2",
|
"quick-xml 0.17.2",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ flate2 = "1.0"
|
|||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
yaml-rust = "0.4"
|
yaml-rust = "0.4"
|
||||||
|
linked-hash-map = "0.5.3"
|
||||||
|
|
||||||
[target.x86_64-pc-windows-gnu]
|
[target.x86_64-pc-windows-gnu]
|
||||||
linker = "x86_64-w64-mingw32-gcc"
|
linker = "x86_64-w64-mingw32-gcc"
|
||||||
|
|||||||
12
config/eventkey_alias.txt
Normal file
12
config/eventkey_alias.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
alias,event_key
|
||||||
|
EventID,Event.System.EventId
|
||||||
|
Channel,Event.System.Channel
|
||||||
|
CommandLine,Event.EventData.CommandLine
|
||||||
|
Signed,Event.EventData.Signed
|
||||||
|
ProcessName,Event.EventData.ProcessName
|
||||||
|
AccessMask,Event.EventData.AccessMask
|
||||||
|
TargetUserName,Event.EventData.TargetUserName
|
||||||
|
param1,Event.EventData.param1
|
||||||
|
param2,Event.EventData.param2
|
||||||
|
ServiceName,Event.EventData.ServiceName
|
||||||
|
ImagePath,Event.EventData.ImagePath
|
||||||
20
rules/deep_blue_cli/powershell/4103.yml
Normal file
20
rules/deep_blue_cli/powershell/4103.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
title: PowerShell Execution Pipeline
|
||||||
|
description: hogehoge
|
||||||
|
enabled: true
|
||||||
|
author: Yea
|
||||||
|
logsource:
|
||||||
|
product: windows
|
||||||
|
detection:
|
||||||
|
selection:
|
||||||
|
EventLog: PowerShell
|
||||||
|
EventID: 4103
|
||||||
|
ContextInfo:
|
||||||
|
- Host Application
|
||||||
|
- ホスト アプリケーション
|
||||||
|
condition: selection
|
||||||
|
falsepositives:
|
||||||
|
- unknown
|
||||||
|
level: medium
|
||||||
|
output: 'command=%CommandLine%'
|
||||||
|
creation_date: 2020/11/8
|
||||||
|
uodated_date: 2020/11/8
|
||||||
19
rules/deep_blue_cli/powershell/4104.yml
Normal file
19
rules/deep_blue_cli/powershell/4104.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
title: PowerShell Execution Remote Command
|
||||||
|
description: hogehoge
|
||||||
|
enabled: true
|
||||||
|
author: Yea
|
||||||
|
logsource:
|
||||||
|
product: windows
|
||||||
|
detection:
|
||||||
|
selection:
|
||||||
|
EventLog: PowerShell
|
||||||
|
EventID: 4104
|
||||||
|
Path: ''
|
||||||
|
ScriptBlockText: '.'
|
||||||
|
condition: selection
|
||||||
|
falsepositives:
|
||||||
|
- unknown
|
||||||
|
level: medium
|
||||||
|
output: 'command=%CommandLine%'
|
||||||
|
creation_date: 2020/11/8
|
||||||
|
uodated_date: 2020/11/8
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
[rule]
|
|
||||||
severity = "high"
|
|
||||||
name = "4103"
|
|
||||||
message = "Execute Pipeline"
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
[rule]
|
|
||||||
severity = "high"
|
|
||||||
name = "4104"
|
|
||||||
message = "Excute Remote Command"
|
|
||||||
@@ -2,12 +2,12 @@ use clap::{App, AppSettings, Arg, ArgMatches};
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SingletonReader {
|
pub struct SingletonReader {
|
||||||
pub regex: Vec<Vec<String>>,
|
|
||||||
pub whitelist: Vec<Vec<String>>,
|
|
||||||
pub args: ArgMatches<'static>,
|
pub args: ArgMatches<'static>,
|
||||||
|
pub event_key_alias_config: EventKeyAliasConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn singleton() -> Box<SingletonReader> {
|
pub fn singleton() -> Box<SingletonReader> {
|
||||||
@@ -17,9 +17,8 @@ pub fn singleton() -> Box<SingletonReader> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
ONCE.call_once(|| {
|
ONCE.call_once(|| {
|
||||||
let singleton = SingletonReader {
|
let singleton = SingletonReader {
|
||||||
regex: read_csv("regexes.txt"),
|
|
||||||
whitelist: read_csv("whitelist.txt"),
|
|
||||||
args: build_app().get_matches(),
|
args: build_app().get_matches(),
|
||||||
|
event_key_alias_config: load_eventkey_alias(),
|
||||||
};
|
};
|
||||||
|
|
||||||
SINGLETON = Some(Box::new(singleton));
|
SINGLETON = Some(Box::new(singleton));
|
||||||
@@ -56,6 +55,41 @@ fn build_app() -> clap::App<'static, 'static> {
|
|||||||
.arg(Arg::from_usage("--credits 'Zachary Mathis, Akira Nishikawa'"))
|
.arg(Arg::from_usage("--credits 'Zachary Mathis, Akira Nishikawa'"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct EventKeyAliasConfig {
|
||||||
|
key_to_eventkey: HashMap<String,String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventKeyAliasConfig {
|
||||||
|
pub fn new() -> EventKeyAliasConfig {
|
||||||
|
return EventKeyAliasConfig{ key_to_eventkey: HashMap::new() };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_event_key(&self, alias: String ) -> Option<&String> {
|
||||||
|
return self.key_to_eventkey.get(&alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_eventkey_alias() -> EventKeyAliasConfig {
|
||||||
|
let config = EventKeyAliasConfig::new();
|
||||||
|
|
||||||
|
read_csv("config/eventkey_alias.txt").into_iter().for_each( | line| {
|
||||||
|
if line.len() != 2 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let alias = line[0];
|
||||||
|
let event_key = line[1];
|
||||||
|
if alias.len() == 0 || event_key.len() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.key_to_eventkey.insert(alias, event_key);
|
||||||
|
});
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
fn read_csv(filename: &str) -> Vec<Vec<String>> {
|
fn read_csv(filename: &str) -> Vec<Vec<String>> {
|
||||||
let mut f = File::open(filename).expect("file not found!!!");
|
let mut f = File::open(filename).expect("file not found!!!");
|
||||||
let mut contents: String = String::new();
|
let mut contents: String = String::new();
|
||||||
|
|||||||
@@ -1,74 +1,78 @@
|
|||||||
extern crate csv;
|
extern crate csv;
|
||||||
extern crate quick_xml;
|
extern crate chrono;
|
||||||
|
|
||||||
use crate::detections::application;
|
use crate::detections::rule;
|
||||||
use crate::detections::applocker;
|
use crate::detections::rule::RuleNode;
|
||||||
use crate::detections::common;
|
use crate::detections::print::{Message};
|
||||||
use crate::detections::powershell;
|
use crate::yaml::ParseYaml;
|
||||||
use crate::detections::security;
|
|
||||||
use crate::detections::sysmon;
|
use chrono::{TimeZone, Utc};
|
||||||
use crate::detections::system;
|
|
||||||
use crate::models::event;
|
|
||||||
use evtx::EvtxParser;
|
use evtx::EvtxParser;
|
||||||
use quick_xml::de::DeError;
|
use serde_json::{Error, Value};
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
const DIRPATH_RULES: &str = "rules";
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Detection {
|
pub struct Detection {
|
||||||
timeline_list: BTreeMap<String, String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Detection {
|
impl Detection {
|
||||||
pub fn new() -> Detection {
|
pub fn new() -> Detection {
|
||||||
Detection {
|
Detection {
|
||||||
timeline_list: BTreeMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&mut self, mut parser: EvtxParser<std::fs::File>) -> Result<(), DeError> {
|
pub fn start(&mut self, mut parser: EvtxParser<std::fs::File>) {
|
||||||
let mut common: common::Common = common::Common::new();
|
// from .etvx to json
|
||||||
let mut security = security::Security::new();
|
let event_records: Vec<Value> = parser
|
||||||
let mut system = system::System::new();
|
.records_json()
|
||||||
let mut application = application::Application::new();
|
.filter_map(|result_record| {
|
||||||
let mut applocker = applocker::AppLocker::new();
|
if result_record.is_err() {
|
||||||
let mut sysmon = sysmon::Sysmon::new();
|
eprintln!("{}", result_record.unwrap_err());
|
||||||
let mut powershell = powershell::PowerShell::new();
|
return Option::None;
|
||||||
|
|
||||||
for record in parser.records() {
|
|
||||||
match record {
|
|
||||||
Ok(r) => {
|
|
||||||
let event: event::Evtx = quick_xml::de::from_str(&r.data)?;
|
|
||||||
let event_id = event.system.event_id.to_string();
|
|
||||||
let channel = event.system.channel.to_string();
|
|
||||||
let event_data = event.parse_event_data();
|
|
||||||
|
|
||||||
&common.detection(&event.system, &event_data);
|
|
||||||
if channel == "Security" {
|
|
||||||
&security.detection(event_id, &event.system, &event.user_data, event_data);
|
|
||||||
} else if channel == "System" {
|
|
||||||
&system.detection(event_id, &event.system, event_data);
|
|
||||||
} else if channel == "Application" {
|
|
||||||
&application.detection(event_id, &event.system, event_data);
|
|
||||||
} else if channel == "Microsoft-Windows-PowerShell/Operational" {
|
|
||||||
&powershell.detection(event_id, &event.system, event_data);
|
|
||||||
} else if channel == "Microsoft-Windows-Sysmon/Operational" {
|
|
||||||
&sysmon.detection(event_id, &event.system, event_data);
|
|
||||||
} else if channel == "Microsoft-Windows-AppLocker/EXE and DLL" {
|
|
||||||
&applocker.detection(event_id, &event.system, event_data);
|
|
||||||
} else {
|
|
||||||
//&other.detection();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => eprintln!("{}", e),
|
|
||||||
}
|
//// refer https://rust-lang-nursery.github.io/rust-cookbook/encoding/complex.html
|
||||||
|
let result_json: Result<Value, Error> =
|
||||||
|
serde_json::from_str(&result_record.unwrap().data);
|
||||||
|
if result_json.is_err() {
|
||||||
|
eprintln!("{}", result_json.unwrap_err());
|
||||||
|
return Option::None;
|
||||||
|
}
|
||||||
|
return result_json.ok();
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
event_records.iter().for_each(|event_rec| {
|
||||||
|
println!("{}", event_rec["Event"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// load rule files
|
||||||
|
let mut rulefile_loader = ParseYaml::new();
|
||||||
|
let resutl_readdir = rulefile_loader.read_dir(DIRPATH_RULES);
|
||||||
|
if resutl_readdir.is_err() {
|
||||||
|
eprintln!("{}", resutl_readdir.unwrap_err());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////
|
// parse rule files
|
||||||
// 表示
|
let rules: Vec<RuleNode> = rulefile_loader
|
||||||
////////////////////////////
|
.files
|
||||||
common.disp();
|
.into_iter()
|
||||||
security.disp();
|
.map(|rule_file| rule::parse_rule(rule_file))
|
||||||
|
.collect();
|
||||||
|
|
||||||
return Ok(());
|
// selection rule files and collect log
|
||||||
|
let mut message = Message::new();
|
||||||
|
rules.iter().for_each(|rule| {
|
||||||
|
&event_records
|
||||||
|
.iter()
|
||||||
|
.filter(|event_record| rule.detection.select(event_record))
|
||||||
|
.for_each(|event_record| message.insert(Utc.ymd(1996, 2, 27).and_hms(1, 5, 1), event_record.to_string()));
|
||||||
|
});
|
||||||
|
|
||||||
|
// output message
|
||||||
|
message.debug();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ pub mod configs;
|
|||||||
pub mod detection;
|
pub mod detection;
|
||||||
mod powershell;
|
mod powershell;
|
||||||
pub mod print;
|
pub mod print;
|
||||||
|
mod rule;
|
||||||
mod security;
|
mod security;
|
||||||
mod sysmon;
|
mod sysmon;
|
||||||
mod system;
|
mod system;
|
||||||
|
|||||||
204
src/detections/rule.rs
Normal file
204
src/detections/rule.rs
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
use serde_json::Value;
|
||||||
|
use yaml_rust::Yaml;
|
||||||
|
use crate::detections::configs;
|
||||||
|
|
||||||
|
pub fn parse_rule(yaml: Yaml) -> RuleNode {
|
||||||
|
let selection = parse_selection(&yaml);
|
||||||
|
return RuleNode {
|
||||||
|
yaml: yaml,
|
||||||
|
detection: DetectionNode {
|
||||||
|
selection: selection,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_selection(yaml: &Yaml) -> Option<Box<dyn SelectionNode>> {
|
||||||
|
let selection_yaml = &yaml["detection"]["selection"];
|
||||||
|
return Option::Some(parse_selection_recursively(vec![], &selection_yaml));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_selection_recursively(mut key_list: Vec<String>, yaml: &Yaml) -> Box<dyn SelectionNode> {
|
||||||
|
if yaml.as_hash().is_some() {
|
||||||
|
let yaml_hash = yaml.as_hash().unwrap();
|
||||||
|
let mut and_node = AndSelectionNode::new();
|
||||||
|
|
||||||
|
yaml_hash.keys().for_each(|hash_key| {
|
||||||
|
let child_yaml = yaml_hash.get(hash_key).unwrap();
|
||||||
|
let mut child_key_list = key_list.clone();
|
||||||
|
child_key_list.push(hash_key.as_str().unwrap().to_string());
|
||||||
|
let child_node = parse_selection_recursively(child_key_list, child_yaml);
|
||||||
|
and_node.child_nodes.push(child_node);
|
||||||
|
});
|
||||||
|
return Box::new(and_node);
|
||||||
|
} else if yaml.as_vec().is_some() {
|
||||||
|
let mut or_node = OrSelectionNode::new();
|
||||||
|
yaml.as_vec().unwrap().iter().for_each(|child_yaml| {
|
||||||
|
let child_node = parse_selection_recursively(key_list.clone(), child_yaml);
|
||||||
|
or_node.child_nodes.push(child_node);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Box::new(or_node);
|
||||||
|
} else {
|
||||||
|
return Box::new(FieldSelectionNode::new(key_list, yaml.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////// RuleNode
|
||||||
|
pub struct RuleNode {
|
||||||
|
pub yaml: Yaml,
|
||||||
|
pub detection: DetectionNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////// Detection Node
|
||||||
|
pub struct DetectionNode {
|
||||||
|
selection: Option<Box<dyn SelectionNode>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DetectionNode {
|
||||||
|
pub fn select(&self, event_record: &Value) -> bool {
|
||||||
|
if self.selection.is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.selection.as_ref().unwrap().select(event_record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////// Selection Node
|
||||||
|
trait SelectionNode {
|
||||||
|
fn select(&self, event_record: &Value) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////// AndSelectionNode
|
||||||
|
struct AndSelectionNode {
|
||||||
|
pub child_nodes: Vec<Box<dyn SelectionNode>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AndSelectionNode {
|
||||||
|
pub fn new() -> AndSelectionNode {
|
||||||
|
return AndSelectionNode {
|
||||||
|
child_nodes: vec![],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelectionNode for AndSelectionNode {
|
||||||
|
fn select(&self, event_record: &Value) -> bool {
|
||||||
|
return self.child_nodes.iter().all(|child_node| {
|
||||||
|
return child_node.as_ref().select(event_record);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////// OrSelectionNode
|
||||||
|
struct OrSelectionNode {
|
||||||
|
pub child_nodes: Vec<Box<dyn SelectionNode>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OrSelectionNode {
|
||||||
|
pub fn new() -> OrSelectionNode {
|
||||||
|
return OrSelectionNode {
|
||||||
|
child_nodes: vec![],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelectionNode for OrSelectionNode {
|
||||||
|
fn select(&self, event_record: &Value) -> bool {
|
||||||
|
return self.child_nodes.iter().any(|child_node| {
|
||||||
|
return child_node.as_ref().select(event_record);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////// Field Selection Node
|
||||||
|
struct FieldSelectionNode {
|
||||||
|
key_list: Vec<String>,
|
||||||
|
select_value: Yaml,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FieldSelectionNode {
|
||||||
|
fn new(key_list: Vec<String>, value_yaml: Yaml) -> FieldSelectionNode {
|
||||||
|
return FieldSelectionNode {
|
||||||
|
key_list: key_list,
|
||||||
|
select_value: value_yaml,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON形式のEventJSONから値を取得する関数 aliasも考慮されている。
|
||||||
|
// TODO Messageを出力する際も利用するので、共通して使えるようにrefactoringする。
|
||||||
|
fn get_event_value<'a>(&self, event_value: &'a Value) -> Option<&'a Value> {
|
||||||
|
if self.key_list.is_empty() {
|
||||||
|
return Option::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key: &str = &self.key_list[0];
|
||||||
|
if key.len() == 0 {
|
||||||
|
return Option::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let event_key = match configs::singleton().event_key_alias_config.get_event_key(key.to_string()) {
|
||||||
|
Some(alias_event_key) => { alias_event_key }
|
||||||
|
None => { key }
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut ret: &Value = event_value;
|
||||||
|
for key in event_key.split(".") {
|
||||||
|
if ret.is_object() == false {
|
||||||
|
return Option::None;
|
||||||
|
}
|
||||||
|
ret = &ret[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Option::Some(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Matcherのインスタンスが都度生成されないようにする。
|
||||||
|
fn get_matchers(&self) -> Vec<Box<dyn FieldSelectionMatcher>> {
|
||||||
|
return vec![Box::new(ValueMatcher {})];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelectionNode for FieldSelectionNode {
|
||||||
|
fn select(&self, event_record: &Value) -> bool {
|
||||||
|
let matchers = self.get_matchers();
|
||||||
|
let matcher = matchers
|
||||||
|
.into_iter()
|
||||||
|
.find(|matcher| matcher.is_target_key(&self.key_list));
|
||||||
|
if matcher.is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let event_value = self.get_event_value(event_record);
|
||||||
|
return matcher
|
||||||
|
.unwrap()
|
||||||
|
.is_match(&self.key_list, &self.select_value, event_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait FieldSelectionMatcher {
|
||||||
|
fn is_target_key(&self, key_list: &Vec<String>) -> bool;
|
||||||
|
fn is_match(
|
||||||
|
&self,
|
||||||
|
key_list: &Vec<String>,
|
||||||
|
select_value: &Yaml,
|
||||||
|
event_value: Option<&Value>,
|
||||||
|
) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueMatcher {}
|
||||||
|
|
||||||
|
impl FieldSelectionMatcher for ValueMatcher {
|
||||||
|
fn is_target_key(&self, key_list: &Vec<String>) -> bool {
|
||||||
|
return key_list.is_empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_match(
|
||||||
|
&self,
|
||||||
|
key_list: &Vec<String>,
|
||||||
|
select_value: &Yaml,
|
||||||
|
event_value: Option<&Value>,
|
||||||
|
) -> bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,12 +8,12 @@ use std::path::{Path, PathBuf};
|
|||||||
use yaml_rust::YamlLoader;
|
use yaml_rust::YamlLoader;
|
||||||
|
|
||||||
pub struct ParseYaml {
|
pub struct ParseYaml {
|
||||||
pub rules: Vec<yaml_rust::Yaml>,
|
pub files: Vec<yaml_rust::Yaml>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParseYaml {
|
impl ParseYaml {
|
||||||
pub fn new() -> ParseYaml {
|
pub fn new() -> ParseYaml {
|
||||||
ParseYaml { rules: Vec::new() }
|
ParseYaml { files: Vec::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_file(&self, path: PathBuf) -> Result<String, String> {
|
pub fn read_file(&self, path: PathBuf) -> Result<String, String> {
|
||||||
@@ -39,7 +39,7 @@ impl ParseYaml {
|
|||||||
let docs = YamlLoader::load_from_str(&s).unwrap();
|
let docs = YamlLoader::load_from_str(&s).unwrap();
|
||||||
for i in docs {
|
for i in docs {
|
||||||
if i["enabled"].as_bool().unwrap() {
|
if i["enabled"].as_bool().unwrap() {
|
||||||
&self.rules.push(i);
|
&self.files.push(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ mod tests {
|
|||||||
fn test_read_yaml() {
|
fn test_read_yaml() {
|
||||||
let mut yaml = yaml::ParseYaml::new();
|
let mut yaml = yaml::ParseYaml::new();
|
||||||
&yaml.read_dir("test_files/rules/yaml/".to_string());
|
&yaml.read_dir("test_files/rules/yaml/".to_string());
|
||||||
for rule in yaml.rules {
|
for rule in yaml.files {
|
||||||
if rule["title"].as_str().unwrap() == "Sysmon Check command lines" {
|
if rule["title"].as_str().unwrap() == "Sysmon Check command lines" {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"*",
|
"*",
|
||||||
|
|||||||
Reference in New Issue
Block a user