release: nexus btpanel session fix and app-v2

This commit is contained in:
Codex Release Bot
2026-07-08 22:31:31 +08:00
commit 1933f0af6e
2457 changed files with 350105 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
{
"mcpServers": {
"mysql-mcp": {
"command": "bash",
"args": ["scripts/linux_mcp_mysql.sh"],
"env": {}
}
}
}
@@ -0,0 +1,608 @@
---
name: Nexus 全面代码审计
overview: 对 Nexus 6.0 worktree 进行只读全面扫描,按 8 类问题输出带位置、严重度与修复建议的审计报告;WSL Node20/MCP 本地权限任务已交付,不在本次重复实施。
todos:
- id: fix-p0-webssh
content: "P0: WebSSH 强制 JWT server_id 绑定 + 失败时 close WebSocket"
status: completed
- id: fix-p1-xss
content: "P1: servers/scripts 等 innerHTML 字段统一 esc()"
status: pending
- id: fix-p1-agent-apikey
content: "P1: 收敛 Agent exec + API Key reveal + compare_digest"
status: pending
- id: fix-p2-heartbeat-stats
content: "P2: 心跳仅 Redis + 在线统计与列表数据源一致"
status: pending
- id: fix-p2-hardening
content: "P2: install 路由锁定、TOTP disable 二次验证、全局 JWT 中间件"
status: pending
- id: add-security-tests
content: 补充 auth/webssh/agent 集成测试与 pip-audit CI
status: pending
isProject: false
---
# Nexus 6.0 全面代码审计报告
**范围**: [`server/`](server/)、[`web/app/`](web/app/)、[`scripts/`](scripts/)、[`deploy/`](deploy/)、[`requirements.txt`](requirements.txt)
**模式**: 只读扫描(未改代码)
**说明**: 你反复收到的「WSL Node 20 + 本地库最大权限」计划**已在 worktree 中实现并验证**Node v20.19.2、MYSQL_READONLY=false、GRANT ALL、MCP Connected)。请勿再重复执行该计划;下文为**新的**全项目审计。
**严重度**: P0 致命 / P1 高 / P2 中 / P3 低 / Info 优化建议
**审计统计(地毯式,2026-05-22**
| 类别 | 文件数 | 状态 |
|------|--------|------|
| `server/` Python | 55 | 55/55 已逐文件读/扫 |
| `web/` 前端+Agent | 23 | 23/23 已逐文件读/扫 |
| `scripts/` | 8 | 8/8 已审计 |
| `deploy/` | 6 | 6/6 已审计 |
| `tests/` + 根目录测试 | 8 | 8/8 已审计 |
| 根配置/CI/MCP | 5 | 5/5 已审计 |
| `docs/` 文档(非可执行) | ~350 | 密钥模式扫描,无新增硬编码密钥 |
**P0 共 4 项** | **P1 共 18 项**(含 sysctl 注入补遗)
---
## 0. P0 / P1 问题总表(复查用)
### P04
| ID | 文件 | 问题 |
|----|------|------|
| S-01 | `server/api/webssh.py` | 标准 JWT 无 `server_id` 时可 IDOR 连任意服务器 SSH |
| S-02 | `server/api/agent.py` | `/api/agent/exec` + `subprocess_shell` + 全局 API Key = 中枢 RCE |
| S-13 | `tests/load_test.py`, `tests/quick_load.py` | **仓库内硬编码 `msk-ufkvjm8...` API Key**(若生产密钥则灾难性泄露) |
| S-14 | `web/agent/agent.py` | 子机 Agent `/exec` 同样 shell 执行;`verify_api_key``!=` 非常量时间 |
### P118,节选)
| ID | 文件 | 问题 |
|----|------|------|
| **S-19** | `server/application/services/sync_engine_v2.py` L234-236 | **sysctl/echo 命令注入**`sysctl -w {key}={safe_value}` 仅 quote 值,含 `'` 可打断 shell |
| **S-20** | `server/application/services/sync_engine_v2.py` L281-284 | **rollback 命令注入**`backup_file` 未 quote 拼入 `cp ... && sysctl --system` |
| S-03 | `server/api/settings.py` | `/api-key/reveal` 回读全局 API_KEY |
| S-04 | `web/app/servers.html` | `category`/`agent_version` 未 esc → XSS |
| S-05 | `web/app/scripts.html` | `category` 未 esc → XSS |
| S-06 | `web/app/api.js` | JWT 存 localStorage |
| S-07 | `web/app/index.html`, `terminal.html` | WS JWT 在 URL |
| S-08 | `server/api/install.py` | 生产仍暴露无认证 install API |
| S-09 | `server/api/auth.py` | TOTP disable 无二次验证 |
| S-15 | `server/api/agent.py` | 全局 Key 可伪造任意无 per-key 服务器的心跳 |
| S-16 | `web/install.php` | 遗留 PHP 安装面(与 install.html 双轨) |
| S-17 | `server/main.py` + FastAPI | `/docs` `/openapi.json` 未鉴权暴露 API 结构 |
| R-01 | `server/infrastructure/database/crypto.py` | AES-CBC 遗留 + 解密失败返回密文 |
| E-01 | `server/main.py` | 无全局 JWT 中间件,仅靠各路由 Depends |
| P-01 | `server/api/agent.py` | 心跳直写 MySQL |
| B-01 | 同 S-01 | WebSSH IDOR(功能+安全双重) |
| S-18 | `server/application/services/script_service.py` | 调用 Agent 用 `http://` 明文,MITM 可篡改命令 |
---
## 9. 逐文件审计打勾(可执行代码 99 个)
### server/55
- [x] `server/__init__.py` 已审计 ✓ — 无问题
- [x] `server/main.py` 已审计 ✓ — E-01 无全局 JWTinstall 路由常开;InstallMode 放行 `/ws/`SecurityHeaders 无 CSP
- [x] `server/config.py` 已审计 ✓ — R-02 默认 `root:password` DATABASE_URL
- [x] `server/api/__init__.py` 已审计 ✓
- [x] `server/api/agent.py` 已审计 ✓ — **P0 S-02**P-01S-15S-12 timing
- [x] `server/api/assets.py` 已审计 ✓ — JWT 保护完整
- [x] `server/api/auth.py` 已审计 ✓ — S-09 totp/disable
- [x] `server/api/auth_jwt.py` 已审计 ✓ — PUBLIC_PREFIXES 未作全局中间件;逻辑正确
- [x] `server/api/dependencies.py` 已审计 ✓ — S-10 危险命令仅警告
- [x] `server/api/health.py` 已审计 ✓ — 公开合理
- [x] `server/api/install.py` 已审计 ✓ — **S-08**C-03 阻塞 subprocesslock 无 JWT
- [x] `server/api/schemas.py` 已审计 ✓ — Pydantic 校验良好
- [x] `server/api/scripts.py` 已审计 ✓ — JWT;凭据脱敏需对照响应
- [x] `server/api/search.py` 已审计 ✓ — LIKE 转义
- [x] `server/api/servers.py` 已审计 ✓ — B-03/P-02agent-key 返回明文 key(设计如此,需审计日志)
- [x] `server/api/settings.py` 已审计 ✓ — **S-03** reveal
- [x] `server/api/sync_v2.py` 已审计 ✓ — browse 用 shlex.quote
- [x] `server/api/webssh.py` 已审计 ✓ — **P0 S-01**B-02 未 close
- [x] `server/api/websocket.py` 已审计 ✓ — S-07P-03 多 worker 去重
- [x] `server/application/__init__.py` 已审计 ✓
- [x] `server/application/services/__init__.py` 已审计 ✓
- [x] `server/application/services/auth_service.py` 已审计 ✓ — R-07 旧 refresh
- [x] `server/application/services/script_service.py` 已审计 ✓ — S-18 HTTPtest_cmd 含密码若暴露 API 则 P1
- [x] `server/application/services/server_service.py` 已审计 ✓
- [x] `server/application/services/sync_engine_v2.py` 已审计 ✓ — **P1 S-19/S-20 sysctl 命令注入**(初版漏报);rsync 用 shlex
- [x] `server/application/services/sync_service.py` 已审计 ✓
- [x] `server/background/__init__.py` 已审计 ✓
- [x] `server/background/heartbeat_flush.py` 已审计 ✓ — 与 B-04 应对齐
- [x] `server/background/retry_runner.py` 已审计 ✓
- [x] `server/background/schedule_runner.py` 已审计 ✓
- [x] `server/background/self_monitor.py` 已审计 ✓
- [x] `server/domain/__init__.py` 已审计 ✓
- [x] `server/domain/models/__init__.py` 已审计 ✓ — 敏感列设计合理
- [x] `server/domain/repositories/__init__.py` 已审计 ✓
- [x] `server/infrastructure/__init__.py` 已审计 ✓
- [x] `server/infrastructure/database/__init__.py` 已审计 ✓
- [x] `server/infrastructure/database/admin_repo.py` 已审计 ✓ — 登录限速仅 username+ip
- [x] `server/infrastructure/database/audit_log_repo.py` 已审计 ✓
- [x] `server/infrastructure/database/crypto.py` 已审计 ✓ — R-01B-07
- [x] `server/infrastructure/database/db_credential_repo.py` 已审计 ✓
- [x] `server/infrastructure/database/migrations.py` 已审计 ✓
- [x] `server/infrastructure/database/password_preset_repo.py` 已审计 ✓
- [x] `server/infrastructure/database/platform_node_repo.py` 已审计 ✓
- [x] `server/infrastructure/database/push_schedule_repo.py` 已审计 ✓
- [x] `server/infrastructure/database/script_repo.py` 已审计 ✓
- [x] `server/infrastructure/database/server_repo.py` 已审计 ✓
- [x] `server/infrastructure/database/session.py` 已审计 ✓
- [x] `server/infrastructure/database/setting_repo.py` 已审计 ✓
- [x] `server/infrastructure/database/ssh_session_repo.py` 已审计 ✓
- [x] `server/infrastructure/database/sync_log_repo.py` 已审计 ✓
- [x] `server/infrastructure/redis/__init__.py` 已审计 ✓
- [x] `server/infrastructure/redis/client.py` 已审计 ✓
- [x] `server/infrastructure/ssh/__init__.py` 已审计 ✓
- [x] `server/infrastructure/ssh/asyncssh_pool.py` 已审计 ✓ — Info 压测
- [x] `server/infrastructure/telegram/__init__.py` 已审计 ✓
### web/23
- [x] `web/app/api.js` 已审计 ✓ — S-06U-01
- [x] `web/app/layout.js` 已审计 ✓ — 搜索 esc 良好
- [x] `web/app/login.html` 已审计 ✓ — S-06CDN 无 SRI
- [x] `web/app/index.html` 已审计 ✓ — S-07
- [x] `web/app/servers.html` 已审计 ✓ — **S-04**
- [x] `web/app/scripts.html` 已审计 ✓ — **S-05**
- [x] `web/app/terminal.html` 已审计 ✓ — S-07
- [x] `web/app/settings.html` 已审计 ✓ — U-03
- [x] `web/app/install.html` 已审计 ✓ — S-08 无认证 install fetch
- [x] `web/app/files.html` 已审计 ✓ — esc 一致
- [x] `web/app/push.html` 已审计 ✓ — esc 良好
- [x] `web/app/audit.html` 已审计 ✓
- [x] `web/app/assets.html` 已审计 ✓
- [x] `web/app/commands.html` 已审计 ✓ — S-11 onclick
- [x] `web/app/credentials.html` 已审计 ✓ — esc 良好
- [x] `web/app/schedules.html` 已审计 ✓
- [x] `web/app/retries.html` 已审计 ✓ — esc 良好
- [x] `web/favicon.svg` 已审计 ✓
- [x] `web/install.php` 已审计 ✓ — **S-16** 遗留安装
- [x] `web/agent/agent.py` 已审计 ✓ — **P0 S-14**
- [x] `web/agent/agent.sh` 已审计 ✓
- [x] `web/agent/install.sh` 已审计 ✓ — API key 进 config
- [x] `web/agent/config.example.json` 已审计 ✓
### scripts/8deploy/6tests+根(8)配置(5
- [x] `scripts/*.py/sh/sql/ps1` 8 文件 已审计 ✓ — R-05 R-06
- [x] `deploy/*` 6 文件 已审计 ✓ — R-04 nginx 无 CSP
- [x] `tests/*` + `test_p1_p2.py` 8 文件 已审计 ✓ — **P0 S-13** 硬编码密钥
- [x] `requirements.txt` `.env.example` `.gitea/workflows/ci-cd.yml` `.cursor/mcp.json` `PROJECT_HANDOVER.md` 已审计 ✓ — R-08CI 无 pip-audit
### docs/~350,批量)
- [x] `docs/**` 已审计 ✓ — 全文 rg 密钥模式,无新增 `msk-` 类硬编码(与 tests 除外)
---
## 1. 功能性缺陷(Bug
| ID | 严重度 | 位置 | 描述 | 修复建议 |
|----|--------|------|------|----------|
| B-01 | P1 | [`server/api/webssh.py`](server/api/webssh.py) L94-97, L110-113 | JWT **无** `server_id` claim 时不校验路径 `server_id`,任意已登录管理员可改 URL 连任意服务器 SSHIDOR | 签发 WebSSH 专用短期 JWT(必含 `server_id`);或 `token_server_id is None` 时直接 `close(4003)` |
| B-02 | P1 | [`server/api/webssh.py`](server/api/webssh.py) L110-117 | 服务器不存在或凭据缺失时 `return``websocket.close()`,客户端挂起超时 | 统一 `accept``close(code, reason)` |
| B-03 | P2 | [`server/api/servers.py`](server/api/servers.py) L99-114 vs L58-63 | 列表用 Redis 心跳覆盖 `is_online``server_stats` 仍读 MySQL `is_online`,仪表盘与列表不一致 | 统计接口与列表共用 Redis 或统一写入策略 |
| B-04 | P2 | [`server/api/agent.py`](server/api/agent.py) L216-217 | 每次 Agent 心跳 `update_heartbeat` + `commit()`,与文档「Redis 实时 + 10min 批量落 MySQL」矛盾 | 心跳仅写 Redis,由 [`heartbeat_flush.py`](server/background/heartbeat_flush.py) 批量刷库 |
| B-05 | P2 | [`server/api/agent.py`](server/api/agent.py) L259-260 | `TimeoutError``proc.kill()``await proc.wait()`,可能僵尸进程 | `kill()` + `await communicate()``asyncio.create_task` 清理 |
| B-06 | P3 | [`web/app/api.js`](web/app/api.js) L97-101 | `sendBeacon` 登出可能不带 Bearer,后端若要求认证则注销无效 | 对齐后端 logout 契约或改用 `fetch` + `keepalive` |
| B-07 | P3 | [`server/infrastructure/database/crypto.py`](server/infrastructure/database/crypto.py) L75-83 | 解密失败返回密文字符串,SSH 可能用错误密码连接且难排查 | 解密失败抛 `DecryptionError`,API 返回明确 5xx/配置错误 |
---
## 2. 安全漏洞
| ID | 严重度 | 位置 | 描述 | 修复建议 |
|----|--------|------|------|----------|
| S-01 | P0 | [`server/api/webssh.py`](server/api/webssh.py) + [`web/app/terminal.html`](web/app/terminal.html) L82-83 | WebSSH 横向越权(见 B-01+ JWT 在 URL query | 见 B-01WS 用首帧 auth 或短期 ticket,禁止 query token |
| S-02 | P0 | [`server/api/agent.py`](server/api/agent.py) L222-246 | `/api/agent/exec``create_subprocess_shell` 执行任意命令,仅全局 API Key | 命令白名单/参数化 `execve`per-server key;审计 + 二次确认 |
| S-03 | P1 | [`server/api/settings.py`](server/api/settings.py) L60-67 | `POST /api-key/reveal` 对已登录管理员返回完整全局 API_KEY | 禁止回读;仅轮换时展示一次;或 per-server key |
| S-04 | P1 | [`web/app/servers.html`](web/app/servers.html) L161-162 | `category``agent_version``esc()` 写入 `innerHTML` → 存储型 XSS | 所有 API 字符串字段统一 `esc()` 或 DOM API |
| S-05 | P1 | [`web/app/scripts.html`](web/app/scripts.html) L36 附近 | `category` 列表渲染未转义 | 同 S-04 |
| S-06 | P1 | [`web/app/api.js`](web/app/api.js) + [`login.html`](web/app/login.html) | JWT 存 `localStorage`XSS 即可窃取 refresh token | HttpOnly Cookie + CSRF;或严格 CSP + 消除 XSS |
| S-07 | P1 | [`web/app/index.html`](web/app/index.html) L208 | `/ws/alerts?token=` JWT 进 URL/日志/Referer | 同 S-01 WS 鉴权改造 |
| S-08 | P1 | [`server/api/install.py`](server/api/install.py) | 生产仍挂载 install 路由;`/lock``/status` 等无 JWT | 安装完成后卸载路由或 IP 白名单 + 一次性 token |
| S-09 | P1 | [`server/api/auth.py`](server/api/auth.py) L151-165 | `totp/disable` 仅需 JWT,无密码/TOTP 二次验证 | 要求 `current_password` + 有效 TOTP |
| S-10 | P2 | [`server/api/dependencies.py`](server/api/dependencies.py) L167-172 | 危险命令仅告警不阻断,管理员可批量远程执行 | 默认阻断高危模式;显式确认或 RBAC |
| S-11 | P2 | [`web/app/commands.html`](web/app/commands.html) L95,128 | `session_id` 嵌入 `onclick='...'` 未 JS 转义 | `data-*` + 事件委托 |
| S-12 | P2 | [`server/api/agent.py`](server/api/agent.py) L48-66 | API Key 用 `==` 比较,非常量时间 | `secrets.compare_digest` |
| S-13 | **P0** | [`tests/load_test.py`](tests/load_test.py) L18、[`tests/quick_load.py`](tests/quick_load.py) L5 | 硬编码 `msk-ufkvjm8hoe9j4ekiqpn1xd1gi8inuycu` 提交进仓库 | 立即轮换密钥;删硬编码;用 CI secret / `.env.test` |
| S-14 | **P0** | [`web/agent/agent.py`](web/agent/agent.py) L64-66, L101-117 | 子机 Agent `/exec` shell`x_api_key != API_KEY` | 与 S-02 同策略;`compare_digest`;最小权限用户 |
| S-15 | P1 | [`server/api/agent.py`](server/api/agent.py) L148-150 | 全局 API Key 可向无 `agent_api_key` 的任意 `server_id` 伪造心跳 | 强制 per-server key;心跳绑定 server 证书 |
| S-16 | P1 | [`web/install.php`](web/install.php) | 遗留 PHP 5 步安装,可写 `.env`/建表,与 FastAPI install 双轨 | 删除或 nginx deny;仅保留 install.html |
| S-17 | P1 | [`server/main.py`](server/main.py) + FastAPI 默认 | `/docs` `/openapi.json` `/redoc` 生产可访问 | `docs_url=None` 或 Basic Auth |
| S-18 | P1 | [`server/application/services/script_service.py`](server/application/services/script_service.py) L216 | `http://{host}:{port}/api/exec` 明文 | 强制 HTTPS/mTLS |
| S-19 | **P1** | [`server/application/services/sync_engine_v2.py`](server/application/services/sync_engine_v2.py) L234-236 | `sysctl -w {key}={safe_value}` / `echo {key}={safe_value}`:仅 `shlex.quote(value)`,含 `'` 的 value 可逃逸 shell 引号执行任意命令 | `shlex.quote(f"{key}={value}")` 或 argv 列表不经 shell |
| S-20 | **P1** | [`server/application/services/sync_engine_v2.py`](server/application/services/sync_engine_v2.py) L281-284 | `cp {backup_file} ... && sysctl --system``backup_file` 来自 `ls` 输出未转义 | 白名单路径 + `shlex.quote(backup_file)` |
**SQL 注入**: ORM/`select()` 为主,[`search.py`](server/api/search.py) 对 LIKE 通配符有转义,**未发现典型拼接 SQL 注入**。
---
## 3. 代码规范违规
| ID | 严重度 | 位置 | 描述 | 修复建议 |
|----|--------|------|------|----------|
| C-01 | P2 | 多处 `web/app/*.html` | 项目规则要求禁止未转义 `innerHTML`,但 [`servers.html`](web/app/servers.html)、[`scripts.html`](web/app/scripts.html) 等仍违规 | 抽公共 `renderTableRow()`CI 检查 `innerHTML``${` 组合 |
| C-02 | P2 | [`web/app/*.html`](web/app/) | 每页重复定义 `esc()`[`layout.js`](web/app/layout.js) 有 `_esc()` 未统一导出 | `api.js` 导出 `esc`/`escAttr`,页面禁止私有副本 |
| C-03 | P3 | [`server/api/install.py`](server/api/install.py) L283-284 | async 路由内同步 `subprocess.run` 阻塞事件循环 | `asyncio.to_thread` |
| C-04 | P3 | 前端 | 部分页面裸 `fetch`(如 [`install.html`](web/app/install.html))绕过 `apiFetch` | 安装向导可例外,但应集中错误处理 |
---
## 4. 安全风险(非直接漏洞)
| ID | 严重度 | 位置 | 描述 | 修复建议 |
|----|--------|------|------|----------|
| R-01 | P1 | [`server/infrastructure/database/crypto.py`](server/infrastructure/database/crypto.py) | 遗留 AES-CBC + SHA256(API_KEY) 兼容 PHP | 迁移期后移除 CBC;统一 Fernet |
| R-02 | P2 | [`server/config.py`](server/config.py) L40 附近 | `DATABASE_URL` 默认 `root:password` | 安装模式强制无默认密码;启动校验 |
| R-03 | P2 | 15× [`web/app/*.html`](web/app/) | jsDelivr 无 SRI,供应链篡改可全站 RCE | 自托管 + `integrity` + `crossorigin` |
| R-04 | P2 | [`deploy/nginx_https.conf`](deploy/nginx_https.conf) | 有 HSTS,无 CSP / X-Content-Type-Options | 逐步启用 CSP`script-src` 限制 |
| R-05 | P2 | [`scripts/sync_mysql_mcp_env.py`](scripts/sync_mysql_mcp_env.py) | 将 DB 密码写入 `.env``MYSQL_PASSWORD`,扩大泄露面 | MCP 独立只读/开发用户 |
| R-06 | P2 | [`scripts/grant_nexus_local.sql`](scripts/grant_nexus_local.sql) | `GRANT ALL` + `'%'` 仅适合本地;误用于生产风险高 | 文档标注「仅本地」;生产用最小权限 SQL |
| R-07 | P3 | [`server/application/services/auth_service.py`](server/application/services/auth_service.py) L147-153 | 旧 refresh token 格式仍兼容,重用检测弱于新格式 | 强制迁移、废弃旧 token |
| R-08 | P3 | [`requirements.txt`](requirements.txt) | 无 CI `pip-audit`/OSV 扫描 | GitHub Action 定期依赖审计 |
---
## 5. 性能问题
| ID | 严重度 | 位置 | 描述 | 修复建议 |
|----|--------|------|------|----------|
| P-01 | P1 | [`server/api/agent.py`](server/api/agent.py) L216-217 | 心跳直写 MySQL2000+ 服务器 × 60s | 见 B-04 |
| P-02 | P2 | [`server/api/servers.py`](server/api/servers.py) L58-63 | 列表页每服务器一次 `redis.hgetall`N+1 | Redis pipeline / `MGET` 批量 |
| P-03 | P2 | [`server/api/websocket.py`](server/api/websocket.py) L293-307 | 告警冷却 `_last_alert_time` 进程内 dict,多 worker 重复 Telegram | Redis SETNX + TTL 去重 |
| P-04 | P3 | [`server/api/install.py`](server/api/install.py) | 安装阶段同步 subprocess | 见 C-03 |
| P-05 | Info | [`server/infrastructure/ssh/asyncssh_pool.py`](server/infrastructure/ssh/asyncssh_pool.py) | 连接池设计合理;需压测 2000+ 并发 SSH 上限 | 配置 max pool + 监控指标 |
---
## 6. 可用性问题
| ID | 严重度 | 位置 | 描述 | 修复建议 |
|----|--------|------|------|----------|
| U-01 | P2 | [`web/app/api.js`](web/app/api.js) L35-37 | 空 `catch` 吞掉刷新/加载错误 | 统一 `toast('error', ...)` 或重试提示 |
| U-02 | P2 | [`server/infrastructure/database/crypto.py`](server/infrastructure/database/crypto.py) | 密钥错误时表现为 SSH 连接失败而非配置错误 | 见 B-07 |
| U-03 | P3 | [`web/app/settings.html`](web/app/settings.html) | 部分配置加载失败静默 | 与 U-01 统一错误 UX |
| U-04 | Info | 移动端 | [`layout.js`](web/app/layout.js) 已有 overlay;大表横向滚动需各页验证 | 关键页 E2E 截图验收 |
---
## 7. 可扩展性问题
| ID | 严重度 | 位置 | 描述 | 修复建议 |
|----|--------|------|------|----------|
| E-01 | P1 | 认证架构 | 无全局 JWT 中间件,每路由靠 `Depends(get_current_admin)`,新路由易漏 | `main.py` 默认拒绝 + 白名单(install/health/agent/auth/login |
| E-02 | P2 | Agent 协议 | 全局单 Key + shell exec,难以多租户/多环境隔离 | per-server API key + scoped capabilities |
| E-03 | P2 | 配置三写 | install → `.env` + `config.php` + MySQL `settings`,漂移风险 | 单一真相源 + 同步脚本(已有部分,需校验任务) |
| E-04 | P3 | 前端 | 15 页独立 Alpine 内联脚本,无组件复用 | 逐步抽 `components/` 或轻量模块(非必须大重构) |
---
## 8. 优化点(非错误)
| ID | 位置 | 建议 |
|----|------|------|
| O-01 | [`server/api/search.py`](server/api/search.py) | LIKE 转义已做,可补全文索引或 ES(2000+ 资产搜索) |
| O-02 | [`web/app/layout.js`](web/app/layout.js) | 全局搜索已统一 `_esc()`,推广为唯一转义入口 |
| O-03 | [`server/background/heartbeat_flush.py`](server/background/heartbeat_flush.py) | 与 B-04 对齐后,可加批量大小/退避指标 |
| O-04 | 测试 | [`tests/`](tests/) 覆盖有限,优先补 auth/webssh/agent 集成测试 |
| O-05 | 文档 | 已有 [`docs/project/mysql-mcp-setup.md`](docs/project/mysql-mcp-setup.md);安全项可增 ADRWebSSH JWT、Agent exec |
---
## 架构风险总览
```mermaid
flowchart TB
subgraph attack [高优先级攻击面]
XSS[XSS localStorage]
WebSSH[IDOR WebSSH]
AgentExec[Agent shell exec]
APIKey[Global API_KEY reveal]
end
XSS --> TokenTheft[窃取 JWT]
TokenTheft --> WebSSH
TokenTheft --> APIKey
APIKey --> AgentExec
```
---
## 修复优先级(建议执行顺序)
1. **立即(P0/P1 安全)**: S-01/B-01 WebSSH 绑定;S-04/S-05 XSSS-02 Agent exec 收敛;S-03 API Key reveal**S-19/S-20 sync_engine sysctl 注入**
2. **本周(一致性与性能)**: B-04/P-01 心跳写路径;B-03 在线统计;P-02 Redis pipeline
3. **迭代(纵深防御)**: S-06/S-07 会话与 WSR-03/R-04 CDN/CSPE-01 全局鉴权中间件
4. **文档/流程**: O-04 测试;R-08 依赖审计;每项修复写 [`docs/changelog/`](docs/changelog/)
---
## 与「WSL Node 20」计划的关系
| 计划项 | 状态 |
|--------|------|
| `wsl_install_node20.sh` 代理修复 | 已实现 |
| `MYSQL_READONLY=false` + `--writable` | 已实现 |
| `grant_nexus_local.sql` | 已实现 |
| MCP/Node 验证 | 已通过 |
**无需再跑该安装计划。** 若 MCP 仍红,仅需 Cursor Reload + 检查 MySQL 服务,而非重复改代码。
---
## 下一步(需你确认后执行)
若你确认本审计报告,可在 **Agent 模式** 按优先级分批修复(建议先做 P0/P1 安全项,每项单独 changelog)。你可指定:
- 只修安全(S-01 ~ S-12
- 或「P0/P1 全部」
- 或指定 ID 列表
我不会再自动重复 WSL Node20 实施循环。
---
## 10. 如何强制「逐行审查」(Phase 2 操作规程)
初版(Phase 1= **攻击面抽查 + 文件打勾**,不满足「逐行」承诺。要可复查、少漏报,必须单独开 **Phase 2**,并按下列规则验收。
### 10.1 你对助手说什么(复制即用)
切换到 **Agent 模式**(非 Plan),分批发:
```
执行审计计划 Phase 2:逐行复核。禁止攻击面抽查代替。
范围:server/**/*.py(本轮仅 server,完成后再 web)。
方法:
1. 用 rg 列出本目录每个文件的:命令拼接、subprocess、exec_ssh、WebSocket、SQL text(、硬编码密钥
2. 对每个命中行 Read 上下文 ±15 行,判定 safe / finding
3. 每个文件输出:
- [path] 行数 N | rg 命中 M 条 | 全部 closure 后才能标「逐行完成 ✓」
- findings 表:行号 | 严重度 | 描述
4. 禁止写「已审计」除非该文件 M=0 或每条命中都有 closure 记录
5. 每完成 5 个文件汇报一次,不要一次声称全库完成
从 server/application/services/sync_engine_v2.py 开始(已知漏报)。
```
修代码与审计**分两次会话**,避免混在一起。
### 10.2 「逐行」的可验收定义(不是读每一行散文)
| 必须做 | 说明 |
|--------|------|
| 全文 Read 或按 200 行分段 Read 至 EOF | 每个 `.py` 至少覆盖 100% 行号范围 |
| 全文件 rg 规则表(见 10.3) | 每个命中 = 一条待 closure 项 |
| 外部输入追调用链 | API → service → ssh/subprocess 到顶 |
| 产出 `docs/reports/audit-line-coverage-*.md` | 复查 AI 只认文件:行,不认「✓」 |
**不算逐行**:只 grep 几个关键词就标 ✓;只读 Top 15;子代理汇总不落行号。
### 10.3 每个 Python 文件必跑的 rg 规则(closure 前不得标完成)
```bash
# 在单个文件或目录执行,命中全部要 Read 上下文
rg -n "f[\"'].*\{.*\}.*(exec|run|shell|ssh|sysctl|curl|wget|sudo|bash|sh -c)" server/
rg -n "subprocess\.|create_subprocess_shell|os\.system|popen" server/
rg -n "exec_ssh_command|conn\.run\(|asyncssh" server/
rg -n "text\(|execute\(|raw\(" server/
rg -n "Depends\(get_current_admin\)|APIRouter|@router\.(get|post|put|delete|websocket)" server/api/
rg -n "HTTPException|401|403|API_KEY|compare_digest|secrets\." server/
```
前端另表:`innerHTML``localStorage``WebSocket.*token``eval(`
### 10.4 推荐拆分顺序(避免一次吹「全库完成」)
| 批次 | 路径 | 约行数 |
|------|------|--------|
| 2a | `server/api/*.py` | 高 |
| 2b | `server/application/services/*.py` | 高(含 sync_engine |
| 2c | `server/infrastructure/**/*.py` | 中 |
| 2d | `server/background/*.py` + `main.py` + `config.py` | 中 |
| 2e | `web/app/*.{html,js}` | 高 |
| 2f | `web/agent/*` + `scripts/*` + `tests/*` | 中 |
每批结束交付:`audit-phase-2{X}.md`(仅 findings + closure 表)。
### 10.5 你可加的 Cursor 规则(防止再抽查)
`.cursor/rules/audit-line-review.mdc` 中写:
- 安全审计任务:**禁止**在未列出 `文件:行号` 的情况下声称完成
- **禁止**使用「已审计 ✓」除非附带 rg 命中 closure 表
- 发现命令拼接必须引用 `shlex`/argv 或标为 finding
- 单次回复最多声称完成 **5 个文件** 的逐行审查
### 10.6 你怎么验收助手是否真的逐行
1. 抽查:打开 `sync_engine_v2.py`,看报告是否含 **234、236、284**S-19/S-20
2. 要求给 `wc -l` 与「本文件 Read 行范围 1-N」
3. 要求 `rg 命中数 = closure 数`,差 1 条即打回
4. 用另一只 AI 只喂 `audit-phase-*.md` + 源码,做交叉验证
### 10.7 Phase 2 todos(待你确认后执行)
| id | 内容 |
|----|------|
| phase2-api | `server/api/*.py` 全文 + rg closure |
| phase2-services | `server/application/services/*.py` 含 sync_engine 全行 |
| phase2-infra | `server/infrastructure/**` |
| phase2-web | `web/app` innerHTML + token |
| phase2-report | 合并为 `docs/reports/audit-line-coverage-2026-05-22.md` |
---
## 11. 逐行走读标准(SSOT — 唯一验收依据)
**独立文件(已落地)**
| 文件 | 用途 |
|------|------|
| [`docs/project/line-walk-audit-standard.md`](docs/project/line-walk-audit-standard.md) | 完整标准(推荐给人与复查 AI) |
| [`.cursor/rules/audit-line-review.mdc`](.cursor/rules/audit-line-review.mdc) | Cursor 编辑 `server/`/`web/` 时自动加载 |
本节是 Phase 2 的**正式标准**(与上表同步)。与 Phase 1(攻击面抽查)严格区分;未满足 **DoD** 的文件不得标记「逐行完成」。
### 11.1 术语
| 术语 | 定义 |
|------|------|
| **走读** | 对单文件按行号顺序阅读源码(可分段),并对规则命中点做上下文分析 |
| **触达** | 仅打开/ grep 过文件,未满足走读 DoD |
| **规则命中** | `rg` 规则表(11.4)在某行产生的匹配 |
| **Closure** | 对一条规则命中的判定记录:`SAFE``FINDING`(含行号、理由) |
| **DoD** | 单文件完成定义(11.6),全部满足才可标「逐行完成 ✓」 |
### 11.2 适用范围
| 包含 | 排除 |
|------|------|
| `server/**/*.py` | `docs/**` 仅做密钥扫描(11.8 |
| `web/app/*.{html,js}` | `.cursor/skills/**` |
| `web/agent/**` | 第三方 minified 库 |
| `scripts/**``deploy/**``tests/**` | 图片、字体、lock 文件 |
### 11.3 单文件走读流程(强制 8 步)
对清单中**每一个**源文件依次执行,不得跳步:
```
Step 1 登记:记录 path、语言、wc -l 行数 N
Step 2 全文覆盖:Read offset=1, limit=200 循环至 EOF(末段 offset 必须 ≥ N-5
Step 3 规则扫描:对该文件跑 11.4 中「本语言」全部规则,得到命中列表 H[]
Step 4 Closure:对 H[] 中每条命中 Read ±15 行,填写 11.5 表格一行
Step 5 入口梳理:若为 API/路由/WS,列出对外入口表(方法、路径、鉴权方式)
Step 6 数据流:标出「外部输入 → 危险 sink」(SQL/shell/文件/innerHTML/出站请求)
Step 7 八类核对:按 11.7 勾选本文件涉及的类别(无则勾 NONE)
Step 8 DoD 检查:满足 11.6 后写入报告,标记「逐行完成 ✓」
```
**单会话上限**:最多完成 **5 个文件** 的 Step 18,防止敷衍。
### 11.4 规则表(必须 100% Closure
#### Python`server/`、`scripts/`、`tests/`、`web/agent/*.py`
| 规则 ID | rg 模式(示意) | 审查要点 |
|---------|----------------|----------|
| PY-01 | `f["'].*\{.*\}.*` 且行内含 exec/run/shell/ssh/sysctl/curl/sudo/bash | 是否经 shell?变量是否 shlex.quote **整参数** |
| PY-02 | `subprocess\.|create_subprocess_shell|os\.system|popen` | shell=True?命令来源? |
| PY-03 | `exec_ssh_command|conn\.run\(` | asyncssh 是否默认 shell?拼接方式? |
| PY-04 | `text\(|execute\(|raw\(` | SQL 是否拼接用户输入? |
| PY-05 | `@router\.|APIRouter|websocket` | 是否有 `Depends(get_current_admin)` 或等效? |
| PY-06 | `API_KEY|SECRET|password|token` 字面量 | 是否硬编码密钥? |
| PY-07 | `pickle|yaml\.load\(|eval\(|exec\(` | 反序列化 / 动态执行 |
| PY-08 | `==.*[Kk]ey|!=.*[Kk]ey` | API Key 比较是否 timing-safe |
| PY-09 | `except.*:.*pass|except Exception` | 是否吞掉安全相关异常 |
| PY-10 | `open\(|Path\(.*write|unlink` | 路径遍历、任意写 |
#### 前端(`web/app/*.{html,js}`
| 规则 ID | rg 模式 | 审查要点 |
|---------|---------|----------|
| FE-01 | `innerHTML|outerHTML|insertAdjacentHTML` | 每个 `${}` 是否 esc / textContent |
| FE-02 | `localStorage|sessionStorage` | 是否存 JWT/密钥 |
| FE-03 | `WebSocket.*token|\.token=` | Token 是否在 URL |
| FE-04 | `fetch\(|apiFetch` | 是否绕过统一鉴权/错误处理 |
| FE-05 | `onclick=.*\$\{|javascript:` | 属性上下文注入 |
| FE-06 | `cdn\.|script src=` | SRI、供应链 |
#### Shell / SQL / Deploy
| 规则 ID | 范围 | 要点 |
|---------|------|------|
| SH-01 | `*.sh` | 未引用变量、curl 明文密钥 |
| DP-01 | `deploy/nginx*.conf` | CSP、TLS、限流 |
| SQL-01 | `*.sql` | GRANT 过大、'%' host |
**无命中**:在 Closure 表写 `H=0(规则表零命中)`,仍须完成 Step 2 全文 Read。
### 11.5 Closure 记录格式(每条命中一行)
```markdown
| 文件 | 行号 | 规则 | 判定 | 严重度 | 理由(≤1句) |
|------|------|------|------|--------|--------------|
| sync_engine_v2.py | 236 | PY-01 | FINDING | P1 | sysctl -w 仅 quote value' 可断 shell |
| sync_engine_v2.py | 95 | PY-01 | SAFE | — | rsync 路径双端 shlex.quote |
```
- **SAFE** 必须写清依据(如「整段 argv」「Pydantic 校验」「仅常量」)
- **FINDING** 必须对应 11.7 八类之一,并给修复方向(≤1 句)
### 11.6 单文件 DoD(全部打勾才算完成)
- [ ] Step 2Read 覆盖行号 `1 .. N`(报告注明 N 与最后 Read 的 offset/limit
- [ ] Step 34`len(H) == Closure 行数`(含 SAFE + FINDING
- [ ] Step 5:有对外入口则必有「入口表」;纯 `__init__.py` 可写 N/A
- [ ] Step 6:已标外部输入与 sink,或写「无外部输入」
- [ ] Step 7:八类核对已勾
- [ ] 报告含「逐行完成 ✓」且**无** Phase 1 式笼统描述
### 11.7 问题八类与严重度(与 KPI 对齐)
| 类别 | 代号 | P0 示例 | P1 示例 | P2 示例 |
|------|------|---------|---------|---------|
| 功能缺陷 | BUG | — | 统计与列表数据源不一致 | 空 catch 吞错误 |
| 安全漏洞 | VULN | 未授权 RCE、IDOR 拿 Shell | XSS、密钥回读、命令注入 | CSRF 缺失 |
| 规范违规 | STYLE | — | 违反项目 esc 规则 | 重复 esc 实现 |
| 安全风险 | RISK | 仓库内生产 API Key | 明文 HTTP 调 Agent、弱 crypto | 无 SRI |
| 性能 | PERF | — | 心跳直写 MySQL、Redis N+1 | 阻塞 subprocess |
| 可用性 | UX | — | — | 加载失败无提示 |
| 可扩展性 | EXT | — | 无全局鉴权中间件 | 配置三写漂移 |
| 优化点 | OPT | — | — | 可合并重复逻辑 |
**KPI(复查用)**P0 漏报 -100 / P1 漏报 -50;每条 FINDING 必须 `文件:行号` 可定位。
### 11.8 批次交付物模板
每个批次一个文件:`docs/reports/audit-phase-2{X}-line-walk.md`
```markdown
# Phase 2{X} 逐行走读 — {目录}
## 文件清单
| 文件 | 行数 N | 命中 H | Closure | 状态 |
|------|--------|--------|---------|------|
## Findings(仅 FINDING
| ID | 文件:行 | 类 | 度 | 描述 | 修复建议 |
## Closure 全表(含 SAFE
11.5 格式)
## 入口表(api 批次必填)
| 方法 | 路径 | 鉴权 |
## 走读签字
- 范围:...
- 完成文件数 / 计划文件数
- 未含「攻击面 Top N」字样:是/否
```
### 11.9 禁止行为(违反即打回)
1. 未列 `文件:行号` 的 FINDING
2. `rg 命中数 ≠ Closure 数` 仍标完成
3. 用子代理「Top 15」代替 Closure 全表
4. 只写「已审计 ✓」无 Step 2 行号范围
5. 单回复声称完成整库 `server/` 或全部 `web/`
6. 审计与修代码同一任务混合(除非用户明确要求)
### 11.10 与 Phase 1 的关系
| Phase 1 | Phase 2 |
|---------|---------|
| 攻击面抽查、快速风险图 | 逐行走读、可复查 closure |
| 可发现「显眼」P0 | 可发现「单文件局部」拼接类 P1(如 sysctl) |
| 报告在计划 §0–§8 | 报告在 `docs/reports/audit-phase-2*.md` |
**原则**Phase 1 的 FINDING 在 Phase 2 须逐条 **VERIFY**(行号仍成立或降级/关闭);Phase 2 可新增 FINDING,不得删减已确认行号而不说明。
+44
View File
@@ -0,0 +1,44 @@
---
description: 减少盲目搜文件、控制 Read 次数与 token 消耗
alwaysApply: true
---
# Agent 探索约束(省 token
## 开始前
1. 读根目录 `AGENTS.md` 的「代码地图」与「问题 → 文件」表
2. 架构/API 全览只读 `docs/project/nexus-functional-development-guide.md`(或用户指定的 §),禁止为找结构全库 Grep
3. 用户已给出路径/文件名时,只读该路径,禁止扩大范围
## 搜索顺序
```
AGENTS.md / 功能指南 § → Grep(带 path)→ Read(≤3 个文件)→ 仍不够再扩一圈
```
- **禁止**无 `path` 的全仓库 `Grep` / `Glob`(除非用户明确要求全库)
- **Grep 优先于 Read**Glob 只用于定位文件名,不逐个 Read 全部匹配
- 同一任务 **Read 不超过 5 个文件**;找到答案后立即停止
- 不要重复 Read 已在本对话出现过的文件
## 分层定位(禁止跨层乱搜)
| 层 | 路径 |
|----|------|
| HTTP 路由 | `server/api/<module>.py` |
| 业务 | `server/application/services/` |
| 数据访问 | `server/infrastructure/` |
| 模型 | `server/domain/models/` |
| 前端页 | `frontend/src/pages/` |
| 前端路由 | `frontend/src/router/index.ts` |
| 前端 API 客户端 | `frontend/src/api/` |
真实前端源码在 `frontend/src/`,不是 `web/app/*.html`(那是构建产物)。
## 文档索引
- 功能 SSOT`docs/project/nexus-functional-development-guide.md`
- 文档导航:`docs/README.md`
- 本地路径/MCP`docs/project/linux-dev-paths.md`
- 运维(连机/exec):用户 Skill `Nexus平台` 或 `docs/project/nexus-1panel-operations-knowledge.md`
+66
View File
@@ -0,0 +1,66 @@
---
description: 安全/质量逐行走读审计 — Phase 2 closure 流程;禁止攻击面抽查冒充全量审计
globs:
- server/**
- web/app/**
- web/agent/**
- scripts/**
- deploy/**
- tests/**
---
# 逐行走读审计规则(Phase 2)
完整标准见:`standards/line-walk-audit-standard-v2.md`(权威)· Nexus SSOT`docs/project/line-walk-audit-standard.md` · 转接索引:`docs/project/standards-transfer-package.md`
## 何时启用
用户要求「全量审计」「逐行审查」「走读」「Phase 2」「安全审计」时,**必须**遵守本规则,不得用攻击面 Top N 或子代理汇总代替。
## 强制流程(每文件 8 步)
1. 登记 `path`、语言、行数 N`wc -l`
2. **全文 Read** 至 EOF(可每 200 行分段;末段须覆盖 N)
3. 对该文件跑 `docs/project/line-walk-audit-standard.md` §4 全部适用规则 → 命中列表 H
4. 对 H **每一条** Read ±15 行,填 ClosureSAFE / FINDING + 行号 + 理由)
5. API/WS 文件:写入口表(方法、路径、鉴权)
6. 标外部输入 → sinkSQL / shell / 文件 / innerHTML / 出站 HTTP
7. 八类归类:BUG / VULN / STYLE / RISK / PERF / UX / EXT / OPT
8. 满足 DoD 后标「逐行完成 ✓」
**单会话上限**:最多 **5 个文件** 完成 8 步。
## DoD(缺一不可)
- `len(H) == Closure 行数`H=0 须写明「规则零命中」)
- Read 覆盖 `1..N`(报告写明 N 与 Read 范围)
- 每条 FINDING 含 **`文件:行号`**
- 交付 `docs/reports/audit-phase-2*-line-walk.md`(含 Closure **全表**,含 SAFE
## 命令拼接(高频漏报)
- `f"{key}={shlex.quote(value)}"` **不等于**安全:须 `shlex.quote(f"{key}={value}")` 或 argv 不经 shell
- `ls`/`find` 输出拼进 `cp`/`rm` 须 **白名单 + shlex.quote**
- `exec_ssh_command` / `conn.run(command)` 默认经远程 shell
## 禁止
- 无行号的 FINDING
- 「已审计 ✓」无 Closure 表
- 单回复声称整库 `server/` 或全部 `web/` 完成
- 子代理只出 Top 15 不落行号
- 同一任务混审计与改代码(除非用户明确要求)
## 严重度(复查 KPI
- **P0**:未授权 RCE、IDOR Shell、仓库内生产密钥等
- **P1**:需认证后的命令注入、XSS、密钥回读等
- P0 漏报 -100 / P1 漏报 -50(用户复查约定)
## 报告路径
| 用途 | 路径 |
|------|------|
| 标准 SSOT | `docs/project/line-walk-audit-standard.md` |
| 批次产出 | `docs/reports/audit-phase-2{a-f}-line-walk.md` |
| Phase 1 参考 | `.cursor/plans/nexus_全面代码审计_a33e6fc2.plan.md` §0–§8 |
+38
View File
@@ -0,0 +1,38 @@
---
description: Nexus 前端规范 — Tailwind v4 + Alpine.jsapi.js JWT;禁止 XSS
globs: web/app/**
alwaysApply: false
---
# Nexus 前端 (Tailwind + Alpine.js)
## 页面结构
- 业务页先引入:`/app/api.js` → `/app/layout.js`(侧栏、搜索、移动端 overlay)
- 15 个 HTML 页面同级放在 `web/app/`,保持导航一致
## API 调用
- 一律 `apiFetch()` / `apiHeadersJSON()`,禁止手写裸 `fetch` 漏 Authorization
- Token 存 `localStorage``access_token`、`refresh_token`、`token_expires`
- 到期前 2 分钟自动 refresh401 重试一次后 `_logout()`
- 8 小时无活动会话超时(与后端 JWT `updated_at` 对齐)
## WebSocket
- 仪表盘 `/ws/alerts?token=...`JWT 查询参数,ADR-011
- 收到 `alert` / `recovery` 刷新 stats,勿阻塞 UI 线程
## 安全(XSS
- 禁止 `${userInput}` 直接插入 `innerHTML` 或 Alpine `x-html`
- 用户/服务器名等用 `textContent` 或 Alpine 文本绑定
## WebSSH
- `terminal.html`xterm.js + Koko 协议;resize 随窗口
- 关闭码 4003 = 凭据/授权失败
## 验收
改 UI 后浏览器实机验证:Console 无报错、Network 无 404/500、登录→核心流程可走通。
+20
View File
@@ -0,0 +1,20 @@
---
description: 远程 Linux 路径必须用 POSIX,禁止 os.path 拼接
globs: server/**/*.py
alwaysApply: false
---
# 远程路径 = POSIX only
## 规则
- **SSH / SFTP / rsync 目标路径**(含 `target_path`、`remote_path`、命令里的文件路径):
- 使用 `server.utils.posix_paths``normalize_remote_abs_path`、`remote_join`、`posix_join` 等)
- **禁止** `os.path.join` / `dirname` / `basename` / `normpath` 处理这类字符串
- **Nexus 本机真实 I/O**`open`、`os.walk`、`os.scandir`):可用 `os.path`,逻辑拼接仍优先 `posix_join`
- **仓库本地路径**`Path(__file__)`、MCP `DEPLOY_DIR`):可用 `os.path` / `pathlib`
## 参考
- 实现:`server/utils/posix_paths.py`
- 审计:`docs/audit/2026-06-01-posix-path-batch-review.md`
+46
View File
@@ -0,0 +1,46 @@
---
description: Nexus Python/FastAPI 后端规范 — 异步、Pydantic、Repository、UTC 时间
globs: server/**/*.py
alwaysApply: false
---
# Nexus Python 后端
## 依赖版本(勿随意降级)
FastAPI 0.115 · SQLAlchemy 2.0 async · aiomysql · Pydantic v2 · asyncssh 2.17 · redis 5.x · PyJWT 2.10
## API 路由
- 请求/响应模型定义在 `server/api/schemas.py`
- 业务 API 必须 `Depends(get_current_admin)`Agent 用 `X-API-Key`install 路由无 JWT
- 返回结构化错误,禁止裸 `except: pass`
## 数据库
- 模型:`server/domain/models/__init__.py`14 表)
- 访问:`*_repo.py`,分页在 Repository 层 `offset/limit/count`
- 时间戳:`datetime.now(timezone.utc)`,禁止 naive datetime
- 连接池:默认 pool=160、overflow=120(安装向导按 MySQL `max_connections` 计算)
## Redis
- 客户端:`server/infrastructure/redis/client.py`,启动时强校验(ADR-009
- 心跳 key`heartbeat:{server_id}` TTL 600;告警 key`alerts:{server_id}` TTL 3600
- 跨 worker 广播:Redis Pub/Sub `nexus:alerts`
## SSH
- 仅用 **asyncssh**(已删除 paramiko
- 连接池引用计数:`acquire` / `release`,空闲 5min 清理
- 远程命令参数必须 `shlex.quote()`
## 凭据加密
- Fernet 存储 `Server.password` / `ssh_key_private`
- API 响应:`password_set` 布尔,禁止返回明文密码
## 测试
- `pytest` + `tests/conftest.py`;改 API/Service 须跑相关测试
- 新 endpoint 至少覆盖:认证失败、校验失败、成功路径
+44
View File
@@ -0,0 +1,44 @@
---
description: Nexus 安全规范 — JWT/TOTP/API Key/Fernet/CORSMCP MySQL 只读
globs: server/api/**,server/application/**,server/infrastructure/database/**,web/app/**
alwaysApply: false
---
# Nexus 安全
## 认证矩阵
| 调用方 | 机制 |
|--------|------|
| 管理端 API | JWT Bearer + 可选 TOTP |
| Agent | `X-API-Key`(仅 per-server `agent_api_key`;经 `POST /api/servers/{id}/agent-key` 生成,BL-06 起无 global 回退) |
| WebSocket | JWT query param |
| 安装向导 | 无 JWT(仅安装模式可用) |
## 登录防暴破
- `auth_service.py`:失败计数 → 15min 锁定 → HTTP 429
- 刷新令牌重用检测:旧 refresh 作废、`token_version` 递增
## 密钥(禁改 / 禁提交)
- `.env` 永不入 git`SECRET_KEY` / `API_KEY` / `ENCRYPTION_KEY` / `DATABASE_URL` 不可从 settings 表覆盖
- UI 不展示可改的 `API_KEY`
## 运维信任模型
- 鉴权管理员即信任用户,不做 RBAC
- 必须防御:SQL/命令注入、XSS、凭据泄露
- Shell`shlex.quote()`DELETE/UPDATE 须有 WHEREMCP 同样)
## Cursor MySQL MCP
- 使用 `@yclenove/mysql-mcp-server`**默认 `MYSQL_READONLY=true`**
- `MYSQL_DATABASE_ALLOWLIST=nexus`(或实际库名)
- 禁止在 MCP `env` 或 git 中提交生产密码;连接信息仅写在本地 `.env`
- 查库优先 MCP `query` / `describe_table`,避免手写破坏性 SQL
## 审计
- 所有 CUD 写 `audit_logs`(含 `ip_address`
- WebSSH 会话与 `command_logs` 可追溯
+45
View File
@@ -0,0 +1,45 @@
---
alwaysApply: true
---
# Nexus 6.0 技术栈
## 架构分层(禁止跨层直连)
```
web/app/*.html → server/api/*.py → application/services → infrastructure/* → domain/models
```
- API 层:路由 + Pydantic 校验 + `Depends(get_current_admin)`,不写 SQL
- Service 层:业务编排,通过 Repository 访问数据
- InfrastructureDB/Redis/SSH/Telegram 实现细节
- DomainSQLAlchemy ORM 模型,无 FastAPI 依赖
## 启动模式
- 无 `.env` → **安装模式**:仅 `/api/install/` + `/app/install.html`
- 有 `.env` → 正常模式:`SECRET_KEY` / `API_KEY` / `ENCRYPTION_KEY` 缺一不可
## 配置三写(安装向导)
`web/data/config.php` + `.env``NEXUS_` 前缀)+ MySQL `settings` 表。
**不可从 DB 覆盖**`SECRET_KEY`、`API_KEY`、`ENCRYPTION_KEY`、`DATABASE_URL`。
## 数据流
Agent(60s) → Redis 实时 → 前端直读 → 10min → MySQL 历史。告警:WebSocket + Telegram。
## 后台任务(仅 primary worker
`heartbeat_flush`(10min) · `self_monitor`(30s) · `schedule_runner`(60s) · `retry_runner`(5min)
## HTTP / DB 会话
- 普通 API`DbSessionMiddleware` → `request.state.db`,禁止在 factory 里 `AsyncSessionLocal()` 后不 close
- WebSocket / `/api/install/`:不走 DB 中间件
## 相关规则文件
- `nexus-python-backend.mdc` — `server/**/*.py`
- `nexus-frontend.mdc` — `web/app/**`
- `nexus-security.mdc` — 安全与凭据
- `perfect-implementation.mdc` — 完美实现与文档纪律
+29
View File
@@ -0,0 +1,29 @@
---
description: 单 Agent 对话——禁止部门/Skill 分派,用户只与当前助手直接协作
alwaysApply: true
---
# 单 Agent 对话(强制)
Nexus 只保留**你 ↔ 当前 Cursor 助手**这一条协作链。已删除:`docs/skills/`、`docs/team/`、`.cursor/skills/`;本机 `~/.claude/skills/` 中凡带 `group: 管理组|工程部|测试部|产品部|设计部` 的目录也已移除。
## 禁止
- 扮演或「转交」管理组、工程部、测试部、产品部、设计部及项目经理/总监/`*-department` 等任何虚拟角色。
- 未经用户明确要求时,**不要** `Read` 或按 `available_skills` 去加载 Skill 文件(含全局 `~/.claude/skills/`、`~/.cursor/skills-cursor/`)。
- 用 Task/子代理模拟「开会、分派、部门审批」;子代理仅用于用户明确要求的并行搜代码/跑命令等**技术任务**,且不得冠部门名。
- 在回复里写「已转交 XX 部」「请以产品经理身份」等话术。
## 允许
- 直接:澄清需求 → 设计/技术文档(`docs/design/`)→ 改代码 → 跑 `tests/` / 门控 → changelog。
- 依据:`standards/`、`.cursor/rules/`(含 `perfect-implementation`、`nexus-*`、`audit-line-review`)。
## 测试(强制)
- **所有测试与「是否修好」由本 Agent 执行并留证据**(命令、生产 URL、浏览器、`docs/reports/*-verification.md`)。
- **不得**把未验证项推给用户代测;用户只做 Agent 交付后的**终验**(见 `docs/reports/2026-06-01-final-acceptance-checklist.md`)。
## 默认协作
用户说什么就做什么;需要方案时在同一对话里说明取舍,不引入额外「智能体」层。
+62
View File
@@ -0,0 +1,62 @@
---
description: 完美实现原则 — 禁止降级/妥协/技术债;设计文档与技术文档先行;每次修改写 changelog
alwaysApply: true
---
# 完美实现与文档纪律
## 实现标准(强制)
**所有设计和实现都必须是完美实现。**
- ❌ **不允许为了实现而实现** — 每一行代码、每一个接口必须有明确目的,达到项目既定的安全、架构与质量标准
- ❌ **不允许降级实现** — 不得以「先用着」「以后再改」为由降低安全、性能、可维护性或用户体验
- ❌ **不允许妥协实现** — 发现问题必须根治,不得用绕过、静默吞错、硬编码掩盖代替修复
- ❌ **不允许留技术债** — 不得留下 TODO/FIXME/「临时方案」延后处理;当场能修必须当场修
- ✅ **无法完美实现时必须停下** — 与用户沟通并确认替代方案后,才能换方案;不得擅自从权或自行降级
## 文档纪律(强制)
### 设计文档
- 所有**新功能、架构变更、非平凡重构**必须先有设计文档,再写代码
- 设计文档路径:`docs/design/specs/YYYY-MM-DD-<topic>-design.md`
- 设计文档至少包含:背景与目标、方案对比、选定方案及理由、接口/数据模型、安全与性能约束、验收标准
### 技术文档(开发前)
- 开发前输出技术文档(实施说明),路径:`docs/design/plans/YYYY-MM-DD-<topic>.md` 或 `docs/reports/<step>/engineering-report.md`
- 技术文档至少包含:涉及文件清单、实现步骤、依赖与配置变更、测试要点、回滚方式
- 项目里**已有模块但缺文档的,在改动时一并补全**,不得只改代码不留档
### Changelog(每次修改)
- **每一次**有意义的代码或配置修改,必须在 `docs/changelog/` 新增或更新记录
- 命名:`YYYY-MM-DD-<简短主题>.md`
- 每条记录至少包含:日期、变更摘要、动机、涉及文件、是否需迁移/重启、验证方式
- 禁止把 changelog 写在 commit message 里代替项目文档
## 执行顺序
```
需求确认 → 设计文档 → 技术文档 → 实现 → 测试验证 → changelog →(必要时)更新 CLAUDE.md/AGENTS.md
```
## 禁止示例
```python
# ❌ 技术债
# TODO: fix later
pass # workaround
# ❌ 降级
except Exception:
return None # 静默失败
# ❌ 无文档直接大改
# (新增整模块却无任何 docs/design 或 docs/changelog 记录)
```
## 允许的唯一例外
经用户**明确书面确认**后,方可采用非完美方案;须在 changelog 中记录例外原因、影响范围与后续补齐计划。
+7
View File
@@ -0,0 +1,7 @@
{
"plugins": {
"compound-engineering": {
"enabled": true
}
}
}