From 15395046d0534838bf24e70bceb7d656c9df2154 Mon Sep 17 00:00:00 2001 From: Felix <> Date: Tue, 18 Mar 2025 11:26:39 +0800 Subject: [PATCH] Release v1.0.1 --- .gitignore | 3 +- README.md | 20 +- gui.py | 680 ++++++++++++++++++++++++++++++++++++++++++++++- requirements.txt | Bin 149 -> 450 bytes 4 files changed, 698 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 53871bc..3f43bdc 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ index.html # 忽略测试输出的文件 data.json index.json -*.xlsx \ No newline at end of file +*.xlsx +*.bak \ No newline at end of file diff --git a/README.md b/README.md index 6c082e2..62cc453 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,18 @@ -# VulnDataAnalyzer -VulnDataAnalyzer是一款基于新版绿盟漏扫系统(v6.0+)的漏扫结果数据分析工具,实现更多维度分析漏洞情况,有助于高效化扩展其它方面的报告 +## 软件介绍 +VulnDataAnalyzer是一款基于绿盟漏扫系统漏扫结果的数据分析工具,实现更多维度数据提取与分析漏洞情况,有助于高效化扩展其它方面的报告 + +## 运行&开发环境 +![](https://img.shields.io/badge/Windows-10-brightgreen) ![](https://img.shields.io/badge/Python-3.9+-brightgreen) + +## 功能支持清单 +|功能 | 支持旧版 | 支持新版(v6.0+) | +| :------------: | :------------: | :------------: | +|漏洞类型分类 | :black_square_button: | :white_check_mark: | +| 明细表格IP提取 | :white_check_mark: | :white_check_mark: | +| 漏扫明细提取toExcel | :white_check_mark: | :white_check_mark: | + +## 更新日志 +``` +2025-03-17 +正式发布v1.0.1 +``` \ No newline at end of file diff --git a/gui.py b/gui.py index fc60041..ee3abb9 100644 --- a/gui.py +++ b/gui.py @@ -7,11 +7,18 @@ import pandas as pd from keyword_manager import KeywordManager from keyword_dialog import KeywordDialog from tkinterdnd2 import TkinterDnD, DND_FILES +import os +import datetime +from bs4 import BeautifulSoup +from openpyxl import Workbook +from openpyxl.styles import Font, PatternFill, Border, Side, Alignment +from openpyxl.utils import get_column_letter +import time class JsonExtractorGUI: def __init__(self, root): self.root = root - self.root.title("漏扫数据分析工具 - By Felix") + self.root.title("漏扫数据分析工具 v1.0.1 - By Felix") self.root.geometry("800x600") # 创建标签页 @@ -26,6 +33,10 @@ class JsonExtractorGUI: self.ip_extract_frame = ttk.Frame(self.notebook) self.notebook.add(self.ip_extract_frame, text="漏洞明细表格IP提取") + # 创建漏扫结果提取标签页 + self.vuln_export_frame = ttk.Frame(self.notebook) + self.notebook.add(self.vuln_export_frame, text="漏扫结果提取明细至Excel") + # 在漏洞分类标签页中添加组件 # 输入文件框 self.input_frame = tk.LabelFrame(self.vuln_class_frame, text="输入HTML文件", padx=10, pady=5) @@ -114,10 +125,12 @@ class JsonExtractorGUI: # 初始化IP提取标签页 self.init_ip_extract_tab() + + # 初始化漏扫结果提取标签页 + self.init_lvmeng_export_tab() 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") # 自动滚动到最新消息 @@ -422,6 +435,669 @@ class JsonExtractorGUI: self.ip_status_var.set(f"发生错误: {str(e)}") messagebox.showerror("错误", f"发生错误: {str(e)}") + def init_lvmeng_export_tab(self): + """初始化漏扫结果提取明细至Excel标签页""" + # 添加说明文字 + description = "本功能用于将绿盟漏扫系统生成的漏洞扫描结果HTML源文件提取至Excel文件,支持新旧版绿盟的漏洞扫描结果的解析和导出,并提供不同的导出样式。" + desc_label = tk.Label(self.vuln_export_frame, text=description, wraplength=700, justify="left") + desc_label.pack(fill="x", padx=10, pady=10) + + # 输入文件框架 + input_frame = tk.LabelFrame(self.vuln_export_frame, text="输入HTML文件", padx=10, pady=5) + input_frame.pack(fill="x", padx=10, pady=5) + + self.lvmeng_input_path = tk.StringVar() + self.lvmeng_input_entry = tk.Entry(input_frame, textvariable=self.lvmeng_input_path, width=80) + self.lvmeng_input_entry.pack(side="left", padx=5) + + browse_btn = tk.Button(input_frame, text="浏览", command=self.browse_lvmeng_input) + browse_btn.pack(side="left", padx=5) + + # 拖放提示 + drop_label = tk.Label(input_frame, text="(支持拖放HTML文件)", pady=5) + drop_label.pack(side="left", padx=5) + + # 版本选择框架 + version_frame = tk.LabelFrame(self.vuln_export_frame, text="绿盟版本", padx=10, pady=5) + version_frame.pack(fill="x", padx=10, pady=5) + + self.lvmeng_version = tk.StringVar(value="new") + new_radio = tk.Radiobutton(version_frame, text="新版绿盟", + variable=self.lvmeng_version, value="new") + new_radio.pack(side="left", padx=5) + old_radio = tk.Radiobutton(version_frame, text="旧版绿盟", + variable=self.lvmeng_version, value="old") + old_radio.pack(side="left", padx=5) + + # 样式选择框架 + style_frame = tk.LabelFrame(self.vuln_export_frame, text="导出样式", padx=10, pady=5) + style_frame.pack(fill="x", padx=10, pady=5) + + self.lvmeng_style = tk.StringVar(value="style1") + style1_radio = tk.Radiobutton(style_frame, text="样式一(简单表格)", + variable=self.lvmeng_style, value="style1") + style1_radio.pack(side="left", padx=5) + style2_radio = tk.Radiobutton(style_frame, text="样式二(复杂表格)", + variable=self.lvmeng_style, value="style2") + style2_radio.pack(side="left", padx=5) + + # 输出文件框架 + output_frame = tk.LabelFrame(self.vuln_export_frame, text="输出Excel文件", padx=10, pady=5) + output_frame.pack(fill="x", padx=10, pady=5) + + self.lvmeng_output_path = tk.StringVar() + self.lvmeng_output_entry = tk.Entry(output_frame, textvariable=self.lvmeng_output_path, width=80) + self.lvmeng_output_entry.pack(side="left", padx=5) + + browse_output_btn = tk.Button(output_frame, text="浏览", command=self.browse_lvmeng_output) + browse_output_btn.pack(side="left", padx=5) + + # 导出按钮 + export_btn = tk.Button(self.vuln_export_frame, text="导出Excel", + command=self.export_lvmeng_to_excel) + export_btn.pack(pady=10) + + # 漏扫提取状态标签 + self.lvmeng_status_var = tk.StringVar() + lvmeng_status_label = tk.Label(self.vuln_export_frame, + textvariable=self.lvmeng_status_var, fg="green") + lvmeng_status_label.pack(pady=5) + + # 日志框 + log_frame = tk.LabelFrame(self.vuln_export_frame, text="运行日志", padx=10, pady=5) + log_frame.pack(fill="both", expand=True, padx=10, pady=5) + + # 创建文本框和滚动条 + self.lvmeng_log_text = tk.Text(log_frame, height=12, width=80) + scrollbar = tk.Scrollbar(log_frame, orient="vertical", command=self.lvmeng_log_text.yview) + self.lvmeng_log_text.configure(yscrollcommand=scrollbar.set) + + # 放置文本框和滚动条 + scrollbar.pack(side="right", fill="y") + self.lvmeng_log_text.pack(side="left", fill="both", expand=True) + + # 文件拖放支持 + self.lvmeng_input_entry.drop_target_register(DND_FILES) + self.lvmeng_input_entry.dnd_bind('<>', self.handle_lvmeng_file_drop) + + def browse_lvmeng_input(self): + """浏览并选择HTML输入文件""" + filename = filedialog.askopenfilename( + filetypes=[("HTML文件", "*.html"), ("所有文件", "*.*")] + ) + if filename: + self.lvmeng_input_path.set(filename) + # 自动设置输出文件名 + output_path = Path(filename).with_suffix('.xlsx') + self.lvmeng_output_path.set(str(output_path)) + self.lvmeng_log("已选择输入文件: " + filename) + + def browse_lvmeng_output(self): + """浏览并选择Excel输出文件""" + filename = filedialog.asksaveasfilename( + defaultextension=".xlsx", + filetypes=[("Excel文件", "*.xlsx"), ("所有文件", "*.*")] + ) + if filename: + self.lvmeng_output_path.set(filename) + self.lvmeng_log("已选择输出文件: " + filename) + + def handle_lvmeng_file_drop(self, event): + """处理拖放HTML文件""" + file_path = event.data + self.lvmeng_log(f"收到拖放文件: {file_path}") + + # 移除花括号并转换为Path对象 + file_path = Path(file_path.strip('{}')) + + # 检查文件扩展名(不区分大小写) + if file_path.suffix.lower() in ('.html', '.htm'): + self.lvmeng_input_path.set(str(file_path)) + output_path = file_path.with_suffix('.xlsx') + self.lvmeng_output_path.set(str(output_path)) + self.lvmeng_log(f"已设置输入文件: {file_path}") + self.lvmeng_log(f"已设置输出文件: {output_path}") + else: + self.lvmeng_log(f"无效的文件类型: {file_path}", "ERROR") + + def lvmeng_log(self, message, level="INFO"): + """添加日志消息到漏扫提取日志框""" + timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + self.lvmeng_log_text.insert("end", f"[{timestamp}] [{level}] {message}\n") + self.lvmeng_log_text.see("end") # 自动滚动到最新消息 + + def export_lvmeng_to_excel(self): + """导出漏扫结果到Excel""" + input_file = self.lvmeng_input_path.get() + output_file = self.lvmeng_output_path.get() + version = self.lvmeng_version.get() + style = self.lvmeng_style.get() + + if not input_file or not output_file: + messagebox.showwarning("警告", "请选择输入和输出文件。") + self.lvmeng_log("请选择输入和输出文件!", "ERROR") + return + + if not Path(input_file).exists(): + messagebox.showwarning("警告", "输入文件不存在。") + self.lvmeng_log("输入文件不存在!", "ERROR") + return + + try: + self.lvmeng_log(f"开始处理文件: {input_file}") + self.lvmeng_log(f"导出版本: {'新版绿盟' if version == 'new' else '旧版绿盟'}") + self.lvmeng_log(f"导出样式: {'样式一' if style == 'style1' else '样式二'}") + + # 根据版本和样式选择处理方法 + if version == "new": + self.process_new_lvmeng(input_file, output_file, style) + else: + self.process_old_lvmeng(input_file, output_file, style) + + self.lvmeng_status_var.set(f"漏洞数据已成功导出到: {output_file}") + messagebox.showinfo("完成", f"漏洞数据已成功导出到: {output_file}") + except Exception as e: + error_msg = f"导出过程中出现错误: {str(e)}" + self.lvmeng_log(error_msg, "ERROR") + self.lvmeng_status_var.set("导出失败") + messagebox.showerror("错误", error_msg) + + def process_new_lvmeng(self, input_file, output_file, style): + """处理新版绿盟漏扫结果并导出到Excel""" + try: + # 读取HTML文件 + with open(input_file, 'r', encoding='utf-8') as f: + file_content = f.read() + + # 提取JSON数据 + self.lvmeng_log("正在提取JSON数据...") + pat_list = re.findall(r'', file_content) + if not pat_list: + raise Exception("未在HTML文件中找到匹配的JSON数据") + + data_json = json.loads(pat_list[0]) + + # 获取漏洞列表 + vuln_list = data_json["categories"][3]["children"][0]["data"]["vulns_info"]["vuln_distribution"]["vuln_list"] + self.lvmeng_log(f"成功提取到 {len(vuln_list)} 个漏洞") + + # 创建Excel工作簿 + wb = Workbook() + ws = wb.active + ws.title = "漏洞信息" + + # 定义样式 + # 定义对齐方式(水平居中,垂直居中) + alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) + # 定义对齐方式(水平左对齐,垂直居中) + horLeft_alignment = Alignment(horizontal='left', vertical='center', wrap_text=True) + # 单独定义字体加粗 + font_bold = Font(bold=True) + # 单独定义颜色字体 + font_red = Font(bold=False, color='E42B00') + font_orange = Font(bold=False, color='AF6100') + font_gray = Font(bold=False, color='737373') + # 定义边框样式 + thin_border = Border( + left=Side(style='thin'), + right=Side(style='thin'), + top=Side(style='thin'), + bottom=Side(style='thin') + ) + + if style == 'style1': + # 样式一:简单表格 + self.lvmeng_log("使用样式一(简单表格)导出...") + + # 添加表头 + headers = ['序号', '漏洞名称', '漏洞等级', '影响主机个数', '受影响主机', '详细描述', '解决办法'] + for col_idx, header in enumerate(headers, 1): + cell = ws.cell(row=1, column=col_idx, value=header) + cell.alignment = alignment + cell.font = font_bold + cell.border = thin_border + + # 设置列宽 + ws.column_dimensions['A'].width = 7 + ws.column_dimensions['B'].width = 45 + ws.column_dimensions['C'].width = 15 + ws.column_dimensions['D'].width = 15 + ws.column_dimensions['E'].width = 40 + ws.column_dimensions['F'].width = 50 + ws.column_dimensions['G'].width = 50 + + # 添加数据 + for idx, vuln in enumerate(vuln_list, 1): + # 获取漏洞等级中文 + level_map = {'high': '高危', 'middle': '中危', 'low': '低危'} + level = level_map.get(vuln.get('vuln_level', ''), '未知') + + # 处理描述和解决方案 + description = '\n'.join(filter(None, vuln.get('i18n_description', []))) + solution = '\n'.join(filter(None, vuln.get('i18n_solution', []))) + + # 添加行数据 + row_data = [ + idx, + vuln.get('i18n_name', ''), + level, + vuln.get('vuln_count', 0), + vuln.get('target', ''), + description, + solution + ] + + row_idx = idx + 1 + for col_idx, value in enumerate(row_data, 1): + cell = ws.cell(row=row_idx, column=col_idx, value=value) + cell.border = thin_border + + # 设置对齐方式 + if col_idx in [1, 3, 4]: # 序号、漏洞等级、影响主机个数居中 + cell.alignment = alignment + else: # 其他左对齐 + cell.alignment = horLeft_alignment + + # 设置漏洞等级颜色 + if col_idx in [2, 3]: # 漏洞名称和等级 + if level == '高危': + cell.font = font_red + elif level == '中危': + cell.font = font_orange + elif level == '低危': + cell.font = font_gray + + else: + # 样式二:复杂表格 + self.lvmeng_log("使用样式二(复杂表格)导出...") + + # 添加表头 + headers = ['序号', '漏洞名称', '漏洞等级', '影响主机个数'] + for col_idx, header in enumerate(headers, 1): + cell = ws.cell(row=1, column=col_idx, value=header) + cell.alignment = alignment + cell.font = font_bold + cell.border = thin_border + + # 设置列宽 + ws.column_dimensions['A'].width = 7 + ws.column_dimensions['B'].width = 45 + ws.column_dimensions['C'].width = 15 + ws.column_dimensions['D'].width = 50 + + current_row = 2 + for idx, vuln in enumerate(vuln_list, 1): + # 获取漏洞等级中文 + level_map = {'high': '高危', 'middle': '中危', 'low': '低危'} + level = level_map.get(vuln.get('vuln_level', ''), '未知') + + # 处理描述和解决方案 + description = '\n'.join(filter(None, vuln.get('i18n_description', []))) + solution = '\n'.join(filter(None, vuln.get('i18n_solution', []))) + + # 添加主行数据 + row_data = [ + idx, + vuln.get('i18n_name', ''), + level, + vuln.get('vuln_count', 0) + ] + + for col_idx, value in enumerate(row_data, 1): + cell = ws.cell(row=current_row, column=col_idx, value=value) + cell.border = thin_border + + # 设置对齐方式 + if col_idx == 2: # 漏洞名称左对齐 + cell.alignment = horLeft_alignment + else: # 其他居中 + cell.alignment = alignment + + # 设置漏洞等级颜色 + if col_idx in [2, 3]: # 漏洞名称和等级 + if level == '高危': + cell.font = font_red + elif level == '中危': + cell.font = font_orange + elif level == '低危': + cell.font = font_gray + + # 添加详细信息行 + details = [ + ('受影响主机', vuln.get('target', '')), + ('详细描述', description), + ('解决办法', solution) + ] + + # 计算实际需要的行数 + total_rows = len(details) + + # 合并第一列的单元格 + if total_rows > 0: + ws.merge_cells(f'A{current_row+1}:A{current_row+total_rows}') + merged_cell = ws.cell(row=current_row+1, column=1) + merged_cell.border = thin_border + + # 添加所有详细信息 + for i, (label, value) in enumerate(details): + row_num = current_row + i + 1 + + # 添加标签(第二列) + label_cell = ws.cell(row=row_num, column=2) + label_cell.value = label + label_cell.font = font_bold + label_cell.border = thin_border + label_cell.alignment = alignment # 标签居中对齐 + + # 添加值(第三列)- 保持为空 + value_cell = ws.cell(row=row_num, column=3) + value_cell.border = thin_border + + # 第四列设置值和自动换行 + value_cell = ws.cell(row=row_num, column=4) + value_cell.value = value + value_cell.border = thin_border + value_cell.alignment = horLeft_alignment # 自动换行 + + current_row += total_rows + 1 # 更新行号,加1是为了下一组数据之间留空 + + # 保存Excel文件 + wb.save(output_file) + self.lvmeng_log(f"成功导出到Excel文件: {output_file}", "SUCCESS") + return True + + except Exception as e: + self.lvmeng_log(f"处理新版绿盟漏扫结果时出错: {str(e)}", "ERROR") + raise + + def process_old_lvmeng(self, input_file, output_file, style): + """处理旧版绿盟漏扫结果并导出到Excel""" + try: + # 读取HTML文件 + with open(input_file, 'r', encoding='utf-8') as f: + html_content = f.read() + + # 使用BeautifulSoup解析HTML + self.lvmeng_log("正在解析HTML文件...") + soup = BeautifulSoup(html_content, 'html.parser') + + # 查找漏洞分布表格 + vuln_table = soup.find('table', id='vuln_distribution') + if not vuln_table: + raise Exception("未找到漏洞分布表格,请确认是否为旧版绿盟漏扫结果") + + # 查找所有漏洞行 + vuln_rows = vuln_table.find_all('tr', class_=re.compile(r'(odd|even) vuln_(high|middle|low)')) + self.lvmeng_log(f"成功提取到 {len(vuln_rows)} 个漏洞") + + # 存储漏洞数据 + vulnerability_data = [] + + for row in vuln_rows: + # 提取基本信息 + tds = row.find_all('td') + number = tds[0].text.strip() + name = row.find('span').text.strip() + affected_hosts_count = tds[2].text.strip() + + # 确定漏洞等级 + if 'vuln_high' in row['class']: + level = '高危' + elif 'vuln_middle' in row['class']: + level = '中危' + else: + level = '低危' + + # 获取详细信息所在的下一个tr + detail_row = row.find_next('tr', class_=re.compile(r'more hide (odd|even)')) + + # 初始化变量 + affected_hosts_str = "" + description = "" + solution = "" + additional_fields = { + '威胁分值': '', + '危险插件': '', + '发现日期': '', + 'CVE编号': '', + 'CNNVD编号': '', + 'CNCVE编号': '', + 'CVSS评分': '', + 'CNVD编号': '' + } + + if detail_row: + # 提取受影响主机 + affected_hosts = [] + # 先查找所有标签的主机 + host_links = detail_row.find_all('a', href=re.compile(r'host/.*\.html')) + if host_links: # 如果找到标签 + for link in host_links: + affected_hosts.append(link.text.strip()) + else: # 如果没有标签,查找特定的td标签 + # 查找class为report_table的table下的width为80%的td + report_table = detail_row.find('table', class_='report_table') + if report_table: + host_td = report_table.find('td', attrs={'width': '80%'}) + if host_td: + # 替换所有 为空格,然后获取文本 + hosts_text = host_td.text.replace(' ', ' ').strip() + # 如果文本不为空,添加到列表 + if hosts_text: + affected_hosts.append(hosts_text) + + # 将所有主机信息合并,用逗号分隔 + affected_hosts_str = ', '.join(affected_hosts) + + # 提取详细描述 + description_row = detail_row.find('tr', class_='even') + description = description_row.find('td').text.strip() if description_row else '' + + # 提取解决办法 + solution_row = description_row.find_next('tr', class_='odd') if description_row else None + solution = solution_row.find('td').text.strip() if solution_row else '' + + # 查找所有可能包含新字段的行 + info_rows = detail_row.find_all('tr', class_=re.compile(r'(odd|even)')) + for info_row in info_rows: + field_name = info_row.find('th') + if field_name and field_name.text.strip() in additional_fields: + field_value = info_row.find('td') + if field_value: + additional_fields[field_name.text.strip()] = field_value.text.strip() + + # 添加到漏洞数据列表 + vulnerability_data.append({ + '序号': number, + '漏洞名称': name, + '漏洞等级': level, + '影响主机个数': affected_hosts_count, + '受影响主机': affected_hosts_str, + '详细描述': description, + '解决办法': solution, + **additional_fields # 添加新字段 + }) + + # 创建Excel工作簿 + wb = Workbook() + ws = wb.active + ws.title = "漏洞信息" + + # 设置边框样式 + border = Border( + left=Side(style='thin'), + right=Side(style='thin'), + top=Side(style='thin'), + bottom=Side(style='thin') + ) + + # 设置表头字体 + header_font = Font(bold=True) + + # 设置不同等级的字体颜色 + high_font = Font(color='FF0000') # 红色 + middle_font = Font(color='FFA500') # 橙色 + low_font = Font(color='808080') # 灰色 + + if style == 'style1': + # 样式一:简单表格 + self.lvmeng_log("使用样式一(简单表格)导出...") + + # 添加表头 + headers = ['序号', '漏洞名称', '漏洞等级', '影响主机个数', '受影响主机', '详细描述', '解决办法'] + ws.append(headers) + + # 设置表头格式 + for cell in ws[1]: + cell.font = header_font + cell.border = border + + # 添加数据并设置格式 + for row_data in vulnerability_data: + row = [row_data[h] for h in headers] + ws.append(row) + row_num = ws.max_row + + # 设置单元格边框和字体颜色 + for col in range(1, len(headers) + 1): + cell = ws.cell(row=row_num, column=col) + cell.border = border + + # 设置漏洞名称和等级的字体颜色 + if col in [2, 3]: # 漏洞名称和漏洞等级列 + level = row_data['漏洞等级'] + if level == '高危': + cell.font = high_font + elif level == '中危': + cell.font = middle_font + else: + cell.font = low_font + + # 调整列宽 + for column in ws.columns: + max_length = 0 + column = [cell for cell in column] + for cell in column: + try: + if len(str(cell.value)) > max_length: + max_length = len(str(cell.value)) + except: + pass + adjusted_width = (max_length + 2) + ws.column_dimensions[get_column_letter(column[0].column)].width = adjusted_width + + else: + # 样式二:复杂表格 + self.lvmeng_log("使用样式二(复杂表格)导出...") + + # 设置居中对齐 + center_alignment = Alignment(horizontal='center', vertical='center') + # 设置自动换行对齐 + wrap_alignment = Alignment(horizontal='left', vertical='center', wrap_text=True) + + # 添加表头 + headers = ['序号', '漏洞名称', '漏洞等级', '影响主机个数'] + ws.append(headers) + + # 设置表头格式 + for cell in ws[1]: + cell.font = header_font + cell.border = border + cell.alignment = center_alignment + + # 设置固定列宽 + ws.column_dimensions['A'].width = 7 # 第一列 + ws.column_dimensions['B'].width = 45 # 第二列 + ws.column_dimensions['C'].width = 15 # 第三列 + ws.column_dimensions['D'].width = 50 # 第四列 + + current_row = 2 + for row_data in vulnerability_data: + # 添加主行 + row = [row_data['序号'], row_data['漏洞名称'], row_data['漏洞等级'], row_data['影响主机个数']] + for col, value in enumerate(row, 1): + cell = ws.cell(row=current_row, column=col) + cell.value = value + cell.border = border + cell.alignment = center_alignment # 第一行所有单元格居中 + + # 第一行第二个单元格设置自动换行 + if col == 2: + cell.alignment = wrap_alignment + + # 设置漏洞名称和等级的字体颜色 + if col in [2, 3]: # 漏洞名称和漏洞等级列 + level = row_data['漏洞等级'] + if level == '高危': + cell.font = high_font + elif level == '中危': + cell.font = middle_font + else: + cell.font = low_font + + # 添加详细信息行 + details = [ + ('受影响主机', row_data['受影响主机']), + ('详细描述', row_data['详细描述']), + ('解决办法', row_data['解决办法']) + ] + + # 添加新字段(只添加有值的字段) + additional_fields = [ + ('威胁分值', row_data['威胁分值']), + ('危险插件', row_data['危险插件']), + ('发现日期', row_data['发现日期']), + ('CVE编号', row_data['CVE编号']), + ('CNNVD编号', row_data['CNNVD编号']), + ('CNCVE编号', row_data['CNCVE编号']), + ('CVSS评分', row_data['CVSS评分']), + ('CNVD编号', row_data['CNVD编号']) + ] + + # 过滤掉空值的字段 + details.extend([(label, value) for label, value in additional_fields if value]) + + # 计算实际需要的行数 + total_rows = len(details) + + # 合并第一列的单元格 + if total_rows > 0: + ws.merge_cells(f'A{current_row+1}:A{current_row+total_rows}') + merged_cell = ws.cell(row=current_row+1, column=1) + merged_cell.border = border + + # 添加所有详细信息 + for i, (label, value) in enumerate(details): + row_num = current_row + i + 1 + + # 添加标签(第二列) + label_cell = ws.cell(row=row_num, column=2) + label_cell.value = label + label_cell.font = header_font + label_cell.border = border + label_cell.alignment = center_alignment # 标签居中对齐 + + # 添加值(第三列)- 保持为空 + value_cell = ws.cell(row=row_num, column=3) + value_cell.border = border + + # 第四列设置值和自动换行 + value_cell = ws.cell(row=row_num, column=4) + value_cell.value = value + value_cell.border = border + value_cell.alignment = wrap_alignment # 自动换行 + + current_row += total_rows + 1 # 更新行号,加1是为了下一组数据之间留空 + + # 保存Excel文件 + wb.save(output_file) + self.lvmeng_log(f"成功导出到Excel文件: {output_file}", "SUCCESS") + return True + + except Exception as e: + self.lvmeng_log(f"处理旧版绿盟漏扫结果时出错: {str(e)}", "ERROR") + raise + if __name__ == "__main__": root = TkinterDnD.Tk() app = JsonExtractorGUI(root) diff --git a/requirements.txt b/requirements.txt index 96efba0cdeb6e392a1b23dfaec577dffedf21751..2307f6a2a8fb511a637695b2ac70a8f48d639973 100644 GIT binary patch literal 450 zcmY*V?Fzy$5X*1DM^W~r;t!ui6gwSm-EdW=53eRy#YwpqdPy$1*K-et9j*wdQ1Y$O zqvhM7z=o&594T_%Ii{Ep9k9iKhRBkfnm@v2L?l*1c`7v2xQ{xS@mu~n_SGB67FX&i zYhj3vemoAmd+SPB73Yf=aH2<#nV|ypqW`ES?3yJv9dSMJ$wyh-$451xs?tSYJ+lnf zk&_fFQ&5)gK7??%jq)x{HQ>ySr?SI^j^sTvB{}#_2z1^V^2iN3P7}jZzp^wjZQOUv Ptm*QrT?AI#^S|#m(MCi+ literal 149 zcmX|)!3x755Jd0(OBda=lpgk5il8M@vo3W5ny-&i$Z_U9W}?2^mOm*+A_NQ8@V-g^ z2Q)<@OK+KI0S6Bwo`bKGA}mI_u7z*GYuq&r3E?$1q*S#N9;2~%2Q)P`A_wj{4)mK? Q<1cVPA`27iF*f{VpM+y9i~s-t