# Phase 2f 逐行走读 — `web/agent/` + `tests/` + `scripts/`

**标准**: `docs/project/line-walk-audit-standard.md`  
**日期**: 2026-05-22

---

## 走读签字

| 项 | 值 |
|----|-----|
| 计划文件数 | 20 |
| 完成文件数 | 20 |
| 新增 FINDING | 8 |
| 批次 | 3（5+5+10） |

---

## 文件清单

| 文件 | N(约) | 状态 |
|------|-------|------|
| `web/agent/agent.py` | 283 | ✓ |
| `web/agent/agent.sh` | 99 | ✓ |
| `web/agent/install.sh` | 137 | ✓ |
| `web/agent/config.example.json` | 15 | ✓ |
| `tests/conftest.py` | 96 | ✓ |
| `tests/test_api.py` | 183 | ✓ |
| `tests/test_step0_infrastructure.py` | 153 | ✓ |
| `tests/test_step1to5_features.py` | 166 | ✓ |
| `tests/test_nexus_v6.py` | 213 | ✓ |
| `tests/quick_load.py` | 47 | ✓ |
| `tests/load_test.py` | 205 | ✓ |
| `scripts/bootstrap_database.py` | 112 | ✓ |
| `scripts/sync_mysql_mcp_env.py` | 148 | ✓ |
| `scripts/wsl_check_mysql.py` | 67 | ✓ |
| `scripts/wsl_show_grants.py` | 46 | ✓ |
| `scripts/grant_nexus_local.sql` | 10 | ✓ |
| `scripts/wsl_mcp_mysql.sh` | 25 | ✓ |
| `scripts/wsl_install_node20.sh` | 118 | ✓ |
| `scripts/cache-node20-windows.ps1` | 29 | ✓ |

---

## Findings

| ID | 文件:行 | 规则 | 度 | 描述 | 修复建议 |
|----|---------|------|----|------|----------|
| F2f-01 | `agent.py:119-123` | AG-02 | P1 | `/exec` 为 `create_subprocess_shell`，持 Key 即远程 shell | 文档化 + 网络隔离；可选非 shell 执行 |
| F2f-02 | `agent.py:67` | AG-01 | P1 | `API_KEY` 为空时不鉴权 | 启动强制非空 API_KEY |
| F2f-03 | `agent.py:268-280` | AG-03 | P2 | 默认 `0.0.0.0:8601` | 默认 `127.0.0.1` + 防火墙 |
| F2f-04 | `quick_load.py:6`, `load_test.py:18` | TS-01 | **P0** | **仓库内硬编码生产型 API Key** `msk-ufkv...` | 改 `os.environ` / `.env`；轮换已泄露 Key |
| F2f-05 | `bootstrap_database.py:67` | SC-01 | P2 | `CREATE DATABASE` 用 f-string 拼库名 | 白名单校验 `cfg['database']` |
| F2f-06 | `tests/*` | TS-02 | P2 | 无 P0 回归：WebSSH token、危险命令拒绝、sysctl quote | 补 pytest 用例 |
| F2f-07 | `wsl_mcp_mysql.sh:5-10` | SC-02 | P2 | 硬编码本机绝对路径 | 用 `$PROJECT_ROOT` 或相对路径 |
| F2f-08 | `grant_nexus_local.sql:4-5` | SC-03 | P2 | `GRANT ALL` 仅适合本地 dev | 注释标明勿用于生产 |

---

## Closure 摘要

### Agent（批次 1）

见上文 F2f-01～03；`config.example.json` 占位符 **SAFE**。

### Tests（批次 2）

| 文件 | 判定 | 要点 |
|------|------|------|
| `test_step0_infrastructure.py` | SAFE | — | Redis/WS/SSH 池单元测试；124 行断言 `/ws/alerts` 公开（与 P2-6 一致，**RISK** 文档化） |
| `test_step1to5_features.py` | SAFE | — | JWT/TOTP/模型 mock；无密钥硬编码 |
| `test_nexus_v6.py` | SAFE | — | 与 step1to5 重叠，纯 mock |
| `quick_load.py` | **FINDING** | P0 | F2f-04 |
| `load_test.py` | **FINDING** | P0 | F2f-04；`--prod` 注入 Key；仍压测 `/api/servers/{id}/exec`（需 JWT，非已删中心 `/api/agent/exec`） |
| `test_api.py` | RISK | P2 | 默认 `admin` 密码，仅本地 |

### Scripts（批次 3）

| 文件 | 判定 | 要点 |
|------|------|------|
| `bootstrap_database.py` | RISK | P2 | F2f-05；仅从 `NEXUS_*` 读 `.env` **SAFE** |
| `sync_mysql_mcp_env.py` | RISK | P2 | 把 DB 密码写入 `.env` MCP 块（注释已提醒勿提交） |
| `wsl_check_mysql.py` | SAFE | — | 连通性检查 |
| `wsl_show_grants.py` | SAFE | — | 只读 SHOW GRANTS |
| `grant_nexus_local.sql` | RISK | P2 | F2f-08 |
| `wsl_mcp_mysql.sh` | RISK | P2 | F2f-07 |
| `wsl_install_node20.sh` | SAFE | — | 离线/在线装 Node，清坏 proxy |
| `cache-node20-windows.ps1` | SAFE | — | 下载到 `.cache/`，无密钥 |

---

## 八类核对

| 类 | 条数 |
|----|------|
| VULN | 2（F2f-01/02） |
| SECRET | 1（F2f-04 P0） |
| GAP | 1（F2f-06 测试覆盖） |
| RISK | 其余 |

---

## Phase 1 / P0 复核

| 项 | 结论 |
|----|------|
| Agent `compare_digest` | **VERIFY** |
| 中心 `/api/agent/exec` 移除 | **VERIFY** |
| 压测脚本仍含真实 API Key | **REGRESSION** — F2f-04 需立即处理 |

---

## Phase 2 总结

| 批次 | 范围 | 报告 | 状态 |
|------|------|------|------|
| 2a | `server/api/` | `audit-phase-2a-line-walk.md` | ✅ |
| 2b | `application/services/` | `audit-phase-2b-line-walk.md` | ✅ |
| 2c | `infrastructure/` | `audit-phase-2c-line-walk.md` | ✅ |
| 2d | `main/config/background` | `audit-phase-2d-line-walk.md` | ✅ |
| 2e | `web/app/` | `audit-phase-2e-line-walk.md` | ✅ |
| 2f | agent/tests/scripts | 本报告 | ✅ |

**Phase 2 逐行走读已全部完成。** 建议下一步：汇总 FINDING 矩阵 → 按 P0/P1 排修复队列（含 F2f-04 密钥轮换、F2e-01/02/07 前端 esc）。
