# Phase 2 逐行走读 — FINDING 全量汇总表

**日期**: 2026-05-22（状态同步 2026-05-23）  
**来源**: `audit-phase-2a` … `audit-phase-2f` 六份走读报告 + `2026-05-22-security-fixes-p0/p1`  
**统计**: 共 **63** 条独立 ID · **✅ 52** · **🟡 3** · **📋 7** · **— 1** · **代码待办 0**  
**开发 SSOT**: [`docs/project/phase-2-audit-remediation.md`](../project/phase-2-audit-remediation.md)  
**走读闭环**: `docs/reports/audit-phase-2-walk-closure.md`

---

## 状态图例

| 状态 | 含义 |
|------|------|
| ✅ FIXED | P0/P1 批次已落地或走读确认代码已改 |
| 🟡 PARTIAL | 有缓解但未根治 |
| ⏳ OPEN | 尚未修复 |
| 📋 ACCEPTED | 已知设计取舍，需文档/运维约束 |
| — | 中心路由已删除，仅子机 Agent 适用 |

---

## 按优先级 — 待办队列（先看这个）

| 优先级 | ID | 状态 | 一句话 |
|--------|-----|------|--------|
| **P0** | F2f-04 | ✅ | 压测改环境变量；**未部署无需轮换**，首次 install 生成新 key |
| **P1** | F2e-01/02/07 | ✅ | 前端 esc / data-setting-key |
| **P1** | F2f-02/03 | ✅ | Agent 强制 API Key + 默认 127.0.0.1 |
| **P1** | F2a-12 | ✅ | 生成 key 一次性展示 + 编辑表单不填掩码 |
| **P1** | F2f-01 | 📋 | 子机 shell 执行面 — SSOT §4.1 + Agent 审计日志（架构接受） |
| **P1** | F2a-08 | ✅ | 安装后除 status 外 install API 403 |
| **P2** | F2g-03 | ✅ | `_parse_server_ids_json` |
| **P2** | F2g-04 | 📋 | 重试新建 id + `parent_execution_id` |
| **P2** | F2g-05, F2h-01 | ✅ | `credential_id` 贯通 + 前端下拉 |
| **P1** | F2h-03 | ✅ | kill 仅 `_running_procs` |

---

## 全量矩阵

### Phase 2a — `server/api/`（18）

| ID | 文件:行 | 类 | 原级 | 状态 | 描述 | 备注 / 修复 |
|----|---------|----|------|------|------|-------------|
| F2a-01 | `webssh.py` | VULN | P0 | ✅ | WebSSH IDOR：普通 JWT 连任意 server | `webssh-token` + 强制 server_id |
| F2a-02 | `agent.py`（中心） | VULN | P0 | ✅ | 中心 `/api/agent/exec` 本机 RCE | 路由已删除 |
| F2a-03 | `agent.py`（中心） | RISK | P2 | ✅ | API Key 非恒定时间比较 | `compare_digest` |
| F2a-04 | `agent.py`（中心） | VULN | P1 | — | sudo 命令拼接无转义 | 中心 exec 已删；子机见 F2f-01 + `shlex` |
| F2a-05 | `webssh.py` | RISK | P2 | 🟡 | JWT 在 WebSocket URL | 同 F2e-04：已 webssh-token，非 access JWT |
| F2a-06 | `webssh.py` | BUG | P2 | ✅ | 鉴权失败未 close | `close(4003)` |
| F2a-07 | `auth.py` | RISK | P2 | ✅ | TOTP 关闭无二次确认 | 需密码+TOTP |
| F2a-08 | `install.py` | RISK | P1 | ✅ | 安装后 `/api/install/*` 仍注册 | `_reject_if_installed()` 除 GET `/status` 外 403 |
| F2a-09 | `install.py` | VULN | P2 | ✅ | `.env` 品牌名未转义 | `_escape_env_value` |
| F2a-10 | `auth_jwt.py` | EXT | P2 | ✅ | 无全局 JWT 中间件 | `JwtAuthMiddleware` + `PUBLIC_PREFIXES` |
| F2a-11 | `sync_v2.py` | VULN | P1 | ✅ | 任意 commands[] → SSH | 危险命令拒绝 |
| F2a-12 | `servers.py:305` | RISK | P1 | ✅ | `generate_agent_api_key` 返回完整 key | `display_once` + 编辑表单不填掩码 |
| F2a-13 | `servers.py` | BUG | P2 | ✅ | `stats.online` 用 MySQL 非 Redis | stats 优先 Redis `heartbeat:*` |
| F2a-14 | `scripts.py` | BUG | P2 | ✅ | `/credentials` 路由顺序 | 2h VERIFY：56–205 在 `/{id}` 前 |
| F2a-15 | `scripts.py` | VULN | P1 | ✅ | 危险命令仅告警不拦 | 2h VERIFY：`/exec`+`retry` 均 400 阻断 |
| F2a-16 | `settings.py` | RISK | P1 | ✅ | API Key reveal 无二次验证 | 需管理员密码+审计 |
| F2a-17 | `dependencies.py` | RISK | P2 | ✅ | 危险命令不阻断 | 与 F2a-15 同修复 |
| F2a-18 | `websocket.py` | RISK | P2 | 📋 | 告警 WS JWT 在 URL | 同 F2e-03：设计接受 |

---

### Phase 2b — `server/application/services/`（9）

| ID | 文件:行 | 类 | 原级 | 状态 | 描述 | 备注 / 修复 |
|----|---------|----|------|------|------|-------------|
| F2b-01 | `sync_engine_v2.py:234` | VULN | P1 | ✅ | echo key=value shell 注入 | `shlex.quote` 整段 |
| F2b-02 | `sync_engine_v2.py:236` | VULN | P1 | ✅ | sysctl 注入 | 同上 |
| F2b-03 | `sync_engine_v2.py:284` | VULN | P1 | ✅ | cp 回滚路径未校验 | 白名单 + quote |
| F2b-04 | `sync_engine_v2.py:158` | VULN | P1 | ✅ | sync_commands 原样 SSH | 危险命令拒绝 |
| F2b-05 | `sync_engine_v2.py:101` | BUG | P2 | ✅ | `duration_seconds` 用 `.seconds` | 改 `total_seconds()` |
| F2b-06 | `sync_service.py:117` | BUG | P1 | ✅ | `completed += 1` 缺 nonlocal | 已加 nonlocal |
| F2b-07 | `script_service.py:216` | RISK | P1 | ✅ | Agent 调用 http 明文 | `agent_url.py` + HTTPS |
| F2b-08 | `server_service.py:56` | RISK | P1 | ✅ | 健康检查 http 明文 | 同上 |
| F2b-09 | `script_service.py:179` | VULN | P2 | ✅ | 凭据测试返回含密码命令 | 不再返回 command |

---

### Phase 2c — `server/infrastructure/`（6）

| ID | 文件:行 | 类 | 原级 | 状态 | 描述 | 备注 / 修复 |
|----|---------|----|------|------|------|-------------|
| F2c-01 | `crypto.py:75-83` | BUG | P1 | ✅ | 解密失败返回密文 | 抛 `ValueError` |
| F2c-02 | `asyncssh_pool.py` | RISK | P1 | ✅ | SSH 严格主机校验可关 | 新装默认 true |
| F2c-03 | — | — | — | — | （报告无 F2c-03 编号） | — |
| F2c-04 | `telegram/__init__.py` | RISK | P2 | ✅ | Telegram HTML 未转义 | 已转义 |
| F2c-05 | `migrations.py:97-99` | RISK | P2 | ✅ | 迁移失败仅 log，半迁移启动 | 非 duplicate column 错误 re-raise |
| F2c-06 | `push_schedule_repo.py` | RISK | P2 | 📋 | `update_status(**kwargs)` 任意列 | 当前仅内部调用 |

---

### Phase 2d — `main.py` + `config.py` + `background/`（4）

| ID | 文件:行 | 类 | 原级 | 状态 | 描述 | 备注 / 修复 |
|----|---------|----|------|------|------|-------------|
| F2d-01 | `config.py` | BUG | P2 | ✅ | 缺 `API_BASE_URL` 字段 | 已补字段 |
| F2d-02 | `main.py:277-295` | EXT | P2 | ✅ | 无全局 JWT 中间件 | `JwtAuthMiddleware`（在 DbSession 内层） |
| F2d-03 | `main.py:287-288` | RISK | P2 | ✅ | 安装模式 `/ws/` 仍可连 | InstallModeMiddleware 返回 503 |
| F2d-04 | `self_monitor.py:72` | RISK | P2 | ✅ | MySQL 异常进 Telegram | `sanitize_external_message` + 固定文案；详情仅写日志 |

---

### Phase 2e — `web/app/`（7）

| ID | 文件:行 | 类 | 原级 | 状态 | 描述 | 备注 / 修复 |
|----|---------|----|------|------|------|-------------|
| F2e-01 | `servers.html:161` | VULN | P1 | ✅ | `category` 未 esc | `${esc(...)}` |
| F2e-02 | `settings.html:147` | VULN | P1 | ✅ | onclick 拼 settings key | `data-key` + escAttr |
| F2e-03 | `index.html:208` | RISK | P2 | 📋 | 告警 WS JWT 在 query | **已讨论**：维持现状；脚本进度用 HTTP 轮询，不做 WS 换票 |
| F2e-04 | `terminal.html:97` | RISK | P2 | 🟡 | WebSSH token 仍在 URL | **P0 已做** `POST /auth/webssh-token` 专用短票+server 绑定；query 带票为浏览器 WS 限制 |
| F2e-05 | `api.js` | RISK | P2 | 📋 | JWT 在 localStorage | CSP + 长期改 HttpOnly |
| F2e-06 | 全站 HTML | RISK | P2 | ✅ | CDN 无 SRI | `web/app/vendor/` 自托管 + `sync_frontend_vendor.py` |
| F2e-07 | `scripts.html:36` | VULN | P1 | ✅ | `category` 未 esc | 同 F2e-01 |

---

### Phase 2f — `web/agent/` + `tests/` + `scripts/`（8）

| ID | 文件:行 | 类 | 原级 | 状态 | 描述 | 备注 / 修复 |
|----|---------|----|------|------|------|-------------|
| F2f-01 | `agent.py:119-123` | VULN | P1 | 📋 | 子机 `/exec` subprocess_shell | 网络隔离 + 审计；见 SSOT §3 |
| F2f-02 | `agent.py:67` | VULN | P1 | ✅ | API_KEY 为空不鉴权 | 启动强制非空 |
| F2f-03 | `agent.py:268-280` | RISK | P2 | ✅ | 默认 `0.0.0.0:8601` | 默认 127.0.0.1 |
| F2f-04 | `quick_load.py`, `load_test.py` | SECRET | **P0** | ✅ | 仓库硬编码 API Key | 环境变量；未部署则首次 install 新 key |
| F2f-05 | `bootstrap_database.py:67` | RISK | P2 | ✅ | CREATE DATABASE f-string | 库名白名单 |
| F2f-06 | `tests/*` | GAP | P2 | 🟡 | 缺 P0 回归测试 | `test_security_unit.py` 17 项；E2E 见验证清单 |
| F2f-07 | `wsl_mcp_mysql.sh` | STYLE | P2 | ✅ | 硬编码绝对路径 | 相对路径 |
| F2f-08 | `grant_nexus_local.sql` | RISK | P2 | 📋 | GRANT ALL 本地 dev | 注释勿用于生产 |

---

### Phase 2g — 脚本执行平台（5 文件，2026-05-22 新增代码）

| ID | 文件:行 | 类 | 原级 | 状态 | 描述 | 备注 / 修复 |
|----|---------|----|------|------|------|-------------|
| F2g-01 | `script_jobs.py:38-58` | VULN | P1 | 📋 | 长任务 `bash -c` 包装用户脚本 | 与 F2f-01 同根因 |
| F2g-02 | `script_jobs.py:47-49` | RISK | P2 | ✅ | 回调 curl 随 API_BASE_URL 可能 HTTP | `agent_url.py` 强制 HTTPS |
| F2g-03 | `script_execution_store.py:192` | BUG | P2 | ✅ | `json.loads(server_ids)` 无 try | `_parse_server_ids_json()` |
| F2g-04 | `script_service.py:477-485` | UX | P2 | 📋 | 重试创建新 execution id | API 返回 `parent_execution_id` + 文档 |
| F2g-05 | `script_service.py:477-485` | BUG | P2 | ✅ | 重试丢失 credential_id | `credential_id` 列 + migration |
| F2g-06 | `agent.py:209` | RISK | P2 | ✅ | script-callback 无限流 | `script_callback_rate.py` |

---

### Phase 2h — API + 前端 + 子机 Agent（5 文件）

| ID | 文件:行 | 类 | 原级 | 状态 | 描述 | 备注 / 修复 |
|----|---------|----|------|------|------|-------------|
| F2h-01 | `schemas.py:141` + `scripts.py:314` | BUG | P2 | ✅ | 无 `credential_id` 字段 | 同 F2g-05 |
| F2h-02 | `agent.py:209` | RISK | P2 | ✅ | script-callback 无限流 | 同 F2g-06 |
| F2h-03 | `web/agent/agent.py:191` | VULN | P1 | ✅ | kill 未登记 pid 仍 `os.kill` | 仅 `_running_procs` |
| F2h-04 | `web/agent/agent.py:215` | BUG | P2 | ✅ | `/exec/wait` 超时未 pop | 清理 + kill |
| F2h-05 | `scripts.html:254` | RISK | P2 | 📋 | 日志整体 esc 后 innerHTML | 2h 确认 SAFE，保持 textContent/esc |

---

### Phase 2i — 脚本支撑层（5 文件）

| ID | 文件:行 | 类 | 原级 | 状态 | 描述 | 备注 / 修复 |
|----|---------|----|------|------|------|-------------|
| F2i-01 | `script_repo.py:83-94` | BUG | P2 | ✅ | `update_status` 无记录返回 None | Optional 返回 + flush 出队 |
| F2i-02 | `schemas.py:24-27` | OPT | P2 | ✅ | `AgentExec` 死模型 | 已删除 |

---

## 去重聚类（同一根因只修一次）

| 主题 | 关联 ID | 建议单一修复 |
|------|---------|--------------|
| WebSocket / URL 带令牌 | F2a-05, F2e-04 | WebSSH：P0 已 `webssh-token`；告警 WS：📋 接受 `?token=` |
| 无全局 JWT | F2a-10, F2d-02 | ✅ `JwtAuthMiddleware`（`main.py` 注册） |
| 危险远程执行 | F2f-01, F2g-01, F2a-11/15（已拦中心） | Agent 网络隔离 + 命令策略（与 2b 对齐） |
| 脚本重试语义 | F2g-04, F2g-05 | 同 id 续跑 + 存 credential_id |
| 长任务回调传输 | F2g-02, F2g-06, F2h-02 | HTTPS + 回调限流 |
| Agent kill 任意 pid | F2h-03 | 仅允许 `_running_procs` 内 pid |
| flush 空 execution | F2i-01 | `update_status` 失败时勿 flush |
| credential 执行 | F2g-05, F2h-01 | schema + API + 前端传参 |
| 前端 XSS（未 esc） | F2e-01, F2e-07 | 抽公共 `esc`/`escAttr`，扫一遍 innerHTML |
| API Key 泄露面 | F2f-04, F2a-12, F2a-16（已加固 reveal） | 密钥不进 git；生成 key 只显示一次 |

---

## 统计摘要

| 维度 | 数量 |
|------|------|
| 总 FINDING ID | 63 |
| ✅ 已修复 | **52** |
| 🟡 部分修复 | **3**（F2a-05/F2e-04 URL 带票、F2f-06 E2E） |
| 📋 接受/低危观察 | **7** |
| — 不适用 | **1**（F2a-04，中心 exec 已删） |
| ⏳ 代码待办 | **0** |
| P0 运维待办 | **0**（未部署；F2f-04 不适用） |
| P2 验证待办 | **1**（首次部署 E2E 清单） |

---

## 建议修复顺序（工程队列）

1. ~~**F2f-04**~~ — 未部署：首次 install 新 key 即可
2. **首次部署** — `docs/project/production-verification-checklist.md`

**不在队列**：WebSocket「换票」— 脚本执行已明确 **JWT + HTTP 轮询**（`script-execution.md`）；WebSSH 已 **POST webssh-token**；告警 WS 维持 `?token=` + 日志脱敏约束。

---

## 报告索引

| 批次 | 路径 |
|------|------|
| 2a | `docs/reports/audit-phase-2a-line-walk.md` |
| 2b | `docs/reports/audit-phase-2b-line-walk.md` |
| 2c | `docs/reports/audit-phase-2c-line-walk.md` |
| 2d | `docs/reports/audit-phase-2d-line-walk.md` |
| 2e | `docs/reports/audit-phase-2e-line-walk.md` |
| 2f | `docs/reports/audit-phase-2f-line-walk.md` |
| 2g | `docs/reports/audit-phase-2g-line-walk.md`（脚本执行 Redis/长任务/script_service 803 行） |
| 2h | `docs/reports/audit-phase-2h-line-walk.md`（API+前端+子机 Agent，+5 FINDING） |
| 2i | `docs/reports/audit-phase-2i-line-walk.md`（script_repo/schemas/main） |
| 2j | `docs/reports/audit-phase-2j-line-walk.md`（安全修复 VERIFY） |
| **闭环** | `docs/reports/audit-phase-2-walk-closure.md` |
| 修复记录 | `docs/changelog/2026-05-22-security-fixes-p0.md`, `...-p1.md` |
