WorkBuddy
fd7195ecb0
fix(safety): Phase 10 复验 —— 修复图片上传魔数校验死代码(生产故障)/ 高风险词写入日志
对 Phase 10(Image & Chat Safety)执行 KEEP-VERIFY 复验:不采信历史 ✅,逐条核对
图片私有访问、类型/大小/魔数校验、审核与水印、风险识别/举报/违规处理、日志脱敏。
Gate 判定通过,发现并修复 2 处缺陷,并推翻旧报告 2 项安全声明。
D1(Critical,生产故障)图片上传的类型与魔数校验整条是死代码
- PhotoService.uploadAndSave 用 `header = new byte[Math.min(12, stream.available())]`
仅分配零值缓冲,从不调用 stream.read(...) → validateMagicNumber 对任何文件都收到
12 个 0x00 → 恒返回 false → 恒抛"无效的图片文件头"。
- 影响链(已确认线上触发):小程序 local-eligibility-supplement.ts 以
photoType='local_eligibility' 调 POST /photos/upload → 本地资格佐证上传 100% 失败。
- 次生问题:isAllowedMimeType(image-only 白名单)在 local_eligibility 分支之后
无条件执行,即使魔数修好,application/pdf 仍被挡,而 FeatureFlagConfig
的 localEvidenceAllowedTypes 明确允许 pdf;另有未 mark 的 stream.reset() 无效调用。
- 测试为何没发现:PhotoServiceTest 只对 validateMagicNumber 传手写字节做单测,
从不经过 uploadAndSave,该路径缺陷完全无覆盖。
- 修复:改用 try(InputStream){ header = s.readNBytes(12); } 真正读取;
MIME 校验按 photoType 分流(local_eligibility 走自己的白名单含 pdf);
魔数校验仅对图片执行、PDF 跳过;移除死 reset();上传用全新流与校验流隔离;
generateFileKey 重载按 Content-Type 推断扩展名,PDF 不再拿到误导性 .jpg key。
D2(Low)高风险词写入普通日志
- ChatSafetyService.scanContent 把命中的高风险词原样写入 log.warn,
与同文件 CONTACT 分支"只记 pattern 不记内容"矛盾,且违反本阶段 Gate。
- 修复:改为 log.warn("Chat content matched HIGH_RISK keyword (blocked)")。
推翻的旧声明(docs/Phase-10-Completion-Report.md)
- 「伪装恶意图片被拒(Magic Number校验)」不成立:该校验运行时恒失败,
连合法图片也一律被拒(fail-closed 到不可用),安全属性并非由它提供。
- 「EXIF清理预留(PhotoService.stripExif占位)」不存在:全仓库无该符号。
且 getUploadToken 客户端直传路径服务端无法介入清理 EXIF。
- 已在旧报告顶部加状态注记,指向本次复验报告。
测试
- 新增 PhotoUploadValidationTest(6 用例),用 MockMultipartFile 真正走一遍
uploadAndSave:合法 JPEG/PNG 通过且确实调用 putObject、伪造 Content-Type 被魔数挡、
PDF 佐证不被 image-only 挡且 key 以 .pdf 结尾、非法 MIME 被拒、超大被拒。
- 负向验证:临时把魔数守卫改为 if(false && ...) → 恰好 spoofedContentTypeRejectedByMagic
一个用例变红,其余 5 个仍绿;随后还原并 grep 确认无 NEGATIVE-CHECK-TEMP 残留。
- 全量回归(离线容器 danyang-match-backend-test,mvn -o test):
334 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS(基线 328,+6)。
登记 gap(不自动实现,待产品确认)
- 图片水印:全仓库无实现;主计划未列硬需求,仅"开发执行计划修订版"提"高级动态水印"。
- EXIF 清理:旧声明不存在;客户端直传路径服务端无法介入,需改为接收侧重编码。
- 直传凭证路径(getUploadToken)无服务端内容校验且不落 DB 记录 → 后续 getSignedUrl
归属校验会查不到 UserPhoto 行,属契约层缺口,超出本阶段安全 Gate 范围。
- 违规自动升级:ChatSafetyService 注释明确属 Phase 17 范围,当前 high 仅拦截不落库。
文档:新增 Phase-10-V11-Recovery-Completion-Report-2026-09-16.md;
旧报告加状态注记;主计划 Phase 10 复选框置 ✅;STATUS / JIRA_BACKLOG 同步(334 tests)。
Phase 11 未启动,按计划每阶段出报告后停止等待确认。
2026-09-16 21:00:52 +08:00
..
2026-09-09 22:05:00 +08:00
2026-09-16 21:00:52 +08:00
2026-09-14 10:02:34 +08:00
2026-09-14 10:02:22 +08:00
2026-09-11 09:07:41 +08:00
2026-09-09 22:05:00 +08:00
2026-09-09 22:05:00 +08:00
2026-09-09 22:05:00 +08:00
2026-09-09 22:05:00 +08:00
2026-09-10 10:58:43 +08:00