This commit is contained in:
fukusuket
2025-03-14 17:49:38 +09:00
parent 1b9a3fd861
commit 5c0c851a37
2 changed files with 52 additions and 56 deletions

View File

@@ -60,13 +60,6 @@ $usableCounts = $usableRules | Group-Object -Property level | ForEach-Object {
}
}
$unusableCounts = $unusableRules | Group-Object -Property level | ForEach-Object {
[PSCustomObject]@{
Level = $_.Name
Count = $_.Count
}
}
# Step 5: Calculate the percentages
$usablePercentages = $usableCounts | ForEach-Object {
$total = ($totalCounts | Where-Object Level -match $PSItem.Level | Select-Object -ExpandProperty Count)[0]
@@ -78,16 +71,6 @@ $usablePercentages = $usableCounts | ForEach-Object {
}
}
$unusablePercentages = $unusableCounts | ForEach-Object {
$total = ($totalCounts | Where-Object Level -match $PSItem.Level | Select-Object -ExpandProperty Count)[0]
[PSCustomObject]@{
Level = $PSItem.Level
UnusableCount = $PSItem.Count
TotalCount = $total
Percentage = "{0:N2}" -f ($PSItem.Count / $total * 100)
}
}
# Step 6: Generate the required outputtotal
$customOrder = @("critical", "high", "medium", "low", "informational")
Write-Output "Detection rules that can be used on this system versus total possible rules:"
@@ -96,12 +79,6 @@ $usablePercentages | ForEach-Object {
Write-Output "$($_.Level) rules: $($_.UsableCount) / $($_.TotalCount) ($($_.Percentage)%)"
}
Write-Output ""
Write-Output "Detection rules that cannot be used on this system:"
$unusablePercentages = $unusablePercentages | Sort-Object { $customOrder.IndexOf($_.Level) }
$unusablePercentages | ForEach-Object {
Write-Output "$($_.Level) rules: $($_.UnusableCount) / $($_.TotalCount) ($($_.Percentage)%)"
}
Write-Output ""
Write-Output "Usable detection rules list saved to: UsableRules.csv"
Write-Output "Unusable detection rules list saved to: UnusableRules.csv"
Write-Output ""

View File

@@ -2,11 +2,26 @@ use csv::ReaderBuilder;
use serde_json::{Value, json};
use std::collections::HashSet;
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::fs::write;
use std::{env, fs};
use walkdir::WalkDir;
use yaml_rust2::{Yaml, YamlLoader};
enum Channel {
Security,
PowerShell,
}
impl Display for Channel {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Channel::Security => write!(f, "sec"),
Channel::PowerShell => write!(f, "pwsh"),
}
}
}
fn list_yml_files(dir: &str) -> Vec<String> {
let mut yml_files = Vec::new();
for entry in WalkDir::new(dir).into_iter().filter_map(|e| e.ok()) {
@@ -58,58 +73,62 @@ fn extract_event_ids(yaml: &Yaml, event_ids: &mut HashSet<String>) {
}
}
fn contains_channel_security(yaml: &Yaml) -> bool {
fn contains_channel_security(yaml: &Yaml) -> Option<Channel> {
match yaml {
Yaml::Hash(hash) => {
for (key, value) in hash {
if key.as_str() == Some("Channel") && value.as_str() == Some("Security") {
return true;
}
if contains_channel_security(value) {
return true;
if key.as_str() == Some("Channel") {
match value.as_str() {
Some("Security") => return Some(Channel::Security),
Some("Microsoft-Windows-PowerShell/Operational")
| Some("PowerShellCore/Operational")
| Some("Windows PowerShell") => return Some(Channel::PowerShell),
_ => None::<Channel>,
};
} else if let Some(channel) = contains_channel_security(value) {
return Some(channel);
}
}
}
Yaml::Array(array) => {
for item in array {
if contains_channel_security(item) {
return true;
if let Some(channel) = contains_channel_security(item) {
return Some(channel);
}
}
}
_ => {}
}
false
None
}
fn parse_yaml(doc: Yaml, eid_subcategory_pair: &Vec<(String, String)>) -> Option<Value> {
if !contains_channel_security(&doc["detection"]) {
return None;
}
let uuid = doc["id"].as_str().unwrap_or("");
let title = doc["title"].as_str().unwrap_or("");
let desc = doc["description"].as_str().unwrap_or("");
let level = doc["level"].as_str().unwrap_or("");
let mut event_ids = HashSet::new();
let mut subcategories = HashSet::new();
extract_event_ids(&doc, &mut event_ids);
for event_id in &event_ids {
for (eid, subcategory) in eid_subcategory_pair {
if eid == event_id {
subcategories.insert(subcategory.clone());
if let Some(ch) = contains_channel_security(&doc["detection"]) {
let uuid = doc["id"].as_str().unwrap_or("");
let title = doc["title"].as_str().unwrap_or("");
let level = doc["level"].as_str().unwrap_or("");
let mut event_ids = HashSet::new();
let mut subcategories = HashSet::new();
extract_event_ids(&doc, &mut event_ids);
for event_id in &event_ids {
for (eid, subcategory) in eid_subcategory_pair {
if eid == event_id {
subcategories.insert(subcategory.clone());
}
}
}
let event_ids: Vec<String> = event_ids.into_iter().collect();
let subcategories: Vec<String> = subcategories.into_iter().collect();
return Some(json!({
"id": uuid,
"title": title,
"channel": ch.to_string(),
"level": level,
"event_ids": event_ids,
"subcategory_guids": subcategories
}));
}
let event_ids: Vec<String> = event_ids.into_iter().collect();
let subcategories: Vec<String> = subcategories.into_iter().collect();
Some(json!({
"id": uuid,
"title": title,
"description": desc,
"level": level,
"event_ids": event_ids,
"subcategory_guids": subcategories
}))
None
}
fn load_event_id_guid_pairs(file_path: &str) -> Result<Vec<(String, String)>, Box<dyn Error>> {