forked from SigmaHQ/pySigma-validators-sigmaHQ
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.py
More file actions
166 lines (143 loc) · 6.82 KB
/
Copy pathconfig.py
File metadata and controls
166 lines (143 loc) · 6.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import json
from pathlib import Path
from typing import Dict, List, Optional
import requests
from sigma.rule import SigmaLogSource
from .data.sigmahq_data import (
file_pattern_version,
ref_sigmahq_fieldsname,
ref_sigmahq_logsource_definition,
ref_sigmahq_logsource_filepattern,
ref_sigmahq_redundant_field,
ref_windows_no_eventid,
ref_windows_provider_name,
taxonomy_version,
windows_version,
)
def core_logsource(source: SigmaLogSource) -> SigmaLogSource:
"""Create a core logsource with product, category and service."""
return SigmaLogSource(product=source.product, category=source.category, service=source.service)
def key_logsource(source: dict) -> str:
"""Generate a unique key for a logsource dictionary."""
product = source.get("product", "none") or "none"
category = source.get("category", "none") or "none"
service = source.get("service", "none") or "none"
return f"{product}_{category}_{service}"
class ConfigHQ:
"""Loads SigmaHQ configuration with support for remote URLs or local files"""
JSON_FOLDER: str = "validator_json"
JSON_NAME_TAXONOMY: str = "sigmahq_taxonomy.json"
JSON_NAME_FILENAME: str = "sigmahq_filename.json"
JSON_NAME_WINDOWS_PROVIDER: str = "sigmahq_windows_validator.json"
DEFAULT_REMOTE_URL: str = (
"https://raw.githubusercontent.com/SigmaHQ/pySigma-validators-sigmaHQ/main/tools"
)
def __init__(self, data_place: Optional[str] = None):
# Initialize with internal reference data
self.taxonomy_version = taxonomy_version
self.sigmahq_redundant_fields = ref_sigmahq_redundant_field
self.sigma_fieldsname = ref_sigmahq_fieldsname
self.sigmahq_logsource_definition = ref_sigmahq_logsource_definition
self.filename_version = file_pattern_version
self.sigmahq_logsource_filepattern = ref_sigmahq_logsource_filepattern
self.windows_version = windows_version
self.windows_provider_name = ref_windows_provider_name
self.windows_no_eventid = ref_windows_no_eventid
# Determine configuration source
self.config_dir: Optional[Path] = None
self.config_url: Optional[str] = None
if data_place is None:
# Prioritize the remote URL by default, with local folder as fallback
self.config_url = self.DEFAULT_REMOTE_URL
default_path = Path.cwd() / self.JSON_FOLDER
if default_path.exists():
# Prioritize local if it exists (for offline work)
self.config_dir = default_path
self.config_url = None
elif data_place.startswith("http://") or data_place.startswith("https://"):
self.config_url = data_place.rstrip("/")
else:
self.config_dir = Path(data_place)
# Try to load configuration from source
if self.config_url is not None or (
self.config_dir is not None and self.config_dir.exists()
):
self._load_sigma_json()
self._load_filename_json()
self._load_windows_provider_json()
def _load_json(self, filename: str) -> Optional[dict]:
"""Load JSON data from either local file or remote URL with error handling."""
if self.config_url:
url = f"{self.config_url}/{filename}"
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
return response.json()
except Exception as e:
print(f"Error loading remote {filename}: {e}")
return None
elif self.config_dir:
path = self.config_dir / filename
if path.exists():
try:
with path.open("r", encoding="UTF-8") as file:
return json.load(file)
except Exception as e:
print(f"Error loading {filename}: {e}")
return None
return None
return None
def _load_sigma_json(self):
"""Load taxonomy configuration from JSON."""
json_dict = self._load_json(self.JSON_NAME_TAXONOMY)
if not json_dict or "taxonomy" not in json_dict:
return
taxonomy_info: Dict[SigmaLogSource, List[str]] = {}
taxonomy_definition: Dict[SigmaLogSource, Optional[str]] = {}
taxonomy_redundant: Dict[SigmaLogSource, List[str]] = {}
# Process taxonomy data
temp = {key_logsource(v["logsource"]): v for v in json_dict["taxonomy"].values()}
for key in sorted(temp.keys(), key=str.casefold):
value = temp[key]
logsource = core_logsource(SigmaLogSource.from_dict(value["logsource"]))
fieldlist = sorted(
value["field"]["native"] + value["field"]["custom"], key=str.casefold
)
taxonomy_info[logsource] = fieldlist
taxonomy_definition[logsource] = value["logsource"].get("definition")
taxonomy_redundant[logsource] = value["field"]["redundant"]
self.taxonomy_version = json_dict["version"]
self.sigma_fieldsname = taxonomy_info
self.sigmahq_redundant_fields = taxonomy_redundant
self.sigmahq_logsource_definition = taxonomy_definition
def _load_filename_json(self):
"""Load filename pattern configuration from JSON."""
json_dict = self._load_json(self.JSON_NAME_FILENAME)
if not json_dict or "pattern" not in json_dict or "version" not in json_dict:
return
filename_info: Dict[SigmaLogSource, str] = {}
temp = {key_logsource(v["logsource"]): v for v in json_dict["pattern"].values()}
for key in sorted(temp.keys(), key=str.casefold):
value = temp[key]
logsource = core_logsource(SigmaLogSource.from_dict(value["logsource"]))
filename_info[logsource] = value["prefix"]
self.filename_version = json_dict["version"]
self.sigmahq_logsource_filepattern = filename_info
def _load_windows_provider_json(self):
"""Load Windows provider configuration from JSON."""
json_dict = self._load_json(self.JSON_NAME_WINDOWS_PROVIDER)
if (
not json_dict
or "category_provider_name" not in json_dict
or "category_no_eventid" not in json_dict
):
return
windows_provider_name = dict()
for category in sorted(json_dict["category_provider_name"], key=str.casefold):
windows_provider_name[
SigmaLogSource(product="windows", category=category, service=None)
] = json_dict["category_provider_name"][category]
windows_no_eventid = sorted(json_dict["category_no_eventid"], key=str.casefold)
self.windows_version = json_dict["version"]
self.windows_provider_name = windows_provider_name
self.windows_no_eventid = windows_no_eventid