fix(report_generator): 转义HTML特殊字符以防止XSS攻击

添加_html_escape方法并对报告中的用户输入数据进行转义处理,包括代码片段、描述、建议、标题和文件路径,以提高安全性
This commit is contained in:
lintsinghua 2025-12-19 11:19:40 +08:00
parent 220b5f793a
commit 89ebd4d797
1 changed files with 18 additions and 4 deletions

View File

@ -3,6 +3,7 @@ PDF 报告生成服务 - 专业审计版 (WeasyPrint)
""" """
import io import io
import html
from datetime import datetime from datetime import datetime
from typing import List, Dict, Any from typing import List, Dict, Any
import math import math
@ -397,19 +398,26 @@ class ReportGenerator:
return "" return ""
return "" return ""
@classmethod
def _escape_html(cls, text: str) -> str:
"""安全转义 HTML 特殊字符"""
if text is None:
return None
return html.escape(str(text))
@classmethod @classmethod
def _process_issues(cls, issues: List[Dict]) -> List[Dict]: def _process_issues(cls, issues: List[Dict]) -> List[Dict]:
processed = [] processed = []
order = {'critical': 0, 'high': 1, 'medium': 2, 'low': 3} order = {'critical': 0, 'high': 1, 'medium': 2, 'low': 3}
sorted_issues = sorted(issues, key=lambda x: order.get(x.get('severity', 'low'), 4)) sorted_issues = sorted(issues, key=lambda x: order.get(x.get('severity', 'low'), 4))
sev_labels = { sev_labels = {
'critical': 'CRITICAL', 'critical': 'CRITICAL',
'high': 'HIGH', 'high': 'HIGH',
'medium': 'MEDIUM', 'medium': 'MEDIUM',
'low': 'LOW' 'low': 'LOW'
} }
for i in sorted_issues: for i in sorted_issues:
item = i.copy() item = i.copy()
item['severity'] = item.get('severity', 'low') item['severity'] = item.get('severity', 'low')
@ -420,18 +428,24 @@ class ReportGenerator:
code = item.get('code_snippet') or item.get('code') or item.get('context') code = item.get('code_snippet') or item.get('code') or item.get('context')
if isinstance(code, list): if isinstance(code, list):
code = '\n'.join(code) code = '\n'.join(code)
item['code_snippet'] = code if code else None item['code_snippet'] = cls._escape_html(code) if code else None
# 确保 description 不为 None # 确保 description 不为 None
desc = item.get('description') desc = item.get('description')
if not desc or desc == 'None': if not desc or desc == 'None':
desc = item.get('title', '') # 如果没有描述,使用标题 desc = item.get('title', '') # 如果没有描述,使用标题
item['description'] = desc item['description'] = cls._escape_html(desc)
# 确保 suggestion 不为 None # 确保 suggestion 不为 None
suggestion = item.get('suggestion') suggestion = item.get('suggestion')
if suggestion == 'None' or suggestion is None: if suggestion == 'None' or suggestion is None:
item['suggestion'] = None item['suggestion'] = None
else:
item['suggestion'] = cls._escape_html(suggestion)
# 转义标题和文件路径
item['title'] = cls._escape_html(item.get('title', ''))
item['file_path'] = cls._escape_html(item.get('file_path'))
processed.append(item) processed.append(item)
return processed return processed