Skip to content

Commit ea08aec

Browse files
committed
add rule group creation
1 parent aaed5dd commit ea08aec

1 file changed

Lines changed: 105 additions & 0 deletions

File tree

gui/rule_editor.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ def __init__(self, config: SysmonConfig) -> None:
3030
self.rule_type = QComboBox()
3131
self.rule_type.addItems(["include", "exclude"])
3232

33+
self.group_box = QComboBox()
34+
self.group_box.setEditable(True)
35+
self.group_box.setInsertPolicy(QComboBox.InsertPolicy.NoInsert)
36+
self.group_box.setPlaceholderText("Select or type rule group (optional)")
37+
38+
self.group_relation = QComboBox()
39+
self.group_relation.addItems(["or", "and"])
40+
3341
self.field_box = QComboBox()
3442

3543
self.condition_box = QComboBox()
@@ -52,11 +60,16 @@ def __init__(self, config: SysmonConfig) -> None:
5260
self.rule_row_1.addWidget(self.field_box)
5361
self.rule_row_1.addWidget(self.condition_box)
5462

63+
self.group_row = QHBoxLayout()
64+
self.group_row.addWidget(self.group_box)
65+
self.group_row.addWidget(self.group_relation)
66+
5567
self.rule_row_2 = QHBoxLayout()
5668
self.rule_row_2.addWidget(self.value_preset_box)
5769
self.rule_row_2.addWidget(self.value_input)
5870

5971
self.layout.addWidget(self.title)
72+
self.layout.addLayout(self.group_row)
6073
self.layout.addLayout(self.rule_row_1)
6174
self.layout.addLayout(self.rule_row_2)
6275
self.layout.addWidget(self.add_button)
@@ -66,14 +79,62 @@ def __init__(self, config: SysmonConfig) -> None:
6679
self.add_button.clicked.connect(self.add_rule)
6780
self.remove_button.clicked.connect(self.remove_selected_rule)
6881
self.field_box.currentTextChanged.connect(self.load_value_presets_for_field)
82+
self.group_box.currentIndexChanged.connect(self.on_group_selected)
6983

7084
def set_event(self, event_id: int, event_name: str) -> None:
7185
self.current_event_id = event_id
7286
self.current_event_name = event_name
7387
self.title.setText(f"{event_id} - {event_name}")
7488
self.load_fields_for_event()
89+
self.refresh_group_options()
7590
self.refresh_rules()
7691

92+
def refresh_group_options(self) -> None:
93+
self.group_box.blockSignals(True)
94+
self.group_box.clear()
95+
self.group_box.addItem("")
96+
self.group_box.setItemData(0, None, Qt.ItemDataRole.UserRole)
97+
98+
if self.current_event_id is None:
99+
self.group_box.blockSignals(False)
100+
return
101+
102+
event_config = self.config.events.get(self.current_event_id)
103+
if event_config is None:
104+
self.group_box.blockSignals(False)
105+
return
106+
107+
seen_group_ids: set[str] = set()
108+
for rule in event_config.rules:
109+
if not rule.group_id or rule.group_id in seen_group_ids:
110+
continue
111+
seen_group_ids.add(rule.group_id)
112+
113+
group_name = rule.group_name or "Imported Rule"
114+
group_relation = rule.group_relation or "or"
115+
label = f"{group_name} ({group_relation})"
116+
self.group_box.addItem(label)
117+
self.group_box.setItemData(
118+
self.group_box.count() - 1,
119+
{
120+
"group_id": rule.group_id,
121+
"group_name": rule.group_name,
122+
"group_relation": group_relation,
123+
},
124+
Qt.ItemDataRole.UserRole,
125+
)
126+
127+
self.group_box.blockSignals(False)
128+
129+
def on_group_selected(self, *_args) -> None:
130+
selected_data = self.group_box.currentData(Qt.ItemDataRole.UserRole)
131+
if not isinstance(selected_data, dict):
132+
return
133+
134+
selected_relation = selected_data.get("group_relation")
135+
if selected_relation in ("or", "and"):
136+
self.group_relation.setCurrentText(selected_relation)
137+
77138
def load_fields_for_event(self) -> None:
78139
from data.sysmon_fields import SYS_MON_FIELDS
79140

@@ -169,17 +230,60 @@ def add_rule(self) -> None:
169230
self.current_event_name,
170231
)
171232

233+
selected_group_data = self.group_box.currentData(Qt.ItemDataRole.UserRole)
234+
selected_group_text = self.group_box.currentText().strip()
235+
236+
group_id: str | None = None
237+
group_name: str | None = None
238+
group_relation: str | None = None
239+
240+
if isinstance(selected_group_data, dict):
241+
group_id = selected_group_data.get("group_id")
242+
group_name = selected_group_data.get("group_name")
243+
group_relation = selected_group_data.get("group_relation")
244+
elif selected_group_text:
245+
existing_group = None
246+
for existing_rule in event_config.rules:
247+
if not existing_rule.group_id:
248+
continue
249+
if (existing_rule.group_name or "").strip().lower() == selected_group_text.lower():
250+
existing_group = existing_rule
251+
break
252+
253+
if existing_group is not None:
254+
group_id = existing_group.group_id
255+
group_name = existing_group.group_name
256+
group_relation = existing_group.group_relation or self.group_relation.currentText()
257+
else:
258+
existing_ids = {
259+
existing_rule.group_id
260+
for existing_rule in event_config.rules
261+
if existing_rule.group_id
262+
}
263+
next_index = 1
264+
group_id = f"user-group-{next_index}"
265+
while group_id in existing_ids:
266+
next_index += 1
267+
group_id = f"user-group-{next_index}"
268+
269+
group_name = selected_group_text
270+
group_relation = self.group_relation.currentText()
271+
172272
event_config.rules.append(
173273
RuleFilter(
174274
rule_type=self.rule_type.currentText(),
175275
field_name=self.field_box.currentText(),
176276
condition=self.condition_box.currentText(),
177277
value=value,
278+
group_id=group_id,
279+
group_name=group_name,
280+
group_relation=group_relation,
178281
)
179282
)
180283

181284
self.value_input.clear()
182285
self.value_preset_box.setCurrentIndex(0)
286+
self.refresh_group_options()
183287
self.refresh_rules()
184288

185289
def remove_selected_rule(self) -> None:
@@ -203,4 +307,5 @@ def remove_selected_rule(self) -> None:
203307
if 0 <= rule_index < len(event_config.rules):
204308
del event_config.rules[rule_index]
205309

310+
self.refresh_group_options()
206311
self.refresh_rules()

0 commit comments

Comments
 (0)