Files
lislgosms/docs/code-quality-remediation-result-20260828.md
T

122 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CMPP 平台代码质量整改执行结果
- 执行日期:2026-08-28
- 对应方案:`docs/code-quality-remediation-plan-20260828.md`
- 本地分支:`main`
- 测试环境:`100.93.204.60`
- 测试环境最终业务代码:`fc4a6a7afcfc3c1c11d1e353f46d5b724f48a39e`
- 环境边界:本轮只修改和部署测试环境;未访问、覆盖、回退或部署预生产
## 1. 结论
本轮已经关闭两个 P0,并完成可信租户上下文、强密码哈希、旧哈希透明迁移、Redis 验证码/锁定、重点写接口 DTO 校验、依赖升级、覆盖率和包体门禁、路由分包、ECharts 按需加载、数据库配置 fail closed、mock 类型解耦及仓库缓存治理。最终代码已部署到测试环境,95 项迁移全部一致,11 项相关服务 activeAPI/Gateway 健康,Redis 回执结果 Stream `pending=0、lag=0`,发布后核心服务 error 级日志为 0。
本轮没有把“完整治理方案”中的长期重构和全业务链复验冒充已完成:两个超大 SendChain Service 未做高风险机械拆分;未重新发送短信、执行压力测试或改动 MinIO 业务对象;浏览器完成登录页真实渲染和控制台检查,但没有在本轮自动输入测试密码和验证码完成登录后页面验收。这些项目不影响本次 P0 关闭和已发布代码运行,但仍属于后续专项验收范围。
## 2. 已实施变更
### 2.1 租户隔离
- 会话校验从数据库读取用户 `tenantId`,客户端请求只使用服务端可信上下文。
- 客户端缺少租户时直接拒绝;`x-tenant-id` 仅做一致性检查,不再决定授权范围。
- 请求头伪造租户返回 `403 / CLIENT_TENANT_MISMATCH` 并写安全事件。
- 客户端认证、删除治理、Open API、风险审核、发送链、运营日志、短信配置、账务及用户接口均改用可信租户。
- 客户端创建请求中的 `tenantId``operatorId``createdById` 不再作为可信身份字段;服务端覆盖或严格拒绝。
- 应用参数、状态、密钥等按资源 ID 的操作补充租户复合范围。
测试环境双租户真实 API 结果:
| 场景 | 结果 |
|---|---|
| 当前租户且不带租户请求头 | 通过 |
| 当前租户且请求头一致 | 通过 |
| 请求头伪造其他租户 | `403 CLIENT_TENANT_MISMATCH` |
| 请求体提交其他租户 ID | 被服务端强制归属当前租户 |
| 访问其他租户应用资源 ID | `404` |
### 2.2 密码哈希
- 使用 Node 标准库 scrypt,格式包含版本、`N=32768/r=8/p=1`、独立 16 字节随机 salt 和摘要。
- 验证使用恒定时间比较,并限制可接受参数范围。
- 创建用户、改密、重置密码、生产管理员维护工具和真实环境 smoke 工具只写强哈希。
- 旧 64 位 SHA-256 继续只读兼容;正确密码登录后使用条件更新透明升级,错误密码不迁移。
- 首次发布使用最长 2 小时的受限兼容窗口,建立第二份恢复资产后移除兼容写入环境变量并重启 API;最终配置和 API 进程均无 `PASSWORD_HASH_LEGACY_*`
- 测试环境创建一次性旧 SHA-256 测试账号,经真实验证码和客户端登录接口成功迁移为 scrypt,随后硬删除;收尾 `@integration.invalid` 活跃账号为 0。
- 测试环境仍有 4 个未登录的历史 SHA-256 账号,按透明迁移策略保留;本次没有改写未知用户密码。
### 2.3 登录状态与输入校验
- 验证码迁移到 Redis TTL,并用 `GETDEL` 原子消费。
- 匿名失败计数使用哈希化 key 和 Lua 原子 `INCR/EXPIRE`,连续 5 次失败锁定 24 小时,成功登录清理计数。
- Redis 不可用时 fail closed,不再退回进程内无界 Map。
- 认证和高风险客户端写接口启用 `transform / whitelist / forbidNonWhitelisted` 严格校验;额外身份字段会被拒绝。
### 2.4 依赖、性能和工程门禁
- `react-router-dom/react-router` 升级到 `7.18.2`NanoID 解析固定到 `3.3.18`;根生产依赖审计为 0。
- 全部页面改为 `React.lazy` 路由级加载,新增统一加载/失败/重试边界。
- ECharts 改为 core、chart、component、canvas renderer 按需注册。
- 初始入口 JavaScript gzip 从审计基线约 626 KiB 降至约 107.38 KiB;图表异步块约 181.64 KiB,按首轮实测将单块预算校准为 190 KiB,入口预算 250 KiB。
- API 覆盖率门槛建立为 statements 59%、branches 50%、functions 60%、lines 62%;本轮实际分别为 69.77%、53.47%、71.08%、73.05%。
- 新增门禁禁止生产页面导入 mock、禁止客户端可信租户回退、禁止密码写工具使用裸 SHA-256、检查依赖版本和 bundle budget。
- 生产/测试环境缺少 `DATABASE_URL` 时 fail closed;开发和测试模式才允许显式本地默认值。
- `auditColumns` 业务类型移出 mock;停止跟踪 `api/tsconfig.build.tsbuildinfo` 并补充缓存/产物忽略规则。
- `AdminDownstreamDeliveriesPage` 的展示和详情辅助逻辑已拆出。两个 SendChain 超大 Service 本轮不做无业务收益的高风险拆分,保留到专门重构任务。
## 3. 自动化验证
| 门禁 | 结果 |
|---|---|
| API Jest | 47/47 套、542/542 项通过 |
| API 覆盖率 | 69.77 / 53.47 / 71.08 / 73.05,全部超过门槛 |
| API TypeScript 正式构建 | 通过 |
| 前端 TypeScript | 通过 |
| Vite 正式构建 | 通过 |
| bundle budget | 通过 |
| Gateway `go test ./... -count=1` | 通过 |
| Gateway `go vet ./...` | 通过 |
| Prisma validate / migration | 通过,测试环境 95/95 |
| Gateway 队列契约 | 5/5 通过 |
| 代码质量/安全部署门禁 | 通过 |
| 根生产依赖审计 | 0 vulnerabilities |
| `git diff --check` | 通过 |
API 子项目安装仍报告其依赖树中的 9 项公告(2 moderate、7 high),但原审计明确列出的 React Router 和 NanoID 两项已关闭,现有依赖缓解门禁通过。本轮没有为了追求审计数字盲目升级可能破坏 Prisma/Nest 兼容性的主版本;剩余公告应另行建立升级兼容矩阵。
## 4. 测试环境发布与恢复资产
密码格式具有单向迁移属性,因此采用两阶段发布:先发布能同时读取新旧格式且只允许受限兼容写入的版本,确认新格式写路径后重新建立恢复资产,再关闭兼容写入并发布最终版本。
| 恢复点 | 用途 |
|---|---|
| `/opt/cmpp-platform-backups/code-quality-before-d85ff859-20260828T035007Z` | 首次兼容发布前,旧部署 `0b843702...` |
| `/opt/cmpp-platform-backups/code-quality-compatible-d85ff859-20260828T035426Z` | 兼容版本稳定后 |
| `/opt/cmpp-platform-backups/code-quality-before-fc4a6a7-20260828T040150Z` | 最终强哈希发布前 |
三份恢复点均包含 PostgreSQL custom dump、运行目录、配置、Redis RDB、MinIO 数据、服务/监听/Stream 状态、回退说明和 `SHA256SUMS``pg_restore --list`、tar 目录读取、Redis RDB 和摘要校验通过。最终发布包:
- `outputs/cmpp-code-quality-fc4a6a7-20260828-120252.tar.gz`
- SHA-256`7b025b31d2964e290b20104f83532ef4613929574bc3d790d584e433273841be`
- 955 个归档条目
## 5. 发布后验收
- 部署标记:`fc4a6a7afcfc3c1c11d1e353f46d5b724f48a39e`
- PostgreSQL:源码迁移 95,数据库完成迁移 95。
- 服务:MinIO、Security Agent、Submit Outbox、Gateway Callback、Protocol Log Worker、Gateway、API、Send Worker、Nginx、Redis、PostgreSQL 共 11 项 active。
- API 和 Gateway 健康响应均为 `status=ok`,测试环境入口 HTTP 200。
- Redis `PONG``gateway.submit.results` 消费者 1、`pending=0、lag=0`
- 发布后 API、Worker、Outbox、Callback、Protocol Log Worker、Gateway、Nginx 的 error 级日志均为 0。
- 恢复资产重新回读通过;配置与 API 进程均不存在旧哈希兼容环境变量。
- 临时管理员、双租户 API 账号、浏览器验收账号和旧哈希迁移账号均已删除;活跃 `@integration.invalid` 用户为 0。
- 浏览器真实打开客户端登录页,页面标题、账号/密码/验证码、验证码挑战、登录按钮和“返回官网”均可见,控制台 error/warning 为 0。
## 6. 保留项与边界
1. 登录后的客户端/运营端页面、深层路由刷新、异步加载失败和移动端状态仍需使用有效账号完成真实浏览器验收;本轮未把未登录页面或源码检查替代为登录后验收。
2. 本轮没有重新执行短信发送、压力测试、MinIO 上传下载或 CMPP 模拟器全链;Gateway、队列和 MinIO 只做健康、状态及自动化契约验证,没有产生业务短信、计费或文件写入。
3. 两个 SendChain 超大 Service 的职责拆分属于长期可维护性重构,应在独立提交中先补行为锁定测试后实施,不与本次安全发布混合。
4. 工作区既有未跟踪文件 `=``pnpm-lock.yaml` 未纳入、覆盖或删除;根 `package-lock.json` 为本轮依赖门禁的标准锁文件。
5. 测试环境通过不自动授权预生产。预生产仍必须在新的明确授权后重新建立恢复资产再发布。