Refine security score calculation logic and enforce mandatory fields for findings in JSON format
Build and Push CodeReview / build (push) Waiting to run Details

This commit is contained in:
vinland100 2026-01-12 14:22:06 +08:00
parent b373692577
commit 7f951d5451
3 changed files with 142 additions and 23 deletions

View File

@ -1354,37 +1354,96 @@ async def _save_findings(
def _calculate_security_score(findings: List[Dict]) -> float:
"""计算安全评分
评分逻辑 100 分开始根据漏洞严重程度扣分
- Critical: -25
- High: -15
- Medium: -8
- Low: -3
- Info: -1
评分逻辑 100 分开始根据漏洞严重程度和数量扣分
🔥 FIX: 确保 severity 转换为小写后再匹配 deductions 字典
使用递减扣分机制同类漏洞越多每个额外漏洞的扣分越少
这样可以避免发现多个漏洞就直接得0分的问题
扣分规则第N个同类漏洞的扣分
- Critical: 第1个-20, 第2个-15, 第3个-10, 第4个及以后-5
- High: 第1个-12, 第2个-8, 第3个-5, 第4个及以后-3
- Medium: 第1个-5, 第2个-3, 第3个及以后-2
- Low: 第1个-2, 第2个及以后-1
- Info: 每个-0.5
最高扣分上限为85分确保最低得分不会低于15分
"""
if not findings:
return 100.0
# 基于发现的严重程度计算扣分
deductions = {
"critical": 25,
"high": 15,
"medium": 8,
"low": 3,
"info": 1,
# 统计各严重程度的数量
severity_counts = {
"critical": 0,
"high": 0,
"medium": 0,
"low": 0,
"info": 0,
}
total_deduction = 0
for f in findings:
if isinstance(f, dict):
# 🔥 FIX: 将 severity 转换为小写,确保能正确匹配 deductions 字典
raw_sev = f.get("severity") or f.get("risk") or "low"
sev = str(raw_sev).lower().strip()
deduction = deductions.get(sev, 3) # 默认使用 low 的扣分
total_deduction += deduction
if sev in severity_counts:
severity_counts[sev] += 1
else:
severity_counts["low"] += 1 # 未知严重程度按 low 处理
# 计算递减扣分
total_deduction = 0.0
# Critical: 20, 15, 10, 5, 5, ...
for i in range(severity_counts["critical"]):
if i == 0:
total_deduction += 20
elif i == 1:
total_deduction += 15
elif i == 2:
total_deduction += 10
else:
total_deduction += 5
# High: 12, 8, 5, 3, 3, ...
for i in range(severity_counts["high"]):
if i == 0:
total_deduction += 12
elif i == 1:
total_deduction += 8
elif i == 2:
total_deduction += 5
else:
total_deduction += 3
# Medium: 5, 3, 2, 2, ...
for i in range(severity_counts["medium"]):
if i == 0:
total_deduction += 5
elif i == 1:
total_deduction += 3
else:
total_deduction += 2
# Low: 2, 1, 1, ...
for i in range(severity_counts["low"]):
if i == 0:
total_deduction += 2
else:
total_deduction += 1
# Info: 0.5 each
total_deduction += severity_counts["info"] * 0.5
# 最高扣分上限为85分
total_deduction = min(total_deduction, 85.0)
score = max(0, 100 - total_deduction)
# 添加日志用于调试
logger.debug(
f"[SecurityScore] Counts: {severity_counts}, "
f"Deduction: {total_deduction:.1f}, Score: {score:.1f}"
)
return float(score)

View File

@ -144,7 +144,7 @@ Final Answer: {
"文件路径:行号 - 风险描述"
],
"initial_findings": [
{"title": "...", "file_path": "...", "line_start": ..., "description": "..."}
{"title": "...", "file_path": "...", "line_start": ..., "description": "...", "vulnerability_type": "hardcoded_secret/sql_injection/...", "severity": "critical/high/medium/low"}
],
"summary": "项目侦察总结"
}
@ -167,11 +167,35 @@ Final Answer: {
**禁止**输出纯描述性文本如 "File write operations with user-controlled paths"必须指明具体文件
### initial_findings 格式要求
🚨 **必须包含以下字段用于安全评分计算**
每个发现**必须**包含
- `title`: 漏洞标题
- `file_path`: 具体文件路径
- `file_path`: 具体文件路径必填
- `line_start`: 行号
- `description`: 详细描述
- `vulnerability_type`: 漏洞类型使用下划线格式 `sql_injection`, `command_injection`, `ssrf`, `hardcoded_secret`
- `severity`: 严重程度**必须使用小写**`critical`, `high`, `medium`, `low`
示例
```json
{
"title": "明文密码存储",
"file_path": "config/settings.py",
"line_start": 25,
"description": "配置文件中硬编码了数据库密码",
"vulnerability_type": "hardcoded_secret",
"severity": "high"
}
```
错误格式导致安全评分无法计算
```json
{"severity": "HIGH", ...} // 应该用小写 "high"
{"severity": "Critical", ...} // 应该用小写 "critical"
{"type": "sqli", ...} // 应该用 "vulnerability_type": "sql_injection"
```
## 🚨 防止幻觉(关键!)

View File

@ -266,11 +266,21 @@ Action Input: {"file_path": "search.php"}
3. Action Input 必须是完整的 JSON 对象不能为空或截断
## Final Answer 格式
🚨 **必须严格遵守以下字段格式否则系统无法正确计算安全评分**
```json
{
"findings": [
{
...原始发现字段...,
"id": 1,
"vulnerability_type": "sql_injection/xss/command_injection/ssrf/path_traversal/hardcoded_secret/...",
"severity": "critical/high/medium/low/info",
"title": "漏洞标题",
"description": "漏洞详细描述",
"file_path": "相对文件路径",
"line_start": 行号,
"code_snippet": "漏洞代码片段",
"verdict": "confirmed/likely/uncertain/false_positive",
"confidence": 0.0-1.0,
"is_verified": true/false,
@ -295,6 +305,32 @@ Action Input: {"file_path": "search.php"}
}
```
### ⚠️ 必填字段要求
| 字段 | 要求 | 示例 |
|------|------|------|
| `vulnerability_type` | **必填**使用下划线格式 | `sql_injection`, `command_injection`, `ssrf`, `xss`, `hardcoded_secret` |
| `severity` | **必填**必须使用**小写** | `critical`, `high`, `medium`, `low`, `info` |
| `title` | **必填**简洁描述漏洞 | `SQL注入漏洞 in search.php` |
| `file_path` | **必填**相对路径 | `src/controllers/user.py` |
| `verdict` | **必填** | `confirmed`, `likely`, `uncertain`, `false_positive` |
### ❌ 常见错误(导致安全评分计算失败)
```json
// 错误使用大写 severity
{"severity": "HIGH", ...} // 应该用 "high"
{"severity": "Critical", ...} // 应该用 "critical"
// 错误使用 type 而不是 vulnerability_type
{"type": "sql_injection", ...} // 应该用 "vulnerability_type"
// 错误severity 值拼写错误
{"severity": "critial", ...} // 拼写错误
{"severity": "unknown", ...} // 无效值
```
## 验证判定标准
- **confirmed**: 漏洞确认存在且可利用有明确证据 Harness 成功触发
- **likely**: 高度可能存在漏洞代码分析明确但无法动态验证