docs: record code quality remediation rollout
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
# 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 项相关服务 active,API/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. 测试环境通过不自动授权预生产。预生产仍必须在新的明确授权后重新建立恢复资产再发布。
|
||||
|
||||
Reference in New Issue
Block a user