From 7f951d54512bc2b2fddc4eda2bb5802a7dbd5136 Mon Sep 17 00:00:00 2001 From: vinland100 Date: Mon, 12 Jan 2026 14:22:06 +0800 Subject: [PATCH] Refine security score calculation logic and enforce mandatory fields for findings in JSON format --- backend/app/api/v1/endpoints/agent_tasks.py | 99 +++++++++++++++---- backend/app/services/agent/agents/recon.py | 28 +++++- .../app/services/agent/agents/verification.py | 38 ++++++- 3 files changed, 142 insertions(+), 23 deletions(-) diff --git a/backend/app/api/v1/endpoints/agent_tasks.py b/backend/app/api/v1/endpoints/agent_tasks.py index d56ddc0..8c57b7e 100644 --- a/backend/app/api/v1/endpoints/agent_tasks.py +++ b/backend/app/api/v1/endpoints/agent_tasks.py @@ -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) diff --git a/backend/app/services/agent/agents/recon.py b/backend/app/services/agent/agents/recon.py index b83cc41..01a7c0e 100644 --- a/backend/app/services/agent/agents/recon.py +++ b/backend/app/services/agent/agents/recon.py @@ -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" +``` ## 🚨 防止幻觉(关键!) diff --git a/backend/app/services/agent/agents/verification.py b/backend/app/services/agent/agents/verification.py index 3ad62c7..16da14f 100644 --- a/backend/app/services/agent/agents/verification.py +++ b/backend/app/services/agent/agents/verification.py @@ -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**: 高度可能存在漏洞,代码分析明确但无法动态验证