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('<>', 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'', 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()