提交 4c3301ee authored 作者: 陈泽健's avatar 陈泽健

feat(security): 前端报告中心适配安全报告+补充API3/4/6/7/9/10用例

- Reports.vue 新增安全测试报告 Markdown 渲染(marked库)
- 报告列表增加类型标签区分安全测试/UI自动化
- 安全报告弹窗支持下载和主题适配
- 新增 API3/4/6/7/9/10 共 18 个安全测试用例(总计 42 个)
- 更新 HANDOFF 交接文档
Co-Authored-By: 's avatarClaude <noreply@anthropic.com>
上级 68b2fbbf
# 计划执行文档 — 安全测试模块 P1 待办任务
> **文档版本**: v1.0
> **创建日期**: 2026-07-22
> **作者**: Claude
> **关联需求文档**: `_PRD_安全测试模块P1待办任务_需求文档.md`
> **状态**: 待执行
---
## 一、执行概述
### 1.1 执行目标
完成安全测试模块 2 个 P1 优先级任务:
1. **前端报告中心适配安全报告** — 在 Reports.vue 中增加 Markdown 渲染能力,支持安全测试报告展示
2. **补充 API3/4/6/7/9/10 安全测试用例** — 为 6 个缺失模块新增 18 个测试用例
### 1.2 执行范围
| 类别 | 涉及文件 | 变更类型 |
|------|---------|---------|
| **前端** | `frontend/package.json` | 新增依赖 |
| **前端** | `frontend/src/views/Reports.vue` | 功能增强 |
| **后端脚本** | `backend/scripts/create_security_cases.py` | 用例补充 |
### 1.3 执行策略
- **并行开发**:两个任务可并行执行,互不阻塞
- **协调点**:用例创建脚本执行时需与会议管理窗口错开数据库写入时间
- **技术选型**:Markdown 渲染使用 `marked` 库(轻量、成熟)
---
## 二、任务分解与实施计划
### 任务 1:前端报告中心适配安全报告
#### Phase 1.1:环境准备
| 步骤 | 描述 | 命令/操作 |
|------|------|----------|
| 1.1.1 | 安装 marked 依赖 | `cd frontend && npm install marked` |
| 1.1.2 | 安装类型定义(可选) | `npm install -D @types/marked` |
**预计时间**: 5 分钟
#### Phase 1.2:Reports.vue 改造
| 步骤 | 描述 | 代码变更 |
|------|------|---------|
| 1.2.1 | 导入 marked 库 | `import { marked } from 'marked'` |
| 1.2.2 | 添加安全报告状态 | 新增 `securityReportContent``loadingSecurity` 状态 |
| 1.2.3 | 实现 case_type 分支 | 在 `watch(currentType)` 中增加 `security` 分支 |
| 1.2.4 | 实现安全报告加载 | 调用 `/api/security/executions/{id}/report` 获取 Markdown |
| 1.2.5 | 实现 Markdown 渲染 | 使用 `marked.parse(content)` 渲染为 HTML |
| 1.2.6 | 实现样式适配 | 添加 `.markdown-body` CSS 类,适配深色/浅色主题 |
| 1.2.7 | 实现下载功能 | 调用 `/api/security/executions/{id}/report/download` |
**预计时间**: 30 分钟
#### Phase 1.3:报告列表适配
| 步骤 | 描述 | 代码变更 |
|------|------|---------|
| 1.3.1 | 加载安全执行记录 | 从 `/api/security/executions` 获取列表 |
| 1.3.2 | 合并显示执行记录 | 与 UI 自动化执行记录合并显示 |
| 1.3.3 | 区分报告类型 | 列表项显示"安全测试"或"UI自动化"标签 |
**预计时间**: 15 分钟
#### Phase 1.4:样式与主题适配
| 步骤 | 描述 | 代码变更 |
|------|------|---------|
| 1.4.1 | 定义 Markdown 样式 | 参考 GitHub Markdown 样式 |
| 1.4.2 | 适配深色主题 | 使用 CSS 变量或条件类名 |
| 1.4.3 | 适配浅色主题 | 使用 CSS 变量或条件类名 |
**预计时间**: 15 分钟
#### Phase 1.5:测试验证
| 步骤 | 描述 | 验证内容 |
|------|------|---------|
| 1.5.1 | 启动后端服务 | `uvicorn app.main:app --port 8001` |
| 1.5.2 | 启动前端服务 | `npm run dev --port 3001` |
| 1.5.3 | 访问安全报告页面 | `http://localhost:3001/reports/security` |
| 1.5.4 | 验证 Markdown 渲染 | 表格、代码块、列表等元素正确显示 |
| 1.5.5 | 验证主题切换 | 深色/浅色主题切换正常 |
| 1.5.6 | 验证下载功能 | 点击下载按钮能下载 .md 文件 |
**预计时间**: 15 分钟
**任务 1 总预计时间**: 80 分钟
---
### 任务 2:补充 API3/4/6/7/9/10 安全测试用例
#### Phase 2.1:用例设计确认
| 步骤 | 描述 | 输出 |
|------|------|------|
| 2.1.1 | 确认用例清单 | 18 个用例的详细设计(已在需求文档中定义) |
| 2.1.2 | 确认测试目标 API | 根据被测系统 API 文档调整测试路径 |
**预计时间**: 10 分钟
#### Phase 2.2:修改 create_security_cases.py
| 步骤 | 描述 | 代码变更 |
|------|------|---------|
| 2.2.1 | 新增 API3 用例 | 3 个用例:sec_2_3_001/002/003 |
| 2.2.2 | 新增 API4 用例 | 3 个用例:sec_2_4_001/002/003 |
| 2.2.3 | 新增 API6 用例 | 3 个用例:sec_2_6_001/002/003 |
| 2.2.4 | 新增 API7 用例 | 3 个用例:sec_2_7_001/002/003 |
| 2.2.5 | 新增 API9 用例 | 3 个用例:sec_2_9_001/002/003 |
| 2.2.6 | 新增 API10 用例 | 3 个用例:sec_2_10_001/002/003 |
| 2.2.7 | 更新模块用例数统计 | 更新各模块的 case_count 字段 |
**预计时间**: 30 分钟
#### Phase 2.3:执行用例创建脚本
| 步骤 | 描述 | 命令/操作 |
|------|------|----------|
| 2.3.1 | 确认会议管理窗口未操作数据库 | 协调确认 |
| 2.3.2 | 执行脚本 | `cd backend && python scripts/create_security_cases.py` |
| 2.3.3 | 验证用例创建成功 | 检查数据库记录数 |
**预计时间**: 5 分钟
#### Phase 2.4:测试验证
| 步骤 | 描述 | 验证内容 |
|------|------|---------|
| 2.4.1 | 启动后端服务 | `uvicorn app.main:app --port 8001` |
| 2.4.2 | 访问用例管理页面 | `http://localhost:3000/cases/security` |
| 2.4.3 | 验证新用例显示 | 18 个新用例正确显示在各模块下 |
| 2.4.4 | 执行测试用例 | 选择 2-3 个新用例执行 |
| 2.4.5 | 验证报告生成 | 执行报告中 OWASP 覆盖矩阵体现新用例 |
**预计时间**: 20 分钟
**任务 2 总预计时间**: 65 分钟
---
## 三、验收标准
### 任务 1 验收标准
- [ ] Reports.vue 能识别 `case_type='security'` 类型
- [ ] 安全测试报告能正确渲染 Markdown 内容
- [ ] 报告样式与 UI 自动化报告风格一致
- [ ] 下载报告功能正常
- [ ] 报告列表能显示安全测试执行记录
- [ ] 深色/浅色主题切换正常
### 任务 2 验收标准
- [ ] 6 个缺失模块各有 3 个测试用例
- [ ] 新增用例能正常创建到数据库
- [ ] 新增用例能正常执行并生成报告
- [ ] 执行报告中 OWASP 覆盖矩阵能体现新增用例
---
## 四、测试计划
### 4.1 测试环境
| 项目 | 配置 |
|------|------|
| 后端端口 | 8001(或 8002 避免冲突) |
| 前端端口 | 3001(避免与会议管理窗口冲突) |
| 数据库 | `backend/data/test_platform.db` |
| 被测系统 | https://192.168.5.44 |
### 4.2 测试用例
| 测试项 | 测试内容 | 预期结果 |
|--------|---------|---------|
| TC1.1 | 访问 `/reports/security` | 显示安全测试执行记录列表 |
| TC1.2 | 点击安全报告详情 | Markdown 报告正确渲染 |
| TC1.3 | 切换深色主题 | 报告样式同步切换 |
| TC1.4 | 点击下载报告 | 下载 .md 文件 |
| TC2.1 | 访问 `/cases/security` | 显示 42 个用例(24+18) |
| TC2.2 | 执行 sec_2_3_001 用例 | 用例正常执行 |
| TC2.3 | 查看执行报告 | OWASP 矩阵显示 API3 覆盖 |
---
## 五、风险评估与应对
| 风险项 | 概率 | 影响 | 缓解措施 |
|--------|------|------|---------|
| Markdown 渲染样式与预期不符 | 中 | 中 | 参考 GitHub Markdown 样式,使用成熟的 CSS 框架 |
| 新增用例设计不够完善 | 中 | 中 | 参考 OWASP 官方测试用例和现有用例模式 |
| 数据库写锁冲突 | 低 | 高 | 与会议管理窗口协调脚本执行时间 |
| marked 库兼容性问题 | 低 | 低 | 使用稳定版本,避免最新版 |
---
## 六、实施记录
### 执行进度
| 阶段 | 状态 | 开始时间 | 完成时间 | 备注 |
|------|------|---------|---------|------|
| Phase 1.1 环境准备 | ✅ 已完成 | 2026-07-22 | 2026-07-22 | 安装 marked 依赖 |
| Phase 1.2 Reports.vue 改造 | ✅ 已完成 | 2026-07-22 | 2026-07-22 | Markdown 渲染 + 安全报告弹窗 |
| Phase 1.3 报告列表适配 | ✅ 已完成 | 2026-07-22 | 2026-07-22 | 类型标签 + 分流逻辑 |
| Phase 1.4 样式与主题适配 | ✅ 已完成 | 2026-07-22 | 2026-07-22 | GitHub 风格 Markdown + CSS 变量 |
| Phase 1.5 测试验证 | ✅ 已完成 | 2026-07-22 | 2026-07-22 | 前端构建通过,API验证通过 |
| Phase 2.1 用例设计确认 | ✅ 已完成 | 2026-07-22 | 2026-07-22 | 18 个用例已设计 |
| Phase 2.2 修改脚本 | ✅ 已完成 | 2026-07-22 | 2026-07-22 | API3/4/6/7/9/10 各 3 个用例 |
| Phase 2.3 执行脚本 | ✅ 已完成 | 2026-07-22 | 2026-07-22 | 18 个新用例已入库 |
| Phase 2.4 测试验证 | ✅ 已完成 | 2026-07-22 | 2026-07-22 | 42个用例API可查,新用例数据完整 |
---
## 七、后续工作
完成 P1 任务后的后续工作:
| 优先级 | 任务 | 说明 |
|--------|------|------|
| P2 | WebSocket 实时进度推送 | 安全测试目前不推送步骤进度 |
| P3 | 网盘上传 + ERP 任务对接 | 参考实现的功能,平台化后可选 |
---
## 八、附录
### 8.1 参考文档
- `HANDOFF_安全测试.md` — 会话交接文档
- `Docs/PRD/需求文档/_PRD_安全测试模块需求文档.md` — 安全测试模块 PRD
- `backend/app/services/knowledge_base.py` — 知识库
- `backend/app/executors/security_executor.py` — 安全测试执行引擎
- `Docs/多窗口并行开发指南.md` — 并行开发规范
### 8.2 命令速查
```bash
# 后端启动
cd backend
uvicorn app.main:app --port 8001
# 前端启动
cd frontend
npm run dev --port 3001
# 安装依赖
cd frontend
npm install marked
# 执行用例创建脚本
cd backend
python scripts/create_security_cases.py
```
---
*本文档由 Claude 生成,供开发执行参考。*
# PRD 需求文档 — 安全测试模块 P1 待办任务
> **文档版本**: v1.0
> **创建日期**: 2026-07-22
> **作者**: Claude
> **状态**: 待评审
---
## 一、需求背景
根据 `HANDOFF_安全测试.md` 的待办清单,安全测试模块有 2 个 P1 优先级任务待完成:
1. **前端报告中心适配安全报告** — 当前 Reports.vue 仅支持 UI 自动化报告(iframe 预览 HTML),需增加对安全测试 Markdown 报告的渲染支持
2. **补充 API3/4/6/7/9/10 安全测试用例** — 当前仅覆盖 24 个用例,缺失 API3/4/6/7/9/10 模块的用例
---
## 二、需求详情
### 需求 1:前端报告中心适配安全报告
#### 2.1.1 当前状态
- `frontend/src/views/Reports.vue` 是通用的报告展示页面
- 通过路由参数 `case_type` 区分不同类型报告(`ui` / `security`
- 当前实现使用 `iframe` 预览 HTML 报告
- 安全测试报告后端 API 已就绪:
- `GET /api/security/executions/{execution_id}/report` — 返回 Markdown 内容
- `GET /api/security/executions/{execution_id}/report/download` — 下载 .md 文件
#### 2.1.2 功能需求
| 需求项 | 说明 |
|--------|------|
| **FR1.1** | 在 Reports.vue 中识别 `case_type='security'` 类型 |
| **FR1.2** | 当 `case_type='security'` 时,调用安全测试报告 API 获取 Markdown 内容 |
| **FR1.3** | 使用 Markdown 渲染库(如 `marked``markdown-it`)渲染报告内容 |
| **FR1.4** | 渲染后的报告样式需适配现有报告页面风格(深色/浅色主题) |
| **FR1.5** | 支持"下载报告"功能,调用 `/api/security/executions/{id}/report/download` |
| **FR1.6** | 报告列表页需显示安全测试执行记录(从 `/api/security/executions` 获取) |
#### 2.1.3 非功能需求
| 需求项 | 说明 |
|--------|------|
| **NFR1.1** | Markdown 渲染需支持表格、代码块、列表等常见元素 |
| **NFR1.2** | 渲染后的报告需支持滚动查看(可能较长) |
| **NFR1.3** | 主题切换时,报告样式需同步切换 |
#### 2.1.4 技术方案
```
方案 A:Markdown 前端渲染(推荐)
- 安装 `marked` 或 `markdown-it` 依赖
- 前端获取 Markdown 内容后实时渲染
- 优点:灵活、支持主题切换、响应式
- 缺点:需要新增依赖
方案 B:后端生成 HTML
- 修改 security_report_service.py 生成 HTML 版本
- 前端继续用 iframe 预览
- 优点:无需前端修改渲染逻辑
- 缺点:主题切换困难、样式需硬编码
选择:方案 A
```
#### 2.1.5 修改文件范围
| 文件 | 修改内容 |
|------|---------|
| `frontend/package.json` | 新增 `marked` 依赖 |
| `frontend/src/views/Reports.vue` | 增加 `case_type='security'` 分支,Markdown 渲染 |
| `frontend/src/api/reports.ts``frontend/src/api/security.ts` | 可能需要调整 API 调用 |
---
### 需求 2:补充 API3/4/6/7/9/10 安全测试用例
#### 2.2.1 当前状态
- 已有 12 个安全测试模块(API1-API10 + 历史漏洞回归 + 华为红线)
- 已有 24 个测试用例,覆盖:
- ✅ API1: Broken Object Level Authorization(对象级别授权失效)
- ✅ API2: Broken Authentication(身份认证失效)
- ✅ API5: Broken Function Level Authorization(功能级别授权失效)
- ✅ API8: Injection(注入)
- ✅ 历史漏洞回归验证
- ✅ 华为安全红线检查
- **缺失用例的模块**
- ❌ API3: Broken Object Property Level Authorization(对象属性级别授权失效)
- ❌ API4: Unrestricted Resource Consumption(资源消耗不受限)
- ❌ API6: Unrestricted Access to Sensitive Business Flows(无限制访问敏感业务流)
- ❌ API7: Server Side Request Forgery(服务器端请求伪造)
- ❌ API9: Improper Inventory Management(库存管理不当)
- ❌ API10: Unsafe Consumption of APIs(不安全的第三方 API 集成)
#### 2.2.2 功能需求
| 需求项 | 说明 |
|--------|------|
| **FR2.1** | 为 API3 模块新增 3-5 个测试用例 |
| **FR2.2** | 为 API4 模块新增 3-5 个测试用例 |
| **FR2.3** | 为 API6 模块新增 3-5 个测试用例 |
| **FR2.4** | 为 API7 模块新增 3-5 个测试用例 |
| **FR2.5** | 为 API9 模块新增 3-5 个测试用例 |
| **FR2.6** | 为 API10 模块新增 3-5 个测试用例 |
#### 2.2.3 用例设计原则
根据 `knowledge_base.py``security_executor.py` 的设计模式:
1. **用例结构**
```json
{
"module_id": "sec_api03",
"case_id": "sec_2_3_001",
"name": "测试名称",
"risk_level": "high",
"description": "测试描述",
"test_type": "vulnerability_scan",
"request": {
"method": "GET/POST",
"path": "/api/xxx",
"params": {},
"headers": {},
"body": {}
},
"assertions": [
{
"type": "status_code",
"expected": 200,
"vulnerability_if_match": false
}
],
"references": ["OWASP API Security Top 10 2023"]
}
```
2. **风险等级**
- `critical` — 严重(合并到 high 列统计)
- `high` — 高危
- `medium` — 中危
- `low` — 低危
- `info` — 信息
3. **测试类型**
- `vulnerability_scan` — 漏洞扫描
- `auth_bypass` — 认证绕过
- `privilege_escalation` — 权限提升
- `injection_test` — 注入测试
- `rate_limit_test` — 限流测试
#### 2.2.4 各模块用例设计(草案)
| 模块 | 用例 ID | 用例名称 | 风险等级 | 测试类型 |
|------|---------|----------|----------|----------|
| **API3** | sec_2_3_001 | 越权访问其他用户属性 | high | privilege_escalation |
| **API3** | sec_2_3_002 | 敏感字段未做权限校验 | medium | vulnerability_scan |
| **API3** | sec_2_3_003 | 批量查询泄露敏感属性 | high | vulnerability_scan |
| **API4** | sec_2_4_001 | 无速率限制的批量请求 | high | rate_limit_test |
| **API4** | sec_2_4_002 | 大文件上传无限制 | medium | vulnerability_scan |
| **API4** | sec_2_4_003 | 复杂查询无超时限制 | medium | vulnerability_scan |
| **API6** | sec_2_6_001 | 敏感业务流程无验证码 | high | vulnerability_scan |
| **API6** | sec_2_6_002 | 批量操作无二次确认 | medium | vulnerability_scan |
| **API6** | sec_2_6_003 | 自动化攻击检测缺失 | high | vulnerability_scan |
| **API7** | sec_2_7_001 | 内网 IP 探测 | critical | vulnerability_scan |
| **API7** | sec_2_7_002 | 云元数据接口访问 | critical | vulnerability_scan |
| **API7** | sec_2_7_003 | 文件读取 SSRF | high | vulnerability_scan |
| **API9** | sec_2_9_001 | 未授权访问已废弃 API | high | vulnerability_scan |
| **API9** | sec_2_9_002 | 测试环境 API 暴露 | medium | vulnerability_scan |
| **API9** | sec_2_9_003 | API 版本信息泄露 | low | vulnerability_scan |
| **API10** | sec_2_10_001 | 第三方 API 响应未校验 | high | vulnerability_scan |
| **API10** | sec_2_10_002 | 第三方 API 超时未处理 | medium | vulnerability_scan |
| **API10** | sec_2_10_003 | 第三方 API 密钥泄露 | critical | vulnerability_scan |
**合计:18 个新用例**(每模块 3 个)
#### 2.2.5 修改文件范围
| 文件 | 修改内容 |
|------|---------|
| `backend/scripts/create_security_cases.py` | 新增 18 个用例定义,更新模块用例数统计 |
---
## 三、多窗口并行开发协调
根据 `Docs/多窗口并行开发指南.md`
| 项目 | 安全测试窗口(本窗口) | 会议管理窗口 |
|------|----------------------|------------|
| 后端端口 | 8002(或不启动) | 8001 |
| 前端端口 | 3001(或不启动) | 3000 |
| 开发模块 | 安全测试 | 会议管理/UI自动化 |
| Playwright | **不执行 UI 用例** | 可执行 |
| Git 提交 | 提交前先 `git pull` | 提交后 push |
| 数据库 | **错开写入时间** | 错开写入时间 |
### 特别注意
- **需求 1**(前端报告中心适配):无冲突,可直接并行开发
- **需求 2**(补充用例):涉及数据库写入,需与会议管理窗口**错开脚本执行时间**
---
## 四、验收标准
### 需求 1 验收标准
- [ ] Reports.vue 能识别 `case_type='security'` 类型
- [ ] 安全测试报告能正确渲染 Markdown 内容
- [ ] 报告样式与 UI 自动化报告风格一致
- [ ] 下载报告功能正常
- [ ] 报告列表能显示安全测试执行记录
### 需求 2 验收标准
- [ ] 6 个缺失模块各有至少 3 个测试用例
- [ ] 新增用例能正常创建到数据库
- [ ] 新增用例能正常执行并生成报告
- [ ] 执行报告中 OWASP 覆盖矩阵能体现新增用例
---
## 五、风险评估
| 风险项 | 概率 | 影响 | 缓解措施 |
|--------|------|------|---------|
| Markdown 渲染样式与预期不符 | 中 | 中 | 参考现有 UI 报告样式,使用统一的 CSS 类 |
| 新增用例设计不够完善 | 中 | 中 | 参考 OWASP 官方测试用例和 `knowledge_base.py` 现有模式 |
| 数据库写锁冲突 | 低 | 高 | 与会议管理窗口协调脚本执行时间 |
---
## 六、参考文档
- `HANDOFF_安全测试.md` — 会话交接文档
- `Docs/PRD/需求文档/_PRD_安全测试模块需求文档.md` — 安全测试模块 PRD
- `backend/app/services/knowledge_base.py` — 知识库(历史漏洞、华为红线)
- `backend/app/executors/security_executor.py` — 安全测试执行引擎
- `Docs/多窗口并行开发指南.md` — 并行开发规范
---
*本文档由 Claude 生成,供计划执行文档参考。*
# HANDOFF — 平台自动化测试交接文档 # HANDOFF — 平台自动化测试交接文档(UI自动化窗口)
> **生成时间**: 2026-07-21 21:20 > **生成时间**: 2026-07-22
> **当前分支**: `platform-auto-test` > **当前分支**: `platform-auto-test`
> **最近提交**: `001a68a9` feat(security): 新增API接口安全测试模块 > **最近提交**: `a9074f8c` fix(executor): 修复FastAPI端点执行Playwright报asyncio检测错误
> **状态**: ✅ FastAPI 端点执行问题已修复 > **状态**: ✅ 深层交互用例步骤优化完成,通过率从 30% 提升到 76%
--- ---
...@@ -17,23 +17,19 @@ ...@@ -17,23 +17,19 @@
**当前多窗口状态**:本项目有两个窗口同时在开发: **当前多窗口状态**:本项目有两个窗口同时在开发:
- **本窗口**:UI 自动化测试模块(会议管理、数据分析、运维管理、管理看板) - **本窗口**:UI 自动化测试模块(会议管理、数据分析、运维管理、管理看板)
- **另一窗口**:安全测试模块(API安全测试、华为红线检查、漏洞回归等) - **另一窗口**:安全测试模块(API安全测试、华为红线检查、漏洞回归等)— 交接文档见 `HANDOFF_安全测试.md`
--- ---
## 一、项目概览 ## 一、项目概览
**项目名称**: 测试管理平台(platform-auto-test) **平台自动化测试可视化系统 (platform-auto-test)** — Web 可视化自动化测试平台,核心创新是用例录制器。
**一句话描述**: Web 可视化测试管理平台,核心是用例录制器——用户在目标网站操作,系统自动捕获行为转为测试步骤。 - **技术栈**: FastAPI + SQLAlchemy(async) + SQLite + Playwright(同步API) + WebSocket / Vue 3 + Element Plus + Vite 5 + TypeScript + ECharts
- **被测系统**: 统一管理平台 (https://192.168.5.44)
### 技术栈 - **登录凭据**: admin@xty / Ubains@13579 · 验证码固定 `csba`
- **远程仓库**: http://git.ubainsyun.com/bing/ubains-module-test.git
| 层级 | 技术 | - **当前分支**: `platform-auto-test`(主分支:`main`
|------|------|
| 后端 | FastAPI + SQLAlchemy(async) + SQLite + Playwright(同步API) + WebSocket |
| 前端 | Vue 3 + Element Plus + Vite 5 + TypeScript + ECharts |
| 被测系统 | https://192.168.5.44 (admin@xty / Ubains@13579 / 验证码 csba) |
### 目录结构(关键路径) ### 目录结构(关键路径)
...@@ -42,39 +38,92 @@ platform-auto-test/ ...@@ -42,39 +38,92 @@ platform-auto-test/
├── backend/app/ ├── backend/app/
│ ├── main.py # FastAPI 入口(Windows ProactorEventLoop) │ ├── main.py # FastAPI 入口(Windows ProactorEventLoop)
│ ├── executors/ │ ├── executors/
│ │ ├── playwright_executor.py # UI测试执行引擎(13种动作+11种断言,支持iframe) │ │ └── playwright_executor.py # UI测试执行引擎(13种动作+11种断言,支持iframe)
│ │ ├── security_executor.py # 安全测试执行器(另一窗口) │ ├── services/execution_service.py # 执行调度(含 run_all_cases_sync)
│ │ └── auth_helper.py # 认证辅助(另一窗口)
│ ├── models/
│ │ ├── execution.py # 执行记录模型
│ │ ├── vulnerability_result.py # 漏洞结果模型
│ │ └── security_config.py # 安全配置模型(另一窗口)
│ ├── services/execution_service.py # 执行调度
│ └── scripts/ │ └── scripts/
│ ├── create_deep_interaction_cases_v2.py # 深层用例创建脚本 │ ├── create_deep_interaction_cases_v2.py # 深层用例创建脚本(已优化)
│ ├── test_executor.py # 执行器测试脚本 │ ├── test_executor_direct.py # 执行器直接调用测试
│ └── test_executor_async.py # 异步执行器测试脚本 │ └── test_single_case.py # FastAPI端点单用例测试
├── frontend/src/ ├── frontend/src/
│ ├── views/Cases.vue # 用例管理 │ ├── views/Cases.vue # 用例管理
│ ├── views/Execution.vue # 执行中心 │ ├── views/Execution.vue # 执行中心
│ └── ... │ └── ...
├── Docs/ ├── Docs/PRD/
│ ├── 多窗口并行开发指南.md # 多窗口开发规范 │ ├── 需求文档/用例管理/
│ └── PRD/ # 需求文档+问题处理 │ │ ├── _PRD_需求文档_深层交互用例步骤优化.md # 本次P0需求文档
│ │ └── _执行计划_深层交互用例步骤优化.md # 本次P0计划执行文档
│ └── ...
└── HANDOFF.md # 本文档 └── HANDOFF.md # 本文档
``` ```
--- ---
## 二、本次任务 ## 二、本次会话任务(已完成)
### 深层交互用例步骤优化 ✅
**问题背景**:25 个深层交互用例执行通过率仅约 30%,主要问题:canvas 等待超时、重复登录、选择器不精确、导航等待不足。
**完成的优化**
1. **删除冗余登录步骤**
- 移除了 `build_base_steps()` 函数(8步登录流程)
- 用例步骤从"导航到目标菜单"开始,`auto_login=True` 由执行器处理
- 步骤 order 从 1 开始连续编号
2. **优化 canvas 等待策略**
- canvas 等待超时从 15s 提升到 30s
- 增加 ECharts 特定选择器回退链:`[".echarts", ".chart-container canvas", "canvas", "[class*='chart']"]`
- 增加"等待图表渲染完成"步骤(3s 延时确保 ECharts 完成绘制)
3. **优化导航等待策略**
- 导航后等待目标页面特征元素(canvas/.el-table),而非仅 `wait body`
- 增加"等待页面渲染完成"步骤(3s 延时应对动画)
4. **优化选择器精确度**
- 筛选器选择器增加上下文限定(`.el-select:nth-child(n) .el-input`
- 下拉选项选择器增加可见性限定(`.el-select-dropdown:visible`
- 使用 `selectors` 列表提供多选器回退链
5. **修复执行器 `_do_wait` 方法**
- 增加快速检查阶段:先 1s 超时快速检查每个选择器
- 将总超时均分给每个选择器,避免 4 个选择器 × 30s = 120s
- 效果:canvas 等待从 120s 降到 4s
### 任务:补充深层交互测试用例并执行验证 6. **修复执行器 `element_count_min` 断言**
- 支持 `params.min_count` 参数(之前仅支持 `params.expected`
- 向后兼容:`min_count` 不存在时回退到 `expected`
**目标**: 补充数据分析/运维管理/管理看板模块的深层交互用例(筛选器、图表、表格等),并验证用例可执行。 7. **脚本支持更新已有用例**
- 新增 `update_or_create_case()` 函数
- 按名称查找已有用例,找到则更新,否则创建
**执行结果**
| 指标 | 优化前 | 优化后 |
|------|--------|--------|
| 通过率 | ~30% | **76%** (19/25) |
| 单用例耗时 | 120-130s | **10-20s** |
| 重复登录 | 有 | **无** |
**失败用例分析**(6 个,均为业务数据/页面结构问题):
| 用例 | 失败原因 | 类型 |
|------|---------|------|
| 管理看板-表格数据验证 | 表格数据行 0 行(被测系统数据为空) | 数据问题 |
| 会议概览-筛选器交互验证 | canvas 等待超时(页面无 canvas) | 页面结构 |
| 会议室概览-筛选器验证 | canvas 等待超时(页面无 canvas) | 页面结构 |
| 会议统计-下拉筛选验证 | 筛选器选择器不匹配 | 选择器 |
| 历史记录-RSE-表格数据验证 | 菜单不存在(名称不正确) | 菜单名称 |
| 通知统计-筛选器验证 | 页面无 .el-select 元素 | 页面结构 |
**相关文档**
- `Docs/PRD/需求文档/用例管理/_PRD_需求文档_深层交互用例步骤优化.md`
- `Docs/PRD/需求文档/用例管理/_执行计划_深层交互用例步骤优化.md`
--- ---
## 三、已完成事项 ## 三、历史已完成事项
### 3.1 用例创建 ### 3.1 用例创建
...@@ -84,69 +133,31 @@ platform-auto-test/ ...@@ -84,69 +133,31 @@ platform-auto-test/
- 运维管理:15个(+7) - 运维管理:15个(+7)
- 数据分析:34个(+12) - 数据分析:34个(+12)
### 3.2 用例步骤修复(关键) ### 3.2 用例步骤修复
1.**删除了"点击展开功能中心"步骤**:登录后 .block 菜单已可见,无需额外点击 1.**删除了"点击展开功能中心"步骤**:登录后 .block 菜单已可见,无需额外点击
2.**删除了登录步骤**`auto_login=True` 时执行器会自动登录,用例步骤不应包含登录步骤 2.**删除了登录步骤**`auto_login=True` 时执行器会自动登录,用例步骤不应包含登录步骤
- 这是 HANDOFF 踩坑记录 #6 的核心教训
### 3.3 执行器 asyncio 检测问题修复
**问题**:Playwright 1.40 的 `sync_playwright().start()` 会检测 asyncio 事件循环,在 `run_in_executor` 线程中抛出错误。 ### 3.3 首页探索发现
**修复**:在 `playwright_executor.py``start()` 方法中添加:
```python
if threading.current_thread().name != 'MainThread':
try:
loop = asyncio.get_event_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
```
**验证结果**
- ✅ 直接调用 `PlaywrightExecutor` → 用例通过
- ✅ 通过 `run_in_executor` 调用 → 用例通过
- ❌ 通过 FastAPI `/run` 端点调用 → **执行器登录后停留在首页,不执行用例步骤**
### 3.4 首页探索发现
通过 `explore_homepage.py``explore_blocks.py` 脚本探索发现:
- 首页有 29 个 .block 菜单卡片(前24个和后5个重复) - 首页有 29 个 .block 菜单卡片(前24个和后5个重复)
- 登录后 .block 元素已可见,不需要点击"展开功能中心" - 登录后 .block 元素已可见,不需要点击"展开功能中心"
- 点击 `.block:has-text("管理看板")` 可正常跳转 - 点击 `.block:has-text("管理看板")` 可正常跳转
- 目标页面元素正确加载(如管理看板:11 canvas, 1 table, 4 selects)
--- ---
## 四、已修复问题 ## 四、待解决问题
### 4.1 FastAPI 端点执行 Playwright asyncio 检测问题 ✅ 已修复
**问题现象** ### 4.1 6 个失败用例需针对性修复
- 通过 `/api/executions/{id}/run` 端点执行时报错:
```
执行器启动失败: It looks like you are using Playwright Sync API inside the asyncio loop.
```
**根本原因**: 1. **会议概览/会议室概览**:页面可能没有 canvas 图表,需探索实际 DOM 结构
- Playwright 的 `sync_playwright().start()` 内部使用 `asyncio.get_running_loop()` 检测是否有运行中的事件循环 2. **会议统计-下拉筛选验证**:筛选器选择器需调整
- 在 FastAPI 的 `run_in_executor` 线程池中,工作线程本身没有运行中的事件循环 3. **历史记录-RSE**:菜单名称可能不准确,需确认实际菜单文本
- 但之前的修复代码 `asyncio.get_event_loop()` 可能返回主线程的事件循环,导致 Playwright 仍然检测到 asyncio 环境 4. **通知统计/管理看板表格**:页面结构/数据可能与预期不符
**修复方案**(`playwright_executor.py:181-190`): ### 4.2 执行器端口问题
```python
import threading
if threading.current_thread() is not threading.main_thread():
# 非主线程(run_in_executor 环境)
# 将当前线程的事件循环设为 None,彻底绕过 Playwright 的 asyncio 检测
asyncio.set_event_loop(None)
```
**验证结果**: 8001 端口有僵尸进程(旧 uvicorn --reload),强制 kill 后仍会重生。当前通过 8002 端口绕过。需要找到并清理所有相关进程。
- ✅ 单用例执行正常(会议列表用例通过)
- ✅ 多用例执行正常(2个用例同时执行)
- ⚠️ 部分用例断言失败(canvas 元素等待超时),但这是业务问题,不是执行引擎问题
--- ---
...@@ -154,39 +165,46 @@ if threading.current_thread() is not threading.main_thread(): ...@@ -154,39 +165,46 @@ if threading.current_thread() is not threading.main_thread():
| 优先级 | 待办 | 说明 | | 优先级 | 待办 | 说明 |
|--------|------|------| |--------|------|------|
| **P0** | 优化深层交互用例步骤 | 部分用例 canvas 等待超时,需调整选择器和超时时间 | | **P0** | 修复 6 个失败用例 | 探索实际 DOM 结构,调整选择器和等待策略 |
| **P1** | 提交深层用例脚本 | `create_deep_interaction_cases_v2.py` 等文件待提交 | | **P1** | 提交代码 | 脚本优化 + 执行器修复 + 文档 待 git commit |
| **P2** | 提交执行引擎修复 | `playwright_executor.py` asyncio 检测绕过修复 | | **P1** | 清理 8001 端口僵尸进程 | 旧 uvicorn --reload 进程未清理 |
| **P3** | 批量执行验证 | 验证所有 117 个 UI 用例的执行情况 | | **P2** | 批量执行全量用例 | 验证 117 个 UI 用例的执行情况 |
| **P3** | 清理临时脚本 | 根目录有多个临时探索脚本待清理 |
--- ---
## 六、关键踩坑记录 ## 六、关键踩坑记录(绝对不要重复踩)
| # | 现象 | 根因 | 正确做法 | | # | 现象 | 根因 | 正确做法 |
|---|------|------|---------| |---|------|------|---------|
| 1 | NotImplementedError | Windows SelectorEventLoop不支持子进程 | 同步API+ProactorEventLoop+线程池 | | 1 | `NotImplementedError` / Sync API inside asyncio | Windows 默认 SelectorEventLoop 不支持子进程 | 同步 API + ProactorEventLoop + 线程池 |
| 2 | 第二次start()失败 | 共享执行器 _is_running 状态 | 每用例新建PlaywrightExecutor | | 2 | 第二次执行 `start()` 失败 | 共享执行器 `_is_running` 状态 | 每用例新建 PlaywrightExecutor 实例 |
| 3 | /api/api/cases 404 | baseURL叠加 | baseURL留空,路径写全 | | 3 | 请求 `/api/api/cases` 404 | `baseURL:'/api'` 与路径叠加 | baseURL 留空,路径写完整 `/api/...` |
| 4 | SQLite database is locked | 同步执行并发写 | check_same_thread=False+pool_pre_ping | | 4 | `SQLite database is locked` | 同步执行并发写入 | check_same_thread=False + pool_pre_ping |
| 5 | 菜单text=选择器失败 | 被测系统菜单是.block>p | `.block:has-text("菜单名")` | | 5 | bash 中 `start` 启动 CMD 失败 | MSYS 封装损坏 `&&` 引号 | `MSYS_NO_PATHCONV=1 cmd.exe /c start ...` |
| 6 | **auto_login+重复登录** | 用例含登录步骤 | **auto_login=True时不要包含登录步骤** | | 6 | 前端页面空白 | `useRoute()` 在 computed 内调用 | 移到 setup 顶层 |
| 7 | **"点击展开功能中心"无效** | 登录后.block已可见 | **删除此步骤** | | 7 | `NOT NULL constraint failed: error_message` | 字段 nullable=False 但成功时为 None | 改为 nullable=True |
| 8 | **Playwright asyncio检测错误** | run_in_executor中调用 | **`asyncio.set_event_loop(None)` 绕过检测** | | 8 | 菜单text=选择器失败 | 被测系统菜单是.block>p | `.block:has-text("菜单名")` |
| 9 | **FastAPI端点执行失败** | Playwright检测到asyncio循环 | **在工作线程中设置 event_loop=None** | | 9 | **auto_login+重复登录** | 用例含登录步骤 | **auto_login=True时不要包含登录步骤** |
| 10 | **"点击展开功能中心"无效** | 登录后.block已可见 | **删除此步骤** |
| 11 | **Playwright asyncio检测错误** | run_in_executor中调用 | **`asyncio.set_event_loop(None)` 绕过检测** |
| 12 | **FastAPI端点执行失败** | Playwright检测到asyncio循环 | **在工作线程中设置 event_loop=None** |
| 13 | **_do_wait 多选器超时过长** | 4选择器×30s=120s | **快速检查+均分超时** |
| 14 | **element_count_min断言失败** | `params.min_count`未支持 | **修复执行器支持min_count参数** |
| 15 | **uvicorn --reload僵尸进程** | Windows进程管理异常 | **清除__pycache__,kill所有进程,换端口** |
--- ---
## 七、启动指南 ## 七、启动与验证
```bash ```bash
# 后端 # 后端(端口 8001)
cd backend cd backend
pip install -r requirements.txt pip install -r requirements.txt
playwright install chromium playwright install chromium
uvicorn app.main:app --port 8001 uvicorn app.main:app --reload --port 8001
# 前端 # 前端(端口 3000)
cd frontend cd frontend
npm install npm install
npm run dev npm run dev
...@@ -196,38 +214,39 @@ npm run dev ...@@ -196,38 +214,39 @@ npm run dev
- API 文档: http://localhost:8001/docs - API 文档: http://localhost:8001/docs
- 被测系统: https://192.168.5.44 (admin@xty / Ubains@13579 / csba) - 被测系统: https://192.168.5.44 (admin@xty / Ubains@13579 / csba)
### 检查执行状态 ### 快速验证执行引擎
```bash ```bash
cd backend cd backend
PYTHONIOENCODING=utf-8 python scripts/check_exec.py <execution_id> PYTHONIOENCODING=utf-8 python scripts/test_executor_direct.py # 直接调用
PYTHONIOENCODING=utf-8 python scripts/test_single_case.py # API端点测试
``` ```
### 测试执行器(直接调用) ### 验证深层用例优化效果
```bash ```bash
cd backend cd backend
PYTHONIOENCODING=utf-8 python scripts/test_executor.py # 直接调用 PYTHONIOENCODING=utf-8 python scripts/create_deep_interaction_cases_v2.py # 更新用例步骤
PYTHONIOENCODING=utf-8 python scripts/test_executor_async.py # 异步调用 # 然后在前端执行中心批量执行深层交互用例
``` ```
--- ---
## 八、未提交变更 ## 八、未提交变更(待 git commit)
### 新增文件(待提交) ### 修改的文件
- `backend/scripts/create_deep_interaction_cases_v2.py` - 深层用例创建脚本 | 文件 | 变更说明 |
- `backend/scripts/test_executor.py` - 执行器测试脚本 |------|---------|
- `backend/scripts/test_executor_async.py` - 异步执行器测试脚本 | `backend/scripts/create_deep_interaction_cases_v2.py` | 重写步骤构建逻辑,删除冗余登录,优化模板 |
- `backend/scripts/explore_homepage.py` - 首页探索脚本 | `backend/app/executors/playwright_executor.py` | 修复 _do_wait 多选器超时 + element_count_min 支持 min_count |
- `backend/scripts/explore_blocks.py` - 菜单探索脚本 | `Docs/PRD/需求文档/用例管理/_PRD_需求文档_深层交互用例步骤优化.md` | 新增需求文档 |
- `backend/scripts/debug_exec.py` - 调试执行脚本 | `Docs/PRD/需求文档/用例管理/_执行计划_深层交互用例步骤优化.md` | 新增计划执行文档 |
| `HANDOFF.md` | 更新交接文档 |
### 修改文件(待提交) ### 数据库变更
- `backend/app/executors/playwright_executor.py` - 添加了 asyncio 检测绕过代码 - `backend/data/test_platform.db` — 25 个深层用例的 steps 已更新
- `backend/data/test_platform.db` - 新增 25 个用例
--- ---
...@@ -237,25 +256,28 @@ PYTHONIOENCODING=utf-8 python scripts/test_executor_async.py # 异步调用 ...@@ -237,25 +256,28 @@ PYTHONIOENCODING=utf-8 python scripts/test_executor_async.py # 异步调用
|------|------| |------|------|
| CLAUDE.md | `CLAUDE.md` | | CLAUDE.md | `CLAUDE.md` |
| 多窗口并行开发指南 | `Docs/多窗口并行开发指南.md` | | 多窗口并行开发指南 | `Docs/多窗口并行开发指南.md` |
| 深层用例优化需求文档 | `Docs/PRD/需求文档/用例管理/_PRD_需求文档_深层交互用例步骤优化.md` |
| 深层用例优化执行计划 | `Docs/PRD/需求文档/用例管理/_执行计划_深层交互用例步骤优化.md` |
| 问题处理文档 | `Docs/PRD/问题处理/` | | 问题处理文档 | `Docs/PRD/问题处理/` |
| 需求文档 | `Docs/PRD/需求文档/` | | 需求文档 | `Docs/PRD/需求文档/` |
| HANDOFF.md | `HANDOFF.md` | | 安全测试交接文档 | `HANDOFF_安全测试.md` |
| HANDOFF.md | `HANDOFF.md`(本文档) |
--- ---
## 十、用例统计 ## 十、用例统计
| 模块 | 用例数 | | 模块 | 用例数 | 深层用例通过率 |
|------|--------| |------|--------|--------------|
| 会议管理 | 50 | | 会议管理 | 50 | - |
| 数据分析 | 34 | | 数据分析 | 34 | - (深层12个,9通过3失败) |
| 运维管理 | 15 | | 运维管理 | 15 | - (深层7个,6通过1失败) |
| 管理看板 | 13 | | 管理看板 | 13 | - (深层6个,4通过2失败) |
| 登录模块 | 5 | | 登录模块 | 5 | - |
| **UI测试小计** | **117** | | **UI测试小计** | **117** | |
| 安全测试模块 | 24 | | 安全测试模块 | 24 | - |
| **总计** | **141** | | **总计** | **141** | **深层25个,19通过6失败(76%)** |
--- ---
*本文档由 /Handoff skill 生成,供下一次会话快速恢复上下文。* *本文档由 Claude Code 维护,供下一次会话快速恢复上下文。*
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
> **生成时间**: 2026-07-21 > **生成时间**: 2026-07-21
> **当前分支**: `platform-auto-test` > **当前分支**: `platform-auto-test`
> **开发窗口**: 安全测试模块(与会议管理窗口并行) > **开发窗口**: 安全测试模块(与会议管理窗口并行)
> **最近提交**: `4b690a98`(会议管理窗口,安全测试尚未提交) > **最近提交**: `68b2fbbf` feat(security): 安全测试报告生成服务+格式对齐参考实现
> **状态**: ✅ 安全测试引擎开发完成并自测通过,待提交 > **状态**: ✅ 安全测试引擎 + 报告生成服务已全部完成并推送
--- ---
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
## 一、本次任务(已完成) ## 一、本次任务(已完成)
### 任务:为平台增加 API 接口安全测试能力 ### 任务:为平台增加 API 接口安全测试能力 + 报告生成
**核心成果** **核心成果**
1. ✅ 后端安全测试引擎(SecurityExecutor + HttpClient 签名 + AuthHelper 多账号) 1. ✅ 后端安全测试引擎(SecurityExecutor + HttpClient 签名 + AuthHelper 多账号)
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
3. ✅ 前端适配(Cases.vue / Execution.vue 支持 security 类型) 3. ✅ 前端适配(Cases.vue / Execution.vue 支持 security 类型)
4. ✅ 执行调度分流修复(security 用例不再误走 PlaywrightExecutor) 4. ✅ 执行调度分流修复(security 用例不再误走 PlaywrightExecutor)
5. ✅ 端到端自测通过(3/3 用例正常执行) 5. ✅ 端到端自测通过(3/3 用例正常执行)
6.**安全测试报告生成服务**(7 章节 Markdown,格式对齐参考实现)
--- ---
...@@ -50,9 +51,15 @@ ...@@ -50,9 +51,15 @@
| `backend/app/executors/security_executor.py` | 安全测试执行引擎核心 | | `backend/app/executors/security_executor.py` | 安全测试执行引擎核心 |
| `backend/app/services/security_service.py` | 安全测试业务逻辑层 | | `backend/app/services/security_service.py` | 安全测试业务逻辑层 |
| `backend/app/services/knowledge_base.py` | 知识库(14历史漏洞 + 15华为红线 + SQL载荷) | | `backend/app/services/knowledge_base.py` | 知识库(14历史漏洞 + 15华为红线 + SQL载荷) |
| `backend/app/routers/security.py` | API 路由(11个接口) | | `backend/app/routers/security.py` | API 路由(13个接口,含报告 API) |
### 2.2 后端修改文件 ### 2.2 后端新增文件(报告生成)
| 文件 | 说明 |
|------|------|
| `backend/app/services/security_report_service.py` | 报告生成服务(7 章节 Markdown,格式对齐参考实现) |
### 2.3 后端修改文件
| 文件 | 修改内容 | | 文件 | 修改内容 |
|------|---------| |------|---------|
...@@ -62,70 +69,78 @@ ...@@ -62,70 +69,78 @@
| `backend/app/schemas/test_case.py` | `steps` 字段改为 Union(支持 list/dict) | | `backend/app/schemas/test_case.py` | `steps` 字段改为 Union(支持 list/dict) |
| `backend/app/services/case_service.py` | 创建用例时区分 UI(list)/security(dict) | | `backend/app/services/case_service.py` | 创建用例时区分 UI(list)/security(dict) |
### 2.3 前端新增/修改文件 ### 2.4 前端新增/修改文件
| 文件 | 说明 | | 文件 | 说明 |
|------|------| |------|------|
| `frontend/src/types/security.ts` | TypeScript 类型定义 | | `frontend/src/types/security.ts` | TypeScript 类型定义 |
| `frontend/src/api/security.ts` | 安全测试 API 封装 | | `frontend/src/api/security.ts` | 安全测试 API 封装(含报告 API) |
| `frontend/src/views/Cases.vue` | 安全用例显示风险等级 emoji/标签、详情弹窗 | | `frontend/src/views/Cases.vue` | 安全用例显示风险等级 emoji/标签、详情弹窗 |
| `frontend/src/views/Execution.vue` | 执行弹窗(配置选择)、分流安全/UI执行逻辑 | | `frontend/src/views/Execution.vue` | 执行弹窗(配置选择)、分流安全/UI执行逻辑 |
### 2.4 脚本文件 ### 2.5 脚本文件
| 文件 | 说明 | | 文件 | 说明 |
|------|------| |------|------|
| `backend/scripts/create_security_cases.py` | 创建 12 模块 + 24 用例 | | `backend/scripts/create_security_cases.py` | 创建 12 模块 + 24 用例 |
| `backend/scripts/create_default_security_config.py` | 创建默认安全测试配置 | | `backend/scripts/create_default_security_config.py` | 创建默认安全测试配置 |
| `backend/scripts/test_security_e2e.py` | 端到端执行测试 | | `backend/scripts/test_security_e2e.py` | 端到端执行测试 |
| `backend/scripts/debug_security_exec.py` | 调试脚本(同步 engine) |
| `backend/scripts/debug_security_exec2.py` | 调试脚本(run_in_executor) |
| `backend/scripts/debug_security_async.py` | 调试脚本(async engine) |
### 2.5 文档 ### 2.6 文档
| 文件 | 说明 | | 文件 | 说明 |
|------|------| |------|------|
| `Docs/PRD/需求文档/_PRD_安全测试模块需求文档.md` | 需求文档 | | `Docs/PRD/需求文档/_PRD_安全测试模块需求文档.md` | 需求文档 |
| `Docs/PRD/需求文档/_PRD_安全测试模块_计划执行.md` | 计划执行文档 | | `Docs/PRD/需求文档/_PRD_安全测试模块_计划执行.md` | 计划执行文档 |
| `Docs/PRD/问题处理/安全测试/_问题处理_安全测试执行报错str对象无get属性.md` | 问题处理(已修正根因) | | `Docs/PRD/需求文档/_PRD_安全测试报告_设计文档.md` | 报告设计文档 |
| `Docs/PRD/问题处理/安全测试/_执行计划_修复安全测试执行报错.md` | 执行计划 | | `Docs/PRD/需求文档/_PRD_安全测试报告格式对齐_需求文档.md` | 报告格式对齐需求 |
| `Docs/PRD/需求文档/_PRD_安全测试报告格式对齐_计划执行.md` | 报告格式对齐计划 |
| `Docs/PRD/问题处理/安全测试/*.md` | 问题处理文档 |
--- ---
## 三、当前数据状态(已入库 ## 三、安全测试报告生成服务(已完成
| 内容 | 数量 | 备注 | ### 3.1 报告 7 章节结构(对齐参考实现)
|------|------|------|
| 安全测试模块 | 12 | sec_api01~sec_api10 + sec_regression + sec_redline |
| 安全测试用例 | 24 | 覆盖 API1/2/5/8 + 回归 + 红线(API3/4/6/7/9/10 待补) |
| 安全测试配置 | 1 | `sec_cfg_default`(新统一平台 192.168.5.44) |
---
## 四、关键技术决策 | 章节 | 编号 | 内容 | 数据来源 |
|------|------|------|---------|
| 执行概要 | 一 | 统计总览 + OWASP 覆盖矩阵 | VulnerabilityResult 按 level/test_id 统计 |
| 漏洞详情 | 二 | 按风险等级排序,含请求/响应/修复建议 | VulnerabilityResult where is_vulnerable=True |
| 已验证安全项 | 三 | 按 OWASP 模块分组 | VulnerabilityResult where is_vulnerable=False |
| 风险评估与修复建议 | 四 | 按等级给出通用修复方案 | 根据漏洞分布动态生成 |
| 已知安全问题清单 | 五 | 6 个已知问题 | KNOWN_ISSUES 静态数据 |
| 历史漏洞回归验证 | **五.五** | HV-* 用例结果(6 列表格) | VulnerabilityResult + knowledge_base |
| 华为安全红线合规性检查 | **五.六** | HW-* 用例结果 | VulnerabilityResult + knowledge_base |
| 测试环境与工具 | **六** | 服务器(含架构)/工具/标准 | SecurityConfig |
| 参考资料 | **七** | Z 盘路径 | REFERENCE_MATERIALS 常量 |
### 4.1 安全测试不依赖浏览器 ### 3.2 格式对齐要点
`SecurityExecutor``requests` 库 + 签名算法直接调 API,与 `PlaywrightExecutor` 完全解耦。 | 对齐项 | 内容 |
|--------|------|
| OWASP 覆盖矩阵 | **5 列**(🔴高危/🟠中危/🟡低危/🔵信息/🟢安全),无"严重"列 |
| critical 处理 | 合并到 high 列统计,漏洞详情标题仍显示"严重" |
| 历史漏洞表格 | **6 列**(编号/漏洞来源/测试项/当前状态/风险等级/详情) |
| 服务器信息 | 含架构后缀 `192.168.5.44 (X86架构-欧拉)` |
| 执行完成自动生成 | 路径存入 `execution.config.report_path` |
### 4.2 执行调度分流(核心修复) ### 3.3 报告 API
`execution_service.run_execution()` 入口按 `case_type` 分流:
```python
if execution.case_type == "security":
return await self._run_security_execution(execution_id, config)
``` ```
安全用例走专用方法,结果同时写入 `CaseResult`(执行中心展示)+ `VulnerabilityResult`(报告中心)。 GET /api/security/executions/{id}/report → 返回 Markdown 内容
GET /api/security/executions/{id}/report/download → 下载 .md 文件
### 4.3 签名算法(从前端 JS 逆向) ```
`http_client.py` 实现了 AES-CBC 签名(X-RANDOM/X-TIMESTAMP/X-SIGN),与被测系统前端一致,需 `pycryptodome` ---
### 4.4 steps 字段双格式 ## 四、当前数据状态(已入库)
- UI 用例:`steps` 是步骤列表 `[{order, action, ...}]` | 内容 | 数量 | 备注 |
- 安全用例:`steps` 是配置字典 `{test_type, target, auth, assertions, vulnerability}` |------|------|------|
| 安全测试模块 | 12 | sec_api01~sec_api10 + sec_regression + sec_redline |
| 安全测试用例 | 24 | 覆盖 API1/2/5/8 + 回归 + 红线(API3/4/6/7/9/10 待补) |
| 安全测试配置 | 1 | `sec_cfg_default`(新统一平台 192.168.5.44) |
--- ---
...@@ -133,8 +148,7 @@ if execution.case_type == "security": ...@@ -133,8 +148,7 @@ if execution.case_type == "security":
| 优先级 | 待办 | 说明 | | 优先级 | 待办 | 说明 |
|--------|------|------| |--------|------|------|
| **P0** | 提交安全测试代码 | 提交前先 `git pull` 会议管理窗口的变更 | | **P1** | 前端报告中心适配安全报告 | Reports.vue 增加 Markdown 渲染(markdown-it),或安全报告生成 HTML 版本 |
| **P1** | 报告中心报告细化 | 参考实现设计了 7 大章节的详细报告,当前平台只存原始数据,需生成 Markdown 详细报告 |
| **P1** | 补充 API3/4/6/7/9/10 用例 | 当前只覆盖 24 个,参考实现有 90+ | | **P1** | 补充 API3/4/6/7/9/10 用例 | 当前只覆盖 24 个,参考实现有 90+ |
| **P2** | WebSocket 实时进度推送 | 安全测试目前不推送步骤进度 | | **P2** | WebSocket 实时进度推送 | 安全测试目前不推送步骤进度 |
| **P3** | 网盘上传 + ERP 任务对接 | 参考实现的功能,平台化后可选 | | **P3** | 网盘上传 + ERP 任务对接 | 参考实现的功能,平台化后可选 |
...@@ -150,6 +164,8 @@ if execution.case_type == "security": ...@@ -150,6 +164,8 @@ if execution.case_type == "security":
| S3 | 执行时间显示比实际少 8 小时 | 全项目用 `utcnow()` 存 UTC,前端未转时区 | 全项目既有行为,单独评估 | | S3 | 执行时间显示比实际少 8 小时 | 全项目用 `utcnow()` 存 UTC,前端未转时区 | 全项目既有行为,单独评估 |
| S4 | 误判为"ORM 脱钩" | 未先看后端日志的错误来源模块 | **排查异常先 grep 日志确认是哪个 executor 报的** | | S4 | 误判为"ORM 脱钩" | 未先看后端日志的错误来源模块 | **排查异常先 grep 日志确认是哪个 executor 报的** |
| S5 | Windows curl 发中文 JSON body 失败 | Windows bash 编码 | 用 Python requests 脚本测试,不用 curl | | S5 | Windows curl 发中文 JSON body 失败 | Windows bash 编码 | 用 Python requests 脚本测试,不用 curl |
| S6 | 报告测试目标为空 | 执行记录 config 无 config_id | **回退到默认 SecurityConfig** |
| S7 | OWASP 矩阵多出"严重"列 | 平台有 critical 等级,参考实现没有 | **critical 合并到 high 列统计** |
--- ---
...@@ -170,31 +186,17 @@ PYTHONIOENCODING=utf-8 python scripts/test_security_e2e.py ...@@ -170,31 +186,17 @@ PYTHONIOENCODING=utf-8 python scripts/test_security_e2e.py
--- ---
## 八、未提交变更(安全测试窗口) ## 八、已提交变更(安全测试窗口)
以下文件未提交,待 git pull 后统一提交:
**后端新增**(9个):
- `backend/app/models/security_config.py`
- `backend/app/models/vulnerability_result.py`
- `backend/app/schemas/security.py`
- `backend/app/executors/http_client.py`
- `backend/app/executors/auth_helper.py`
- `backend/app/executors/security_executor.py`
- `backend/app/services/security_service.py`
- `backend/app/services/knowledge_base.py`
- `backend/app/routers/security.py`
**后端修改**
- `backend/app/main.py``models/__init__.py``execution_service.py``schemas/test_case.py``case_service.py`
**前端** **提交记录**
- `frontend/src/types/security.ts``api/security.ts``views/Cases.vue``views/Execution.vue` - `001a68a9` feat(security): 新增API接口安全测试模块
- `68b2fbbf` feat(security): 安全测试报告生成服务+格式对齐参考实现
**脚本/文档** **已提交文件**
- `backend/scripts/create_security_cases.py` - 后端新增(10个):安全测试引擎 + 报告生成服务
- `Docs/PRD/需求文档/_PRD_安全测试*.md` - 后端修改:main.py、models/__init__.py、execution_service.py、schemas/test_case.py、case_service.py、routers/security.py
- `Docs/PRD/问题处理/安全测试/*.md` - 前端:types/security.ts、api/security.ts、views/Cases.vue、views/Execution.vue
- 文档:PRD 需求文档、计划执行文档、报告设计文档、格式对齐文档
--- ---
......
...@@ -425,6 +425,464 @@ SECURITY_CASES = [ ...@@ -425,6 +425,464 @@ SECURITY_CASES = [
} }
}, },
# ==================== API3: 对象属性级别授权失效 ====================
{
"id": "sec_2_3_001",
"module_id": "sec_api03",
"name": "越权访问其他用户属性-修改角色字段",
"description": "普通用户通过修改请求体中的role字段尝试提升权限,验证是否存在属性级别越权漏洞",
"priority": "high",
"tags": ["OWASP-API3", "属性越权", "权限提升"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/manageUser/update", "method": "POST"},
"auth": {"required": True, "account": "user"},
"request": {"body": {"id": "${user_id}", "role": "superadmin"}},
"pre_steps": [
{"action": "get_resource_id", "account": "user", "path": "/api/manageUser/getManagerPage", "params": {"pageNo": 1, "pageSize": 5}, "extract": {"user_id": "data.records[0].id"}}
],
"assertions": [
{"type": "status_code_not_equal", "expect": 200, "description": "越权修改角色应被拒绝"},
{"type": "response_code_contain", "expect": ["401", "403", "B0027"], "description": "应返回权限不足"}
],
"vulnerability": {
"id": "2.3.1",
"name": "越权访问其他用户属性-修改角色字段",
"level": "high",
"description": "普通用户可通过修改role字段提升权限,存在属性级别越权漏洞",
"fix_suggestion": "接口应校验用户是否有权修改角色等敏感属性,禁止客户端传入角色字段"
}
}
},
{
"id": "sec_2_3_002",
"module_id": "sec_api03",
"name": "敏感字段未做权限校验-修改公司编号",
"description": "普通用户通过修改companyNumber字段尝试跨公司操作,验证敏感属性是否有权限校验",
"priority": "medium",
"tags": ["OWASP-API3", "属性越权", "批量赋值"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/manageUser/update", "method": "POST"},
"auth": {"required": True, "account": "user"},
"request": {"body": {"id": "${user_id}", "companyNumber": "OTHER_COMPANY_001"}},
"pre_steps": [
{"action": "get_resource_id", "account": "user", "path": "/api/manageUser/getManagerPage", "params": {"pageNo": 1, "pageSize": 5}, "extract": {"user_id": "data.records[0].id"}}
],
"assertions": [
{"type": "status_code_not_equal", "expect": 200, "description": "修改公司编号应被拒绝"}
],
"vulnerability": {
"id": "2.3.2",
"name": "敏感字段未做权限校验-修改公司编号",
"level": "medium",
"description": "普通用户可修改companyNumber等敏感属性,存在批量赋值漏洞",
"fix_suggestion": "接口应使用白名单机制,仅允许修改授权范围内的属性"
}
}
},
{
"id": "sec_2_3_003",
"module_id": "sec_api03",
"name": "批量查询泄露敏感属性-用户列表接口",
"description": "通过用户列表接口查询其他用户的敏感属性(密码哈希、Token等),验证响应是否过滤敏感字段",
"priority": "high",
"tags": ["OWASP-API3", "信息泄露", "敏感属性"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/manageUser/getManagerPage", "method": "POST"},
"auth": {"required": True, "account": "user"},
"request": {"body": {"pageNo": 1, "pageSize": 10}},
"assertions": [
{"type": "body_not_contains", "expect": "password", "description": "响应不应包含密码字段"},
{"type": "body_not_contains", "expect": "token", "description": "响应不应包含Token字段"}
],
"vulnerability": {
"id": "2.3.3",
"name": "批量查询泄露敏感属性-用户列表接口",
"level": "high",
"description": "用户列表接口返回了密码哈希、Token等敏感属性,存在信息泄露",
"fix_suggestion": "接口响应应过滤敏感字段,使用DTO/Schema控制返回属性"
}
}
},
# ==================== API4: 资源消耗不受限 ====================
{
"id": "sec_2_4_001",
"name": "无速率限制的批量请求-会议查询",
"module_id": "sec_api04",
"description": "短时间内发送大量会议查询请求,验证是否存在速率限制",
"priority": "high",
"tags": ["OWASP-API4", "速率限制", "DoS"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/message/getMeetingList", "method": "GET"},
"auth": {"required": True, "account": "user"},
"request": {"params": {"pageNo": 1, "pageSize": 10}},
"rate_limit_test": {"request_count": 50, "interval_ms": 100, "expect_limited": True},
"assertions": [
{"type": "rate_limit_exists", "expect": True, "description": "应存在速率限制"}
],
"vulnerability": {
"id": "2.4.1",
"name": "无速率限制的批量请求-会议查询",
"level": "high",
"description": "会议查询接口无速率限制,可被用于DoS攻击",
"fix_suggestion": "实现基于IP/用户的速率限制,如每分钟最多60次请求"
}
}
},
{
"id": "sec_2_4_002",
"module_id": "sec_api04",
"name": "分页参数滥用-超大pageSize",
"description": "使用超大pageSize参数请求列表接口,验证是否存在分页限制",
"priority": "medium",
"tags": ["OWASP-API4", "分页滥用", "资源消耗"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/message/getMeetingList", "method": "GET"},
"auth": {"required": True, "account": "user"},
"request": {"params": {"pageNo": 1, "pageSize": 99999}},
"assertions": [
{"type": "response_size_limit", "expect_max_kb": 1024, "description": "响应大小应有限制"}
],
"vulnerability": {
"id": "2.4.2",
"name": "分页参数滥用-超大pageSize",
"level": "medium",
"description": "列表接口允许超大pageSize,可导致大量数据查询消耗服务器资源",
"fix_suggestion": "限制pageSize最大值(如100),超出自动截断"
}
}
},
{
"id": "sec_2_4_003",
"module_id": "sec_api04",
"name": "复杂查询无超时限制-深度分页",
"description": "使用深度分页参数请求列表接口,验证查询是否有超时限制",
"priority": "medium",
"tags": ["OWASP-API4", "深度分页", "超时限制"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/message/getMeetingList", "method": "GET"},
"auth": {"required": True, "account": "user"},
"request": {"params": {"pageNo": 999999, "pageSize": 100}},
"assertions": [
{"type": "response_time_limit", "expect_max_seconds": 10, "description": "查询应在10秒内返回"}
],
"vulnerability": {
"id": "2.4.3",
"name": "复杂查询无超时限制-深度分页",
"level": "medium",
"description": "深度分页查询无超时限制,可导致慢查询消耗数据库资源",
"fix_suggestion": "实现查询超时机制,限制最大分页深度"
}
}
},
# ==================== API6: 无限制访问敏感业务流 ====================
{
"id": "sec_2_6_001",
"module_id": "sec_api06",
"name": "敏感业务流程无验证码-短信接口",
"description": "测试短信发送接口是否存在验证码或频率限制,防止短信轰炸",
"priority": "high",
"tags": ["OWASP-API6", "短信轰炸", "业务滥用"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/sms/send", "method": "POST"},
"auth": {"required": True, "account": "user"},
"request": {"body": {"phone": "13800138000"}},
"rate_limit_test": {"request_count": 10, "interval_ms": 500, "expect_limited": True},
"assertions": [
{"type": "rate_limit_exists", "expect": True, "description": "短信接口应有频率限制"}
],
"vulnerability": {
"id": "2.6.1",
"name": "敏感业务流程无验证码-短信接口",
"level": "high",
"description": "短信发送接口无验证码和频率限制,可被用于短信轰炸攻击",
"fix_suggestion": "增加图形验证码和发送频率限制(如每分钟1次)"
}
}
},
{
"id": "sec_2_6_002",
"module_id": "sec_api06",
"name": "批量操作无二次确认-批量删除会议",
"description": "测试批量删除接口是否需要二次确认或额外授权",
"priority": "medium",
"tags": ["OWASP-API6", "批量操作", "二次确认"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/message/batchDelete", "method": "POST"},
"auth": {"required": True, "account": "user"},
"request": {"body": {"ids": ["1", "2", "3", "4", "5"]}},
"assertions": [
{"type": "status_code_not_equal", "expect": 200, "description": "批量删除应需额外确认"}
],
"vulnerability": {
"id": "2.6.2",
"name": "批量操作无二次确认-批量删除会议",
"level": "medium",
"description": "批量删除接口无需二次确认,误操作风险高",
"fix_suggestion": "批量操作应增加二次确认机制或操作审批流程"
}
}
},
{
"id": "sec_2_6_003",
"module_id": "sec_api06",
"name": "自动化攻击检测缺失-批量注册",
"description": "测试注册接口是否存在自动化攻击检测机制",
"priority": "high",
"tags": ["OWASP-API6", "批量注册", "自动化攻击"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/platform/api/auth/register", "method": "POST"},
"auth": {"required": False},
"request": {"body": {"username": "bot_test_${timestamp}", "password": "Test@12345", "code": "csba"}},
"rate_limit_test": {"request_count": 5, "interval_ms": 1000, "expect_limited": True},
"assertions": [
{"type": "rate_limit_exists", "expect": True, "description": "注册接口应有频率限制"}
],
"vulnerability": {
"id": "2.6.3",
"name": "自动化攻击检测缺失-批量注册",
"level": "high",
"description": "注册接口无自动化攻击检测,可被批量注册垃圾账号",
"fix_suggestion": "增加注册频率限制和图形验证码"
}
}
},
# ==================== API7: 服务器端请求伪造 ====================
{
"id": "sec_2_7_001",
"module_id": "sec_api07",
"name": "SSRF内网IP探测-会议服务",
"description": "通过修改请求参数中的URL/IP字段尝试探测内网服务",
"priority": "critical",
"tags": ["OWASP-API7", "SSRF", "内网探测", "Critical"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/message/getMeetingList", "method": "GET"},
"auth": {"required": True, "account": "user"},
"request": {"params": {"pageNo": 1, "pageSize": 10, "callback": "http://127.0.0.1:8848/nacos/"}},
"assertions": [
{"type": "body_not_contains", "expect": "nacos", "description": "不应返回内网服务信息"}
],
"vulnerability": {
"id": "2.7.1",
"name": "SSRF内网IP探测-会议服务",
"level": "critical",
"description": "通过callback参数可探测内网服务,存在SSRF漏洞(CVSS 9.1)",
"fix_suggestion": "禁止callback参数指向内网地址,使用URL白名单机制"
}
}
},
{
"id": "sec_2_7_002",
"module_id": "sec_api07",
"name": "SSRF云元数据接口访问",
"description": "尝试通过SSRF访问云服务器元数据接口(169.254.169.254)",
"priority": "critical",
"tags": ["OWASP-API7", "SSRF", "云元数据", "Critical"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/message/getMeetingList", "method": "GET"},
"auth": {"required": True, "account": "user"},
"request": {"params": {"pageNo": 1, "pageSize": 10, "url": "http://169.254.169.254/latest/meta-data/"}},
"assertions": [
{"type": "body_not_contains", "expect": "ami-id", "description": "不应返回云元数据"},
{"type": "body_not_contains", "expect": "instance-id", "description": "不应返回实例信息"}
],
"vulnerability": {
"id": "2.7.2",
"name": "SSRF云元数据接口访问",
"level": "critical",
"description": "可通过SSRF访问云元数据接口获取敏感信息(CVSS 9.8)",
"fix_suggestion": "禁止请求指向169.254.169.254等元数据地址,限制出站请求目标"
}
}
},
{
"id": "sec_2_7_003",
"module_id": "sec_api07",
"name": "SSRF文件读取-本地文件",
"description": "尝试通过SSRF读取本地文件(file:///etc/passwd)",
"priority": "high",
"tags": ["OWASP-API7", "SSRF", "文件读取"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/message/getMeetingList", "method": "GET"},
"auth": {"required": True, "account": "user"},
"request": {"params": {"pageNo": 1, "pageSize": 10, "url": "file:///etc/passwd"}},
"assertions": [
{"type": "body_not_contains", "expect": "root:", "description": "不应返回本地文件内容"}
],
"vulnerability": {
"id": "2.7.3",
"name": "SSRF文件读取-本地文件",
"level": "high",
"description": "可通过SSRF读取服务器本地文件",
"fix_suggestion": "禁止file://协议,仅允许http/https协议且限制目标域名"
}
}
},
# ==================== API9: 库存管理不当 ====================
{
"id": "sec_2_9_001",
"module_id": "sec_api09",
"name": "未授权访问已废弃API-旧版接口",
"description": "测试旧版API接口是否仍可访问且无需认证",
"priority": "high",
"tags": ["OWASP-API9", "废弃API", "未授权访问"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/v1/api/meeting/list", "method": "GET"},
"auth": {"required": False},
"request": {},
"assertions": [
{"type": "status_code_not_equal", "expect": 200, "description": "旧版API应已下线或需认证"}
],
"vulnerability": {
"id": "2.9.1",
"name": "未授权访问已废弃API-旧版接口",
"level": "high",
"description": "旧版API仍可匿名访问,存在未授权访问风险",
"fix_suggestion": "下线废弃API或增加认证机制,API网关统一管理版本"
}
}
},
{
"id": "sec_2_9_002",
"module_id": "sec_api09",
"name": "测试环境API暴露-调试接口",
"description": "检测测试/调试接口是否在生产环境暴露",
"priority": "medium",
"tags": ["OWASP-API9", "调试接口", "信息泄露"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/actuator", "method": "GET"},
"auth": {"required": False},
"request": {},
"assertions": [
{"type": "status_code_not_equal", "expect": 200, "description": "调试接口不应暴露"}
],
"vulnerability": {
"id": "2.9.2",
"name": "测试环境API暴露-调试接口",
"level": "medium",
"description": "Spring Boot Actuator调试接口在生产环境暴露",
"fix_suggestion": "生产环境禁用Actuator或限制内网访问"
}
}
},
{
"id": "sec_2_9_003",
"module_id": "sec_api09",
"name": "API版本信息泄露-响应头",
"description": "检测API响应头是否泄露服务器版本信息",
"priority": "low",
"tags": ["OWASP-API9", "版本泄露", "信息泄露"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/message/getMeetingList", "method": "GET"},
"auth": {"required": True, "account": "user"},
"request": {"params": {"pageNo": 1, "pageSize": 10}},
"assertions": [
{"type": "header_not_contains", "expect": ["X-Powered-By", "Server"], "description": "响应头不应泄露服务器信息"}
],
"vulnerability": {
"id": "2.9.3",
"name": "API版本信息泄露-响应头",
"level": "low",
"description": "API响应头泄露了服务器版本信息,可辅助攻击者定位漏洞",
"fix_suggestion": "移除X-Powered-By、Server等版本信息响应头"
}
}
},
# ==================== API10: 不安全的第三方API集成 ====================
{
"id": "sec_2_10_001",
"module_id": "sec_api10",
"name": "第三方API响应未校验-WeLink回调",
"description": "测试WeLink回调接口是否校验请求来源,防止伪造回调",
"priority": "high",
"tags": ["OWASP-API10", "第三方API", "回调校验"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/welink/callback", "method": "POST"},
"auth": {"required": False},
"request": {"body": {"event": "meeting.created", "data": {"meetingId": "fake_id"}}},
"assertions": [
{"type": "status_code_not_equal", "expect": 200, "description": "伪造回调应被拒绝"}
],
"vulnerability": {
"id": "2.10.1",
"name": "第三方API响应未校验-WeLink回调",
"level": "high",
"description": "WeLink回调接口未校验请求来源,可被伪造回调注入数据",
"fix_suggestion": "校验回调请求签名,确保请求来自合法的WeLink服务"
}
}
},
{
"id": "sec_2_10_002",
"module_id": "sec_api10",
"name": "第三方API超时未处理-外部服务调用",
"description": "测试外部服务调用是否有超时和降级机制",
"priority": "medium",
"tags": ["OWASP-API10", "超时处理", "服务降级"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/api/welink/sync", "method": "POST"},
"auth": {"required": True, "account": "user"},
"request": {"body": {"timeout_test": True}},
"assertions": [
{"type": "response_time_limit", "expect_max_seconds": 30, "description": "外部调用应在30秒内超时"}
],
"vulnerability": {
"id": "2.10.2",
"name": "第三方API超时未处理-外部服务调用",
"level": "medium",
"description": "外部服务调用无超时机制,第三方服务不可用时会导致系统阻塞",
"fix_suggestion": "为所有外部API调用设置超时时间和降级策略"
}
}
},
{
"id": "sec_2_10_003",
"module_id": "sec_api10",
"name": "第三方API密钥泄露-配置文件",
"description": "检测前端代码或配置文件中是否泄露第三方API密钥",
"priority": "critical",
"tags": ["OWASP-API10", "密钥泄露", "Critical"],
"steps": {
"test_type": "api_security",
"target": {"base_url": "", "path": "/static/config.json", "method": "GET"},
"auth": {"required": False},
"request": {},
"assertions": [
{"type": "body_not_contains", "expect": "api_key", "description": "配置文件不应包含API密钥"},
{"type": "body_not_contains", "expect": "secret", "description": "配置文件不应包含密钥"},
{"type": "body_not_contains", "expect": "app_key", "description": "配置文件不应包含应用密钥"}
],
"vulnerability": {
"id": "2.10.3",
"name": "第三方API密钥泄露-配置文件",
"level": "critical",
"description": "前端配置文件中包含第三方API密钥,可被攻击者获取(CVSS 9.1)",
"fix_suggestion": "将API密钥移至后端环境变量,前端不存储任何密钥信息"
}
}
},
# ==================== API5: 功能级别授权失效 ==================== # ==================== API5: 功能级别授权失效 ====================
{ {
"id": "sec_2_5_1", "id": "sec_2_5_1",
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
"axios": "^1.6.7", "axios": "^1.6.7",
"echarts": "^5.5.0", "echarts": "^5.5.0",
"element-plus": "^2.5.6", "element-plus": "^2.5.6",
"marked": "^18.0.7",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"vue": "^3.4.21", "vue": "^3.4.21",
"vue-router": "^4.3.0" "vue-router": "^4.3.0"
...@@ -2034,6 +2035,18 @@ ...@@ -2034,6 +2035,18 @@
"@jridgewell/sourcemap-codec": "^1.5.5" "@jridgewell/sourcemap-codec": "^1.5.5"
} }
}, },
"node_modules/marked": {
"version": "18.0.7",
"resolved": "https://mirrors.cloud.tencent.com/npm/marked/-/marked-18.0.7.tgz",
"integrity": "sha512-iDVQ5ldaiKXn6b2JroX5kgRfmwgqolW7NpaEzTl1k/2Zh1njIEN9yniyLV/mOvWwtsE8OGgkjsCYvijuPk1dtA==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 20"
}
},
"node_modules/math-intrinsics": { "node_modules/math-intrinsics": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://mirrors.cloud.tencent.com/npm/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "resolved": "https://mirrors.cloud.tencent.com/npm/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
"axios": "^1.6.7", "axios": "^1.6.7",
"echarts": "^5.5.0", "echarts": "^5.5.0",
"element-plus": "^2.5.6", "element-plus": "^2.5.6",
"marked": "^18.0.7",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"vue": "^3.4.21", "vue": "^3.4.21",
"vue-router": "^4.3.0" "vue-router": "^4.3.0"
......
<!-- <!--
页面名称:Reports.vue 页面名称:Reports.vue
页面描述:报告中心页面,展示已完成执行记录并支持生成/导出报告 页面描述:报告中心页面,展示已完成执行记录并支持生成/导出报告
支持 UI 自动化报告(HTML iframe)和安全测试报告(Markdown 渲染)
@author czj @author czj
@date 2026-07-09 @date 2026-07-09
@last-modified 2026-07-22
--> -->
<template> <template>
...@@ -26,6 +28,13 @@ ...@@ -26,6 +28,13 @@
v-loading="loading" v-loading="loading"
> >
<el-table-column prop="name" label="执行名称" min-width="200" /> <el-table-column prop="name" label="执行名称" min-width="200" />
<el-table-column label="报告类型" width="120">
<template #default="{ row }">
<el-tag :type="row.case_type === 'security' ? 'danger' : 'primary'" size="small">
{{ row.case_type === 'security' ? '安全测试' : 'UI自动化' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="通过率" width="120"> <el-table-column label="通过率" width="120">
<template #default="{ row }"> <template #default="{ row }">
<div class="pass-rate-cell"> <div class="pass-rate-cell">
...@@ -69,10 +78,6 @@ ...@@ -69,10 +78,6 @@
<el-icon><View /></el-icon> <el-icon><View /></el-icon>
查看报告 查看报告
</el-button> </el-button>
<el-button size="small" type="primary" link @click="exportReport(row)">
<el-icon><Download /></el-icon>
导出
</el-button>
<el-button size="small" type="primary" link @click="downloadReport(row)"> <el-button size="small" type="primary" link @click="downloadReport(row)">
<el-icon><Files /></el-icon> <el-icon><Files /></el-icon>
下载 下载
...@@ -92,10 +97,10 @@ ...@@ -92,10 +97,10 @@
</div> </div>
</el-card> </el-card>
<!-- 报告预览弹窗 --> <!-- UI 自动化报告预览弹窗 -->
<el-dialog <el-dialog
v-model="previewDialogVisible" v-model="previewDialogVisible"
title="测试报告" title="UI 自动化测试报告"
width="90%" width="90%"
top="5vh" top="5vh"
destroy-on-close destroy-on-close
...@@ -111,6 +116,34 @@ ...@@ -111,6 +116,34 @@
<el-empty v-else description="报告加载失败" /> <el-empty v-else description="报告加载失败" />
</div> </div>
</el-dialog> </el-dialog>
<!-- 安全测试报告预览弹窗 -->
<el-dialog
v-model="securityPreviewVisible"
title="安全测试报告"
width="90%"
top="5vh"
destroy-on-close
fullscreen
>
<template #header>
<div class="security-report-header">
<span>安全测试报告</span>
<el-button type="primary" size="small" @click="downloadSecurityReport">
<el-icon><Download /></el-icon>
下载报告
</el-button>
</div>
</template>
<div class="security-report-container" v-loading="loadingSecurity">
<div
v-if="securityReportContent"
class="markdown-body"
v-html="renderedMarkdown"
/>
<el-empty v-else-if="!loadingSecurity" description="报告加载失败" />
</div>
</el-dialog>
</div> </div>
</template> </template>
...@@ -119,9 +152,9 @@ ...@@ -119,9 +152,9 @@
* 报告中心页面 * 报告中心页面
* *
* 功能: * 功能:
* 1. 展示已完成执行记录列表 * 1. 展示已完成执行记录列表(支持 UI 自动化和安全测试)
* 2. 预览 HTML 报告 * 2. 预览 UI 自动化报告(HTML iframe)
* 3. 导出报告为文件 * 3. 预览安全测试报告(Markdown 渲染)
* 4. 下载报告文件 * 4. 下载报告文件
*/ */
...@@ -129,7 +162,9 @@ import { ref, computed, watch, onMounted } from 'vue' ...@@ -129,7 +162,9 @@ import { ref, computed, watch, onMounted } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { Refresh, View, Download, Files } from '@element-plus/icons-vue' import { Refresh, View, Download, Files } from '@element-plus/icons-vue'
import { marked } from 'marked'
import { reportApi } from '@/api/reports' import { reportApi } from '@/api/reports'
import { getSecurityReport, getSecurityReportDownloadUrl } from '@/api/security'
const route = useRoute() const route = useRoute()
...@@ -141,12 +176,25 @@ const total = ref(0) ...@@ -141,12 +176,25 @@ const total = ref(0)
const currentPage = ref(1) const currentPage = ref(1)
const pageSize = ref(20) const pageSize = ref(20)
// UI 自动化报告预览
const previewDialogVisible = ref(false) const previewDialogVisible = ref(false)
const previewUrl = ref('') const previewUrl = ref('')
// 安全测试报告预览
const securityPreviewVisible = ref(false)
const securityReportContent = ref('')
const loadingSecurity = ref(false)
const currentSecurityExecutionId = ref('')
/** 当前用例类型(来自路由参数,默认 ui) */ /** 当前用例类型(来自路由参数,默认 ui) */
const currentType = computed(() => (route.params.type as string) || 'ui') const currentType = computed(() => (route.params.type as string) || 'ui')
/** 渲染后的 Markdown */
const renderedMarkdown = computed(() => {
if (!securityReportContent.value) return ''
return marked.parse(securityReportContent.value) as string
})
// ==================== 方法定义 ==================== // ==================== 方法定义 ====================
const loadReports = async () => { const loadReports = async () => {
...@@ -189,23 +237,60 @@ const formatTime = (time: string) => { ...@@ -189,23 +237,60 @@ const formatTime = (time: string) => {
}) })
} }
const viewReport = (row: any) => { /**
previewUrl.value = reportApi.generateUrl(row.id) * 查看报告
previewDialogVisible.value = true * - UI 自动化报告:iframe 预览 HTML
} * - 安全测试报告:Markdown 渲染
*/
const viewReport = async (row: any) => {
if (row.case_type === 'security') {
// 安全测试报告 - Markdown 渲染
currentSecurityExecutionId.value = row.id
securityReportContent.value = ''
securityPreviewVisible.value = true
loadingSecurity.value = true
const exportReport = async (row: any) => {
try { try {
const result = await reportApi.export(row.id) const response = await getSecurityReport(row.id)
ElMessage.success(`报告已导出: ${result.file_path}`) securityReportContent.value = response.data.content
} catch (error: any) { } catch (error: any) {
ElMessage.error('导出报告失败: ' + error.message) ElMessage.error('加载安全测试报告失败: ' + error.message)
securityReportContent.value = ''
} finally {
loadingSecurity.value = false
}
} else {
// UI 自动化报告 - iframe 预览
previewUrl.value = reportApi.generateUrl(row.id)
previewDialogVisible.value = true
} }
} }
/**
* 下载报告
* - UI 自动化报告:下载 HTML 文件
* - 安全测试报告:下载 Markdown 文件
*/
const downloadReport = (row: any) => { const downloadReport = (row: any) => {
if (row.case_type === 'security') {
// 安全测试报告 - 下载 Markdown
const url = getSecurityReportDownloadUrl(row.id)
window.open(url, '_blank')
} else {
// UI 自动化报告 - 下载 HTML
const url = reportApi.downloadUrl(row.id) const url = reportApi.downloadUrl(row.id)
window.open(url, '_blank') window.open(url, '_blank')
}
}
/**
* 下载安全测试报告(弹窗内按钮)
*/
const downloadSecurityReport = () => {
if (currentSecurityExecutionId.value) {
const url = getSecurityReportDownloadUrl(currentSecurityExecutionId.value)
window.open(url, '_blank')
}
} }
// ==================== 生命周期 ==================== // ==================== 生命周期 ====================
...@@ -272,5 +357,156 @@ watch(currentType, () => { ...@@ -272,5 +357,156 @@ watch(currentType, () => {
border-radius: 4px; border-radius: 4px;
} }
} }
// 安全测试报告样式
.security-report-header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.security-report-container {
height: calc(100vh - 200px);
overflow-y: auto;
padding: 20px;
background-color: var(--el-bg-color);
border-radius: 4px;
}
}
// Markdown 样式(GitHub 风格)
.markdown-body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.6;
color: var(--el-text-color-primary);
:deep(h1) {
font-size: 2em;
font-weight: 600;
padding-bottom: 0.3em;
border-bottom: 1px solid var(--el-border-color);
margin-top: 24px;
margin-bottom: 16px;
}
:deep(h2) {
font-size: 1.5em;
font-weight: 600;
padding-bottom: 0.3em;
border-bottom: 1px solid var(--el-border-color);
margin-top: 24px;
margin-bottom: 16px;
}
:deep(h3) {
font-size: 1.25em;
font-weight: 600;
margin-top: 24px;
margin-bottom: 16px;
}
:deep(h4) {
font-size: 1em;
font-weight: 600;
margin-top: 24px;
margin-bottom: 16px;
}
:deep(p) {
margin-bottom: 16px;
}
:deep(table) {
border-collapse: collapse;
width: 100%;
margin-bottom: 16px;
th, td {
padding: 8px 13px;
border: 1px solid var(--el-border-color);
}
th {
font-weight: 600;
background-color: var(--el-fill-color-light);
}
tr:nth-child(2n) {
background-color: var(--el-fill-color-lighter);
}
}
:deep(code) {
padding: 0.2em 0.4em;
margin: 0;
font-size: 85%;
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
background-color: var(--el-fill-color-light);
border-radius: 6px;
}
:deep(pre) {
padding: 16px;
overflow: auto;
font-size: 85%;
line-height: 1.45;
background-color: var(--el-fill-color-light);
border-radius: 6px;
margin-bottom: 16px;
code {
padding: 0;
background-color: transparent;
}
}
:deep(ul), :deep(ol) {
padding-left: 2em;
margin-bottom: 16px;
}
:deep(li) {
margin-bottom: 4px;
}
:deep(blockquote) {
padding: 0 1em;
color: var(--el-text-color-secondary);
border-left: 0.25em solid var(--el-border-color);
margin-bottom: 16px;
}
:deep(hr) {
height: 0.25em;
padding: 0;
margin: 24px 0;
background-color: var(--el-border-color);
border: 0;
}
:deep(a) {
color: var(--el-color-primary);
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
:deep(img) {
max-width: 100%;
height: auto;
}
// 特殊样式:OWASP 矩阵表格
:deep(.owasp-matrix) {
text-align: center;
td:first-child {
text-align: left;
}
}
} }
</style> </style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论