由 OCR(open-codereview.ai,deepseek-v4-flash)审查产出: - 审查汇总.md / high-severity-comments.md / all-comments.jsonl / 修复记录.md 等 Reviewed-by: OCR (open-codereview.ai)
13 KiB
13 KiB
gongxue-base 修复记录(Phase 0-2 实施)
依据 2026-08-09 OCR 全库审查(
all-comments.jsonl,1626 条精确去重)与用户确认的计划(P0+P1,直接实施,允许 schema 变更)。 执行:主 agent + 9 个并行 worker(按互不重叠的文件划分)。全部改动未提交,供人工 review。
验证基线(最终)
- server:
npm run typecheck✅ |npm run lint✅ 0 error 0 warning |jest148 套件 / 1087 用例 ✅ - admin:
npm run typecheck✅ |vitest run33 文件 / 175 用例 ✅ |oxlint0 error(any 为 warn) - OCR 复查:全量改动
ocr review1 轮(100 文件 / 84 条),定向ocr scan2 轮(12 文件 60 条 → 4 文件 18 条),发现的 critical/high 已逐条修复或明确降级为待办。
Phase 0 — 部署/运维
serve-proxy.js:API_TARGET 真正生效(new URL 解析);SPA fallback 仅导航请求,缺失资源 404;fs.promises 异步读文件;剥离 hop-by-hop 头(含 Connection 提名头);proxyRes/req error 监听 + 30s 超时(带 error 销毁 + 504)+ 同步异常 try/catch;路径穿越防护。migrate.sh:密码改用子 shell 内MYSQL_PWD(不进 argv、不泄漏 pm2);删除硬编码兜底密码,缺失即报错;SQL 包事务;sleep 8改为轮询 users/tenants/operation_logs 就绪;pm2 startOrReload;trapERR 停应用;.env从脚本目录读取并去引号;导入计数不再被set -e掩盖。deploy.sh/.gitea/workflows/deploy.yml:rsync 排除.env*/data.sql/uploads/;总是npm ci(含 devDependencies,migration:run 依赖 ts-node);远程块set -e;workflow 加 concurrency。ecosystem.config.cjs:kill_timeout: 5000、listen_timeout: 8000、绝对日志路径。oxlint.config.ts:React 版本对齐实际安装(19.2.7);no-explicit-any改为 warn。
Phase 1 — 安全
- JWT:
jwt-secret.ts统一取密钥,生产缺JWT_SECRET启动即失败,删除公开兜底密钥。 - 密码策略:登录最短 8 位、
@MaxLength(72);用户名@MaxLength(64);登录失败文案统一防枚举。 - 越权/IDOR:classes 全部变更接口补
assertReadAccess;dashboard 五个全局接口按班级范围收紧(非管理员返回空);schedules.findAll 始终应用范围;students basic-lookups 范围化;attendance match 校验学生范围、auto-match 仅管理员;archive 全部路由按学生范围校验(新增resolveRecordStudentId+StudentsService.assertStudentAccess);exams 权限码尝试分离后因exam:create/edit/delete属废弃码回滚为exam:view(服务层已有范围校验,见待办)。 - 全局
ValidationPipe({ transform: true })(不开 whitelist,避免裸 body 被剥空)。 - 凭据:集成配置 appSecret 复用 AES-256-GCM 静态加密(
secret-crypto.ts+ 回填脚本npm run encrypt:integration-secrets);对外输出脱敏。 - 注入:
common/like-escape.ts+ 13 处 LIKE 调用转义(含 rbac 教师搜索)。 - 无界输入:attendance records
@ArrayMaxSize(500)、ai-chat values ≤200 字段、notes@MaxLength(1000)、sync limit ≤200、全部上传 10MB 上限。 - 上传/下载/XSS:
common/mime.ts扩展名白名单归一化 +X-Content-Type-Options: nosniff+ 安全类型才 inline;上传判空 400;流 error/close 处理;AttachmentsTab不再 window.open(blobURL)(img/sandbox iframe/ + revoke)。 - 其他:
request-utils默认不信任代理头(TRUST_PROXY=1才信任);updateTeacherProfile返回脱敏;.env.example补充 TRUST_PROXY 说明。
Phase 2 — 数据正确性
- 并发/事务:wallets 原子扣款/退款(
balance ± :amount+ 余额条件,杜绝 double-spend);financial-operations 条件认领 + 支持 15 分钟 stale running 回收 + 结果 JSON 解析容错;ai-a2ui 唯一约束冲突幂等;deposits purge/create 排除归档;with-audit-log best-effort;sync-runner finally 不掩盖原错误;imports.commit/preview、students.import、rooms.create、exams 批量、attendance-generation 事务化;occupancies 用父行 FOR UPDATE 防重复入住 + 退宿按剩余在住置状态;attendance-import 用 MySQL advisory lock + 30 分钟看门狗。 - 校验:3 个 DTO 的矛盾校验器(@Matches vs @IsISO8601 strict)统一为仅日期;rental 跨字段
endDate >= startDate;expenses 上传空值 400。 - 实体:learning-record / student-profile 的 student_id 双映射移除;student-wallet / bill 金额 DECIMAL 加 number transformer;ding-leave/attendance-raw 可空 FK 类型
| null;class-student 外键 onDelete 统一 CASCADE;app.modulesynchronize 仅显式 true。 - 时区:
common/china-time.ts(getChinaDate / addDaysToDateOnly / getWeekDayFromDateOnly / isConsecutiveDates + 单测);attendance-calendar、ai-review.import-basic、attendance-generation、rental-schedule 的本地/UTC 混用修复。 - 状态/路由:bills batch/status 与 classrooms report 路由顺序;attendance-report 连续日真实判断;sync 空 mappedValues 跳过更新;考勤组名带 classId;rbac-seed 仅空表或
SEED_ROLES=true播种 + 默认管理员仍会创建(生产缺 ADMIN_PASSWORD 报错、不打印密码、不再批量重激活禁用用户、删除死代码);expense-types 唯一性检查回滚为与 DB 唯一索引一致;agent-tool.registry 重复注册守卫。 - 前端:Roles 多组勾选受控化;Schedules 月视图可新建;useDirtyGuard 返回稳定引用;JinshujuMatchModal success:false 分支;Attendance 订阅 store;late 计入出勤率/主状态;EditableCell Enter 不再与 select/date 编辑器冲突;DynamicChart 空数据短路;useSubmissionState ref 变更移到 layout effect。
追加修复(2026-08-09 第二轮)
- exams 写权限分离:
exam:create/exam:edit/exam:delete从DEPRECATED_PERMISSION_CODES移除并加入PRESET_PERMISSIONS(seed 会创建);控制器 create→exam:create,archive/restore/batch-*/updateScore→exam:edit,purge→exam:purge;spec 同步(新增 4 组权限断言)。 - wallets refundBill 幂等:退款前对账单做条件更新
status <> 'cancelled'(affected=0 直接返回),与 bills.cancel 的事务悲观锁双保险,杜绝并发重复冲正。 - exams updateScore 防误清空:
score缺失(undefined)直接 400,清空必须显式传 null。 - 验证:server typecheck/lint ✅,jest 148 套件 / 1091 用例 ✅;定向 OCR 复查 3 文件无 high/critical。
持续修复循环(Round 1-7,OCR 复查驱动)
按用户要求启动持续修复+review goal:每轮并行派发 3 个互不重叠的 worker → 全量验证 → OCR 定向复查 → 再修,共 7 轮。
- R1(medium 清理):deploy SSH_HOST 校验/密钥 heredoc、jwt-secret 环境判定、encrypt 脚本防回退密钥+连接释放、archive 下载审计、bills 通知 catch;rbac 密码 8-72、attendance-import success/release、wallets 分页、attendance 日期边界/连续段语义、AttachmentsTab 乱序、serve-proxy 尾斜杠;migrate trap/防重入/就绪检查、rental ENDED、students.import 死注入、bills-generation 注释。
- R2(high 修复):archive 死代码、rental 死循环×2、financial SQL 优先级、wallets :amount 绑定、exams 权限回滚+spec、rbac-seed admin 创建、attendance 看门狗语义、serve-proxy fallback MIME、migrate argv、deploy npm ci、expense-types 一致性。
- R3(exams 权限 + wallets 幂等):exam:create/edit 从废弃列表恢复并 seed、控制器按写操作标注;refundBill 条件更新幂等;updateScore 空 PUT 防护。定向复查 3 文件 0 high。
- R4(medium 批量):bills 通知/ids、jwt-secret、encrypt 脚本、archive 审计、attendance success/release/日期/连续段、AttachmentsTab 序号、serve-proxy、migrate trap/flock、rental ENDED、students.import 死注入、rbac.dto 校验。
- R5(4 high + quick wins):migrate cd/TOCTOU、jwt SEED_DEV、students.import 空 updates、bills 首次生成并发(GET_LOCK);serve-proxy 超时竞态/非法头/请求头 hop-by-hop/URL 回退;attendance-generation N+1、matchImport 事务、锁名常量、rbac.dto 校验、bills 批量学生+allSettled。
- R6(high→1 + quick):attendance 看门狗只告警、runner.connect try/finally、fetchAllPages 并发、mapToEntity 批量预取;attendance-generation 标签/死代码/事务;bills 批量保存/四舍五入/未用注入;rental 多日展开+事务+枚举;students.import host org 缓存/日期日历校验;rbac.dto 锚定正则+长度上限。
- R7(high→0):bills 路由顺序(export 在 :id 前)+ ids DTO;serve-proxy 失败分支 error 监听/写头抛错释放 socket/客户端断开;migrate DUMP 绝对路径/原子锁/文件预检/APP_STARTED/5 表就绪/DML-only 说明;rbac.dto 显示名允许内部空格+密码字节上限+subjects 唯一性;bills 通知幂等(仅新增 paid 才发)+ reason 审计转义(本轮新增)。
收敛结论
- 连续 2 轮 OCR 定向复查(覆盖所有修复过的文件):high/critical = 0。
- 每轮全量验证:server
typecheck✅lint0 error ✅jest152 套件 / 1170 用例 ✅;admintypecheck✅vitest175 ✅。 - 剩余为 P2 性能/重构/依赖 schema 项(见下),非本轮目标。
收尾补充(复查核对后)
bills.controller:取消账单的reason写入审计日志前做控制字符过滤(防日志注入)。serve-proxy:删除 serveStatic 未使用的req参数。migrate.sh:就绪检查超时时输出期间收集的 MySQL stderr(密码错误/容器未启动可诊断)。- 验证:server lint/typecheck/jest 152 套件 / 1170 用例全绿。
P2 纯代码项(并行 3 worker + 复查二修)
- serve-proxy 静态流式化:fs.stat + Content-Length + createReadStream pipe,消除全量缓冲;目录请求 404、realpath 防 symlink 穿越。
- migrate.sh 库名变量化:LEGACY_DB/TARGET_DB 抽变量;COMMIT 用 printf 防粘行;IMPORTED_COUNT 失败兜底;
.migrate.done改为数据提交后立即写。 - attendance-import:IN(dingIds) 按 1000 分块并发查询;finally 仅抢到锁才清理运行态。
- students.import:phone/idNumber 批量预取(2 条 IN 查询)替代逐行 findOne;新增双键冲突检测(phone/idNumber 命中不同学生 → 跳过并计 skippedConflict,防数据错乱)。
- bills-generation:per-room 入住查询改为一次 IN 批量 + 内存分组;日期统一 YYYY-MM-DD 字符串比较(toDateOnly/daysBetweenDateOnly),消除 Date hydrate 比较不可靠;eligibleDays 提出循环。
- bills DTO:BatchIdsBodyDto 移入 dto/bill.dto;batchPurge/batchRemove 复用 common BatchIdsDto;billingMonth 收紧为真实月份。
- rbac.dto:UpdateRoleDto/UpdateUserDto 用 PartialType/OmitType 去重;subjects 补非空白 each。
- 复查二修:serve-proxy 目录/realpath、matchImport 冲突、bills 日期、attendance finally、DTO/migrate 小项;spec 同步。
- 验证:server typecheck/lint 全绿,jest 152 套件 / 1178 用例 ✅。
剩余(已收敛)
- 数据库唯一约束(bills 周期、Student.phone/idNumber、expense-type code 放开软删复用)与迁移文件生成(class_student 外键 CASCADE)——需接库环境执行
npm run migration:generate -- FixEntityMappings。 - 少量 low(STATIC_DIR 可配置、%20 解码、X-Forwarded 链保留、登录时序侧信道权衡项等)按需处理。
- 迁移文件生成(class_student 外键 CASCADE 等):
cd apps/server && npm run migration:generate -- FixEntityMappings(需接库环境)。 - 低价值 low 项(如 STATIC_DIR 可配置、%20 解码、x-forwarded 链保留、attendance-import 无界 IN 分块等)按需后续处理。
待办/已知(未修)
exams 写权限分离✅ 已修复(见追加修复)。wallets refund 的 bill 状态 check-then-act✅ 已修复(条件更新幂等)。- 迁移文件:实体改动(class_student 外键 CASCADE 等)需在接库环境
cd apps/server && npm run migration:generate -- FixEntityMappings生成并 review 后上线;本地无 MySQL 未生成。 - P2 backlog:剩余 low/维护性 ~700 条(见
all-comments.jsonl),未在本轮实施。 - OCR 复查仍提示的 medium/low(如 migrate.sh 无并发锁/命令注入面、rbac-seed N+1、serve-proxy 的 servedIndex 冗余等)可按后续迭代处理。
交付物
- 改动:
git status约 120+ 文件(未提交)。 - 报告:
ocr-reports/审查汇总.md、high-severity-comments.md、all-comments.jsonl、admin/server/misc-scan-comments.jsonl、修复记录.md(本文件)。