-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgui.py
259 lines (207 loc) · 10.3 KB
/
gui.py
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
import tkinter as tk
from tkinter import filedialog, messagebox
import json
import re
from pathlib import Path
import pandas as pd
from keyword_manager import KeywordManager
from keyword_dialog import KeywordDialog
class JsonExtractorGUI:
def __init__(self, root):
self.root = root
self.root.title("HTML JSON提取器")
self.root.geometry("800x600")
# 输入文件框
self.input_frame = tk.LabelFrame(root, text="输入HTML文件", padx=10, pady=5)
self.input_frame.pack(fill="x", padx=10, pady=5)
self.input_path = tk.StringVar()
self.input_entry = tk.Entry(self.input_frame, textvariable=self.input_path, width=80)
self.input_entry.pack(side="left", padx=5)
self.browse_btn = tk.Button(self.input_frame, text="浏览", command=self.browse_input)
self.browse_btn.pack(side="left", padx=5)
# 拖放提示移到输入框右边
self.drop_label = tk.Label(self.input_frame, text="(支持拖放HTML文件)", pady=5)
self.drop_label.pack(side="left", padx=5)
# 输出文件框
self.output_frame = tk.LabelFrame(root, text="输出JSON文件", padx=10, pady=5)
self.output_frame.pack(fill="x", padx=10, pady=5)
self.output_path = tk.StringVar()
self.output_entry = tk.Entry(self.output_frame, textvariable=self.output_path, width=80)
self.output_entry.pack(side="left", padx=5)
self.save_btn = tk.Button(self.output_frame, text="浏览", command=self.browse_output)
self.save_btn.pack(side="left", padx=5)
# 将提取JSON按钮移到输出文件框中
self.extract_btn = tk.Button(self.output_frame, text="提取JSON",
command=self.extract_json)
self.extract_btn.pack(side="left", padx=5)
# 添加关键词管理器
self.keyword_manager = KeywordManager()
# 创建漏洞分类框架
vuln_frame = tk.LabelFrame(root, text="漏洞类型分类", padx=5, pady=5)
vuln_frame.pack(fill="x", padx=10, pady=5)
# 添加匹配模式选择
self.match_mode = tk.StringVar(value="both")
match_label = tk.Label(vuln_frame, text="匹配模式:")
match_label.pack(side="left", padx=5)
modes = [
("精准匹配", "exact"),
("模糊匹配", "fuzzy"),
("精准+模糊", "both")
]
for text, mode in modes:
tk.Radiobutton(vuln_frame, text=text, variable=self.match_mode,
value=mode).pack(side="left", padx=5)
# 添加关键词管理按钮
self.keyword_btn = tk.Button(vuln_frame, text="关键词管理",
command=self.show_keyword_dialog)
self.keyword_btn.pack(side="left", padx=15)
# 添加导出按钮
self.vuln_btn = tk.Button(vuln_frame, text="导出分类",
command=self.export_vuln_types)
self.vuln_btn.pack(side="left", padx=5)
# 日志框
self.log_frame = tk.LabelFrame(root, text="运行日志", padx=10, pady=5)
self.log_frame.pack(fill="both", expand=True, padx=10, pady=5)
# 创建文本框和滚动条
self.log_text = tk.Text(self.log_frame, height=15, width=80)
self.scrollbar = tk.Scrollbar(self.log_frame, orient="vertical", command=self.log_text.yview)
self.log_text.configure(yscrollcommand=self.scrollbar.set)
# 放置文本框和滚动条
self.scrollbar.pack(side="right", fill="y")
self.log_text.pack(side="left", fill="both", expand=True)
# 状态显示
self.status_var = tk.StringVar()
self.status_label = tk.Label(root, textvariable=self.status_var)
self.status_label.pack(pady=5)
# 绑定拖放事件
self.root.drop_target_register(DND_FILES)
self.root.dnd_bind('<<Drop>>', self.handle_drop)
def log_message(self, message, level="INFO"):
"""添加日志消息到日志框"""
import datetime
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.log_text.insert("end", f"[{timestamp}] [{level}] {message}\n")
self.log_text.see("end") # 自动滚动到最新消息
def browse_input(self):
filename = filedialog.askopenfilename(
filetypes=[("HTML文件", "*.html"), ("所有文件", "*.*")]
)
if filename:
self.input_path.set(filename)
# 自动设置输出文件名
output_path = Path(filename).with_suffix('.json')
self.output_path.set(str(output_path))
def browse_output(self):
filename = filedialog.asksaveasfilename(
defaultextension=".json",
filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
)
if filename:
self.output_path.set(filename)
def handle_drop(self, event):
file_path = event.data
self.log_message(f"收到拖放文件: {file_path}")
# 移除花括号并转换为Path对象
file_path = Path(file_path.strip('{}'))
# 检查文件扩展名(不区分大小写)
if file_path.suffix.lower() in ('.html', '.htm'):
self.input_path.set(str(file_path))
output_path = file_path.with_suffix('.json')
self.output_path.set(str(output_path))
self.log_message(f"已设置输入文件: {file_path}")
self.log_message(f"已设置输出文件: {output_path}")
else:
self.log_message(f"无效的文件类型: {file_path}", "ERROR")
def extract_json(self):
input_file = self.input_path.get()
output_file = self.output_path.get()
if not input_file or not output_file:
self.log_message("请选择输入和输出文件!", "ERROR")
return
try:
# 尝试多种编码格式
encodings = ['utf-8', 'gbk', 'gb2312', 'iso-8859-1']
file_content = None
for encoding in encodings:
try:
with open(input_file, encoding=encoding) as f:
file_content = f.read()
self.log_message(f"成功使用 {encoding} 编码读取文件")
break
except UnicodeDecodeError:
continue
if file_content is None:
self.log_message(f"无法读取文件,已尝试以下编码: {', '.join(encodings)}", "ERROR")
return
self.log_message(f"正在处理文件: {input_file}")
pat_list = re.findall(r'<script>window.data = (.*?);</script>', file_content)
if not pat_list:
self.log_message("未在HTML文件中找到匹配的JSON数据!", "ERROR")
return
data_json = json.loads(pat_list[0])
with open(output_file, 'w', encoding='utf8') as f:
json.dump(data_json, f, ensure_ascii=False, indent=4)
success_msg = f"JSON数据已成功保存到: {output_file}"
self.log_message(success_msg, "SUCCESS")
self.status_var.set("JSON提取成功!")
except Exception as e:
error_msg = f"处理过程中出现错误:{str(e)}"
self.log_message(error_msg, "ERROR")
self.status_var.set(f"错误: {str(e)}")
def show_keyword_dialog(self):
KeywordDialog(self.root, self.keyword_manager)
def export_vuln_types(self):
try:
# 先让用户选择保存位置
output_excel = filedialog.asksaveasfilename(
title="保存漏洞分类Excel",
defaultextension=".xlsx",
initialfile="漏洞类型分类.xlsx",
filetypes=[
("Excel文件", "*.xlsx"),
("所有文件", "*.*")
]
)
if not output_excel: # 用户取消选择
return
# 读取JSON文件
with open(self.output_path.get(), 'r', encoding='utf-8') as f:
data = json.load(f)
# 获取漏洞列表
vuln_list = data['categories'][3]['children'][0]['data']['vulns_info']['vuln_distribution']['vuln_list']
# 准备Excel数据
excel_data = []
for i, vuln in enumerate(vuln_list, 1):
# 处理描述和解决方案
description = '\n'.join(filter(None, vuln.get('i18n_description', [])))
solution = '\n'.join(filter(None, vuln.get('i18n_solution', [])))
# 获取漏洞等级中文
level_map = {'high': '高危', 'middle': '中危', 'low': '低危'}
level = level_map.get(vuln.get('vuln_level', ''), '未知')
# 获取漏洞类型
vuln_type = self.keyword_manager.get_type(
vuln.get('i18n_name', ''),
self.match_mode.get()
)
excel_data.append({
'序号': i,
'漏洞名称': vuln.get('i18n_name', ''),
'类型': vuln_type,
'漏洞等级': level,
'影响主机个数': vuln.get('vuln_count', 0),
'受影响主机': vuln.get('target', ''),
'详细描述': description,
'解决办法': solution
})
# 创建DataFrame并导出到Excel
df = pd.DataFrame(excel_data)
df.to_excel(output_excel, index=False)
self.log_message(f"漏洞类型分类已导出到: {output_excel}", "SUCCESS")
except Exception as e:
self.log_message(f"导出漏洞类型分类时出错: {str(e)}", "ERROR")
if __name__ == "__main__":
# 需要安装tkinterdnd2包来支持拖放功能
from tkinterdnd2 import *
root = TkinterDnD.Tk()
app = JsonExtractorGUI(root)
root.mainloop()