-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathX9_editor.py
More file actions
738 lines (625 loc) · 30.9 KB
/
X9_editor.py
File metadata and controls
738 lines (625 loc) · 30.9 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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
import os.path
import re
import sys
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QMessageBox, QInputDialog, QMenu, QTreeWidget, QSplitter)
from PyQt6.QtWidgets import QFileDialog
from PyQt6.QtWidgets import QTreeWidgetItem
from libs_com.utils_yaml import load_yaml, save_yaml_format
from libs_editor.editor_form import RulesForm
from libs_editor.editor_toolbar import RulesToolBar
from libs_rules.rules_data import fixed_rules
from libs_rules.rules_enum import RuleKeys, SeverityLevel, VulnType
from libs_rules.rules_utils import sort_lang_rules, find_lang_dup_rule
from libs_rules.rules_valid import check_all_rule_regex
from setting import DEF_CONFIG_RULES
class RulesEditor(QMainWindow):
def __init__(self, rules_file):
super().__init__()
self.rules_file = rules_file
self.rules_data = None
self.__init_ui()
def __init_ui(self):
self.setWindowTitle('SAST Rules Editor')
self.setGeometry(100, 100, 800, 600)
# 配置tool_bar
# self.__toolbar_setup()
self.toolbar = RulesToolBar(self)
self.addToolBar(self.toolbar)
# 连接信号
self.toolbar.save_action.triggered.connect(self.save_config)
self.toolbar.reload_action.triggered.connect(self.quick_load_rules)
self.toolbar.check_action.triggered.connect(self.check_all_rules)
self.toolbar.check_dup_action.triggered.connect(self.check_duplicate_rules) # 添加这行
self.toolbar.disable_action.triggered.connect(lambda: self.toggle_all_rules(False))
self.toolbar.enable_action.triggered.connect(lambda: self.toggle_all_rules(True))
self.toolbar.open_action.triggered.connect(self.open_config_file)
self.toolbar.sort_action.triggered.connect(self.sort_rules) # 添加这行
# 创建主窗口部件
main_widget = QWidget()
self.setCentralWidget(main_widget)
layout = QHBoxLayout()
main_widget.setLayout(layout)
# 创建分割器
splitter = QSplitter(Qt.Orientation.Horizontal)
# 左侧规则树
left_widget = QWidget()
left_layout = QVBoxLayout()
left_widget.setLayout(left_layout)
left_widget.setMinimumWidth(350) # 设置最小宽度,防止过度收缩
# 创建规则树
self.tree = QTreeWidget()
self.tree.setHeaderLabel('Rules List')
self.tree.setSelectionMode(QTreeWidget.SelectionMode.ExtendedSelection)
self.tree.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.tree.itemClicked.connect(self.on_item_selected)
self.tree.currentItemChanged.connect(self.on_item_selected) # 实现按键触发更新
self.tree.customContextMenuRequested.connect(self.show_context_menu)
left_layout.addWidget(self.tree)
# 右侧编辑区
right_widget = QWidget()
right_layout = QVBoxLayout()
right_widget.setLayout(right_layout)
# 将左右部件添加到分割器
splitter.addWidget(left_widget)
splitter.addWidget(right_widget)
# 设置左右比例为1:2
splitter.setStretchFactor(0, 1) # 左侧权重为1
splitter.setStretchFactor(1, 2) # 右侧权重为2
# 将分割器添加到主布局
layout.addWidget(splitter)
# 创建规则编辑表单
self.edit_form = RulesForm()
self.edit_form.save_btn.clicked.connect(self.save_rule)
self.edit_form.delete_btn.clicked.connect(self.delete_rule)
right_layout.addWidget(self.edit_form)
self.edit_form.hide()
layout.addWidget(left_widget)
layout.addWidget(right_widget)
def open_config_file(self):
"""打开配置文件"""
file_name, _ = QFileDialog.getOpenFileName(self, "Select config file", "", "YAML files (*.yml *.yaml);;All files (*.*)")
if file_name:
# self.edit_form.hide() # 隐藏编辑表单
self.load_rules(file_name)
def load_rules(self, rules_file):
# 备份当前的配置文件
rules_data_copy = self.rules_data.copy() if self.rules_data else None
try:
rules_data = load_yaml(rules_file)
if rules_data:
rules_data = fixed_rules(rules_data)
self.rules_data = rules_data
self.update_tree()
self.update_language_combo()
# 加载成功后再进行配置文件更新
self.rules_file = rules_file
QMessageBox.information(self, 'Hint', f'Loading the rule file {rules_file} was successful.')
else:
QMessageBox.critical(self, 'Error', f'Loading the rule file {rules_file} is empty')
except Exception as e:
QMessageBox.critical(self, 'Error', f'Error loading rule file {rules_file}: {str(e)}')
# 进行规则还原
self.rules_data = rules_data_copy
self.update_tree()
self.update_language_combo()
def quick_load_rules(self):
# 检查规则文件是否存在
if not self.rules_file or not os.path.isfile(self.rules_file):
QMessageBox.critical(self, 'Error', f'Failed find config file: {self.rules_file}')
return
# 直接进行加载
if not self.rules_data:
self.load_rules(self.rules_file)
return
# 进行重新加
reply = QMessageBox.question(
self,
'Confirm to reload',
'Reload will lose unsaved changes. Do you want to continue?',
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
)
if reply == QMessageBox.StandardButton.Yes:
# self.edit_form.hide() # 隐藏编辑表单
self.load_rules(self.rules_file) # 重新加载规则
def save_to_file(self, rules_data=None, rules_file=None):
"""保存规则到文件"""
rules_file = rules_file if rules_file else self.rules_file
rules_data = rules_data if rules_data else self.rules_data
if not rules_data:
QMessageBox.warning(self, 'Error', 'Failed to save the rule file: The current data is empty')
return False
if not rules_file:
QMessageBox.warning(self, 'Error', 'Failed to save the rule file: No file path was specified')
return False
# 进行文件保存
status, error = save_yaml_format(rules_file, rules_data)
if error:
QMessageBox.critical(self, 'Error', f'Failed to save the rule file: {str(error)}')
return False
return True
def save_config(self):
"""保存当前配置"""
if self.save_to_file():
QMessageBox.information(self, 'Hint', 'Config has been saved')
def save_rule(self):
current_item = self.tree.currentItem()
if not current_item or not current_item.parent():
QMessageBox.warning(self, 'Warn', 'Please select a rule first.')
return
# 获取表单数据
vuln_data = self.edit_form.get_form_data()
# 检查当前规则是否有效
matches = []
for pattern in vuln_data[RuleKeys.PATTERNS.value]:
# 排除空正则的情况
if not pattern.strip():
continue
try:
flags = re.MULTILINE | re.DOTALL
if vuln_data[RuleKeys.IGNORE_CASE.value]:
flags |= re.IGNORECASE
sample_code = ' '.join(vuln_data[RuleKeys.SAMPLE_CODE.value].splitlines())
for match in re.finditer(pattern, sample_code, flags):
matches.append(match.group(0))
except re.error as e:
QMessageBox.warning(self, 'Rule Verification', f'Regular expression error: {e}')
return
if not matches:
reply = QMessageBox.question(
self,
'Rule Verification',
'The current rule does not match the sample code. Do you want to continue saving it?',
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
)
if reply == QMessageBox.StandardButton.No:
return
# 更新数据
lang_name = self.edit_form.lang_combo.currentText()
old_name = current_item.text(0)
for lang in self.rules_data[RuleKeys.LANGUAGES.value]:
if lang[RuleKeys.LANGUAGE.value] == lang_name:
for i, vuln in enumerate(lang[RuleKeys.VULNS.value]):
if vuln[RuleKeys.RULE_NAME.value] == old_name:
lang[RuleKeys.VULNS.value][i] = vuln_data
break
if self.save_to_file():
if matches:
QMessageBox.information(self, 'save successfully', f'Rule verification passed\nMatching content: {matches}')
# 保存当前规则的信息
lang_name = self.edit_form.lang_combo.currentText()
rule_name = vuln_data[RuleKeys.RULE_NAME.value]
# 更新树并重新选择节点
self.update_tree()
# 重新查找并选择保存的规则
for i in range(self.tree.topLevelItemCount()):
lang_item = self.tree.topLevelItem(i)
if lang_item.text(0) == lang_name:
# 遍历漏洞类型节点
for j in range(lang_item.childCount()):
class_item = lang_item.child(j)
# 遍历规则节点
for k in range(class_item.childCount()):
rule_item = class_item.child(k)
if rule_item.text(0) == rule_name:
self.tree.setCurrentItem(rule_item)
rule_item.setSelected(True) # 确保节点被选中
self.on_item_selected(rule_item) # 触发选择事件
return # 找到后直接返回
def delete_rule(self):
current_item = self.tree.currentItem()
if not current_item or not current_item.parent():
return
reply = QMessageBox.question(self, 'Confirm remvoe',
f'Are you sure you want to delete the rule {current_item.text(0)} ?',
QMessageBox.StandardButton.Yes |
QMessageBox.StandardButton.No)
if reply == QMessageBox.StandardButton.Yes:
lang_name = current_item.parent().text(0)
for lang in self.rules_data[RuleKeys.LANGUAGES.value]:
if lang[RuleKeys.LANGUAGE.value] == lang_name:
lang[RuleKeys.VULNS.value] = [v for v in lang[RuleKeys.VULNS.value]
if v[RuleKeys.RULE_NAME.value] != current_item.text(0)]
break
if self.save_to_file():
self.update_tree()
self.edit_form.hide()
def update_language_combo(self):
self.edit_form.lang_combo.clear()
if self.rules_data:
languages = [lang[RuleKeys.LANGUAGE.value] for lang in self.rules_data.get(RuleKeys.LANGUAGES.value, [])]
self.edit_form.lang_combo.addItems(languages)
self.edit_form.lang_combo.setDisabled(True) # 设置不可点击
def on_item_selected(self, item):
# 检查 item 是否为 None
if item is None:
return
# 如果是父节点(语言节点或漏洞类型节点)
if not item.parent() or (item.parent() and not item.parent().parent()):
item.setExpanded(True) # 自动展开当前节点
self.edit_form.hide()
return
# 规则节点(有两层父节点: 语言->漏洞类型->规则)
self.edit_form.show()
vuln_data = item.data(0, Qt.ItemDataRole.UserRole)
self.edit_form.set_form_data(vuln_data)
self.edit_form.lang_combo.setCurrentText(item.parent().parent().text(0)) # 设置语言为祖父节点的文本
def update_tree(self):
# 保存当前展开状态
expanded_items = {}
for i in range(self.tree.topLevelItemCount()):
lang_item = self.tree.topLevelItem(i)
if lang_item.isExpanded():
expanded_items[lang_item.text(0)] = set()
for j in range(lang_item.childCount()):
class_item = lang_item.child(j)
if class_item.isExpanded():
expanded_items[lang_item.text(0)].add(class_item.text(0))
self.tree.clear()
if not self.rules_data:
return
# 重建树并恢复展开状态
for lang in self.rules_data.get(RuleKeys.LANGUAGES.value, []):
# 创建语言节点
lang_item = QTreeWidgetItem([lang[RuleKeys.LANGUAGE.value]])
self.tree.addTopLevelItem(lang_item)
# 按漏洞类型分组规则
vuln_types = {}
for vuln in lang.get(RuleKeys.VULNS.value, []):
vuln_type = vuln.get(RuleKeys.VULN_TYPE.value, VulnType.OTHER.value)
if vuln_type not in vuln_types:
vuln_types[vuln_type] = []
vuln_types[vuln_type].append(vuln)
# 添加漏洞类型节点和漏洞
for vuln_type, vulns in sorted(vuln_types.items()):
class_item = QTreeWidgetItem([vuln_type])
lang_item.addChild(class_item)
for vuln in sorted(vulns, key=lambda x: x[RuleKeys.RULE_NAME.value]):
vuln_item = QTreeWidgetItem([vuln[RuleKeys.RULE_NAME.value]])
vuln_item.setData(0, Qt.ItemDataRole.UserRole, vuln)
# 根据规则状态和危险等级设置不同颜色
if not vuln.get(RuleKeys.LOADED.value, True):
# 未启用的规则显示为绿色
vuln_item.setForeground(0, Qt.GlobalColor.green)
else:
# 根据危险等级设置颜色
severity = vuln.get(RuleKeys.SEVERITY.value, SeverityLevel.HIGH.value)
if severity == SeverityLevel.HIGH.value:
# 高危显示为红色
vuln_item.setForeground(0, Qt.GlobalColor.red)
elif severity == SeverityLevel.MEDIUM.value:
# 中危显示为蓝色
vuln_item.setForeground(0, Qt.GlobalColor.blue)
elif severity == SeverityLevel.LOW.value:
# 低危显示为灰色
vuln_item.setForeground(0, Qt.GlobalColor.gray)
class_item.addChild(vuln_item)
# 恢复展开状态
if lang[RuleKeys.LANGUAGE.value] in expanded_items:
lang_item.setExpanded(True)
for j in range(lang_item.childCount()):
class_item = lang_item.child(j)
if class_item.text(0) in expanded_items[lang[RuleKeys.LANGUAGE.value]]:
class_item.setExpanded(True)
def check_all_rules(self):
if not self.rules_data:
QMessageBox.warning(self, 'Warn', 'No rules were found.')
return
all_lang_rules = self.rules_data.get(RuleKeys.LANGUAGES.value, [])
validation_results = check_all_rule_regex(all_lang_rules)
if validation_results:
QMessageBox.warning(self, 'Rule check results', '\n'.join(validation_results))
else:
QMessageBox.information(self, 'Result of rule check', 'All rules have passed the verification !')
def toggle_all_rules(self, enable):
if not self.rules_data:
QMessageBox.warning(self, 'Warn', 'No rules were found.')
return
count = 0
for lang_rule in self.rules_data.get(RuleKeys.LANGUAGES.value, []):
for vuln in lang_rule.get(RuleKeys.VULNS.value, []):
if vuln[RuleKeys.LOADED.value] != enable:
vuln[RuleKeys.LOADED.value] = enable
count += 1
if self.save_to_file():
status = 'Enable' if enable else 'Disable'
QMessageBox.information(
self,
'operate successfully',
f'There are {count} rules have been {status}'
)
self.update_tree()
def show_context_menu(self, position):
"""显示右键菜单"""
item = self.tree.itemAt(position)
menu = QMenu()
if not item: # 空白处或标题处右键
add_lang_action = menu.addAction('Add Language')
action = menu.exec(self.tree.viewport().mapToGlobal(position))
if action == add_lang_action:
self.add_language()
return
if not item.parent(): # 语言节点
lang_name = item.text(0)
# 创建语言菜单项
add_lang_action = menu.addAction('Add Language')
menu.addSeparator()
delete_action = menu.addAction(f'Delete language {lang_name}')
disable_action = menu.addAction(f'Disable all rules of the language {lang_name}')
enable_action = menu.addAction(f'Enable all rules of the language {lang_name}')
add_rule_action = menu.addAction(f'Add rules for {lang_name}')
action = menu.exec(self.tree.viewport().mapToGlobal(position))
if action == add_lang_action:
self.add_language()
elif action == delete_action:
self.delete_language(lang_name)
elif action == disable_action:
self.toggle_language_rules(lang_name, False)
elif action == enable_action:
self.toggle_language_rules(lang_name, True)
elif action == add_rule_action:
self.tree.setCurrentItem(item)
self.add_vulnerability()
return
# 获取所有选中的项目
selected_items = self.tree.selectedItems()
# 规则分类节点或规则节点
if item.parent():
# 获取规则节点(包括规则分类下的所有规则)
rule_items = []
if item.parent().parent(): # 规则节点
rule_items = [i for i in selected_items if i.parent() and i.parent().parent()]
else: # 规则分类节点
# 如果点击的是分类节点,获取该分类下的所有规则
for i in range(item.childCount()):
rule_items.append(item.child(i))
if not rule_items:
return
rules_count = len(rule_items)
# 创建规则菜单项
copy_action = menu.addAction(f'Copy {rules_count} selected rules')
delete_action = menu.addAction(f'Delete {rules_count} selected rules')
# 检查是否所有规则都启用或禁用
all_enabled = all(i.data(0, Qt.ItemDataRole.UserRole).get(RuleKeys.LOADED.value, True) for i in rule_items)
all_disabled = all(not i.data(0, Qt.ItemDataRole.UserRole).get(RuleKeys.LOADED.value, True) for i in rule_items)
toggle_action = None
if all_disabled:
toggle_action = menu.addAction(f'Enable the selected {rules_count} rules')
elif all_enabled:
toggle_action = menu.addAction(f'Disable the selected {rules_count} rules')
else:
toggle_enable_action = menu.addAction(f'Enable the selected rule')
toggle_disable_action = menu.addAction(f'Disable the selected rule')
# 添加漏洞类型子菜单
vuln_type_menu = menu.addMenu(f'Set the type of vuln')
vuln_type_actions = {}
for vuln_type in VulnType.choices():
action = vuln_type_menu.addAction(vuln_type)
vuln_type_actions[action] = vuln_type
# 显示菜单并获取选择的动作
action = menu.exec(self.tree.viewport().mapToGlobal(position))
if action == delete_action:
self.delete_selected_rules(rule_items)
elif action == copy_action:
self.copy_rules(rule_items)
elif all_disabled and action == toggle_action:
self.toggle_selected_rules(rule_items, True)
elif all_enabled and action == toggle_action:
self.toggle_selected_rules(rule_items, False)
elif not all_disabled and not all_enabled:
if action == toggle_enable_action:
self.toggle_selected_rules(rule_items, True)
elif action == toggle_disable_action:
self.toggle_selected_rules(rule_items, False)
elif action in vuln_type_actions: # 添加这个条件分支
self.set_rules_vuln_type(rule_items, vuln_type_actions[action])
def delete_selected_rules(self, items):
"""删除选中的规则"""
reply = QMessageBox.question(
self,
'Confirm remvoe',
f'Are you sure you want to delete {len(items)} selected rules?',
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
)
if reply == QMessageBox.StandardButton.Yes:
changes_made = False
for item in items:
lang_name = item.parent().parent().text(0) # 获取语言节点(祖父节点)
rule_name = item.text(0)
for lang in self.rules_data[RuleKeys.LANGUAGES.value]:
if lang[RuleKeys.LANGUAGE.value] == lang_name:
lang[RuleKeys.VULNS.value] = [
v for v in lang[RuleKeys.VULNS.value]
if v[RuleKeys.RULE_NAME.value] != rule_name
]
changes_made = True
if changes_made and self.save_to_file():
self.update_tree()
self.edit_form.hide()
def copy_rules(self, items):
"""复制多个规则"""
from datetime import datetime
import copy
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
copied_count = 0
for item in items:
vuln_data = item.data(0, Qt.ItemDataRole.UserRole)
lang_name = item.parent().parent().text(0) # 获取语言节点(祖父节点)
# 创建新规则的副本
new_vuln = copy.deepcopy(vuln_data)
new_vuln[RuleKeys.RULE_NAME.value] = f"{vuln_data[RuleKeys.RULE_NAME.value]}_copy_{timestamp}"
# 添加新规则到对应语言
for lang in self.rules_data[RuleKeys.LANGUAGES.value]:
if lang[RuleKeys.LANGUAGE.value] == lang_name:
lang[RuleKeys.VULNS.value].append(new_vuln)
copied_count += 1
break
# 保存并更新UI
if copied_count > 0 and self.save_to_file():
self.update_tree()
QMessageBox.information(self, 'Hint', f'{copied_count} rules have been copied.')
def toggle_selected_rules(self, items, enable):
"""启用/禁用选中的规则"""
count = 0
for item in items:
lang_name = item.parent().parent().text(0) # 获取语言节点(祖父节点)
rule_name = item.text(0)
for lang in self.rules_data[RuleKeys.LANGUAGES.value]:
if lang[RuleKeys.LANGUAGE.value] == lang_name:
for vuln in lang[RuleKeys.VULNS.value]:
if vuln[RuleKeys.RULE_NAME.value] == rule_name and vuln.get(RuleKeys.LOADED.value, True) != enable:
vuln[RuleKeys.LOADED.value] = enable
count += 1
if count > 0 and self.save_to_file():
status = 'Enable' if enable else 'Disable'
QMessageBox.information(
self,
'operate successfully',
f'There are {count} rules have been {status}'
)
self.update_tree()
def add_vulnerability(self):
if not self.rules_data or not self.rules_data.get(RuleKeys.LANGUAGES.value):
QMessageBox.warning(self, 'Warn', 'Please add the language first.')
return
# 获取当前选中的树节点
current_item = self.tree.currentItem()
if not current_item:
QMessageBox.warning(self, 'Warn', 'Please select a language.')
return
# 如果选中的是规则节点,获取其父节点(语言节点)
if current_item.parent():
lang_name = current_item.parent().text(0)
else:
lang_name = current_item.text(0)
vuln_data = {
RuleKeys.RULE_NAME.value: 'new rule',
RuleKeys.VULN_TYPE.value: VulnType.OTHER.value,
RuleKeys.PATTERNS.value: [''],
RuleKeys.DESCRIPTION.value: '',
RuleKeys.SEVERITY.value: f'{SeverityLevel.HIGH.value}',
RuleKeys.SAMPLE_CODE.value: '',
RuleKeys.IGNORE_CASE.value: True,
RuleKeys.LOADED.value: True,
RuleKeys.RELATED_SUFFIXES.value: '*',
RuleKeys.CONTEXT_BEFORE.value: 50,
RuleKeys.CONTEXT_AFTER.value: 50,
RuleKeys.CONTEXT_NEED.value: False
}
# Add vulnerability to selected language
for lang in self.rules_data[RuleKeys.LANGUAGES.value]:
if lang[RuleKeys.LANGUAGE.value] == lang_name:
lang[RuleKeys.VULNS.value].append(vuln_data)
break
# Save and update UI
if self.save_to_file():
self.update_tree()
# Select the newly added rule
for i in range(self.tree.topLevelItemCount()):
lang_item = self.tree.topLevelItem(i)
if lang_item.text(0) == lang_name:
vuln_item = lang_item.child(lang_item.childCount() - 1)
self.tree.setCurrentItem(vuln_item)
self.on_item_selected(vuln_item)
break
def add_language(self):
lang_name, ok = QInputDialog.getText(self, 'Add Language', 'Please enter the language:')
if ok and lang_name:
# 初始化 rules_data 如果不存在
if not self.rules_data:
self.rules_data = {RuleKeys.LANGUAGES.value: []}
# 检查语言是否已存在
if any(lang[RuleKeys.LANGUAGE.value] == lang_name for lang in self.rules_data.get(RuleKeys.LANGUAGES.value, [])):
QMessageBox.warning(self, 'Warn', f'Language {lang_name} already exists')
return
self.rules_data[RuleKeys.LANGUAGES.value].append({
RuleKeys.LANGUAGE.value: lang_name,
RuleKeys.VULNS.value: []
})
if self.save_to_file():
self.update_tree()
self.update_language_combo()
def delete_language(self, lang_name):
"""Delete language"""
reply = QMessageBox.question(
self,
'Confirm remvoe',
f'Are you sure delete the language {lang_name} and all its rules? ',
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
)
if reply == QMessageBox.StandardButton.Yes:
self.rules_data[RuleKeys.LANGUAGES.value] = [
lang for lang in self.rules_data[RuleKeys.LANGUAGES.value]
if lang[RuleKeys.LANGUAGE.value] != lang_name
]
if self.save_to_file():
self.update_tree()
self.update_language_combo()
self.edit_form.hide()
def toggle_language_rules(self, lang_name, enable):
"""启用/禁用语言的所有规则"""
count = 0
for lang in self.rules_data[RuleKeys.LANGUAGES.value]:
if lang[RuleKeys.LANGUAGE.value] == lang_name:
for vuln in lang[RuleKeys.VULNS.value]:
if vuln[RuleKeys.LOADED.value] != enable:
vuln[RuleKeys.LOADED.value] = enable
count += 1
break
if count > 0 and self.save_to_file():
status = 'Enable' if enable else 'Disable'
QMessageBox.information(
self,
'operate successfully',
f'There are {count} rules for {lang_name} that have been {status}'
)
self.update_tree()
def check_duplicate_rules(self):
"""检查每个语言中是否存在重复规则"""
if not self.rules_data:
QMessageBox.warning(self, 'Warn', 'No rules were found.')
return
all_lang_rules = self.rules_data.get(RuleKeys.LANGUAGES.value, [])
duplicate_results = find_lang_dup_rule(all_lang_rules)
if duplicate_results:
QMessageBox.warning(self, 'Repetition rule check results', '\n'.join(duplicate_results))
else:
QMessageBox.information(self, 'Repetition rule check results', '未发现重复规则!')
def sort_rules(self):
"""按字母顺序排序每个语言的规则"""
if not self.rules_data:
QMessageBox.warning(self, 'Warn', 'No rules were found.')
return
all_lang_rules = self.rules_data[RuleKeys.LANGUAGES.value]
changes_made = sort_lang_rules(all_lang_rules)
if changes_made:
if self.save_to_file():
self.update_tree()
QMessageBox.information(self, 'Sorting completed', 'The rules have been sorted in alphabetical order and saved.')
else:
QMessageBox.information(self, 'Sorting completed', 'The rules are already sorted in alphabetical order.')
def set_rules_vuln_type(self, items, vuln_type):
"""设置选中规则的漏洞类型"""
count = 0
for item in items:
lang_name = item.parent().parent().text(0) # 获取语言节点(祖父节点)
rule_name = item.text(0)
for lang in self.rules_data[RuleKeys.LANGUAGES.value]:
if lang[RuleKeys.LANGUAGE.value] == lang_name:
for vuln in lang[RuleKeys.VULNS.value]:
if vuln[RuleKeys.RULE_NAME.value] == rule_name:
vuln[RuleKeys.VULN_TYPE.value] = vuln_type.value
count += 1
if count > 0 and self.save_to_file():
QMessageBox.information(
self,
'operate successfully',
f'The vulne type of {count} rules has been set to {vuln_type.value}'
)
self.update_tree()
if __name__ == '__main__':
app = QApplication(sys.argv)
editor = RulesEditor(DEF_CONFIG_RULES) # 显式传入配置文件路径
editor.show()
sys.exit(app.exec())