Files
lucky_shop/pages_tool
ZF sun 8cfc144ba9 fix(frontend): 端用户附件走 inline base64 路径, 避开 MediaGuard SSRF 闸门
PR-FRONTEND-MEDIA-INLINE-BASE64 (2026-09-16)
PR-WB-MP-INBOUND-MEDIA 配套

症状: 用户在前端上传图片成功后 (OSS URL 已返回), AI Agent 仍回复
"我这边并没有收到或看到任何图片附件".

根因链路:
1. backend Upload.php::buildPublicUrl 用 request()->domain() 拼 URL
   (LAN 部署返回 http://192.168.x.x:8050/upload/..., 公网部署返回公网域名)
2. 前端把 upload 响应直接透给 AI chat attachments[] 字段
3. backend MediaGuard::urlAllowed 把 192.168.x.x 当内网 IP
   (FILTER_FLAG_NO_PRIV_RANGE) 拒绝 → attachments 整批丢
4. 静默失败: MediaGuard 在 '白名单过滤后空集' 时返回 ok=true + media=[]
   → Kefu.php 不 return error, 业务继续走
5. WB 收到 0 个 media block → Agent 看不到附件

修复:
- media-upload.js 新增 fileToBase64(filePath, opts) helper
  - H5: fetch(blobUrl) → blob → FileReader.readAsDataURL → strip data: 前缀
  - MP-WEIXIN/APP-PLUS: uni.getFileSystemManager().readFileSync(base64)
  - SIZE_LIMIT_INLINE_BASE64 = 5MB, 与后端 INLINE_MEDIA_LIMIT 对齐
  - 超限 / 失败返回 null, 调用方退回 URL 路径
- ai-chat-message.vue sendMessage 改为 await Promise.all 模式:
  - 对每个 pending item 调 fileToBase64(tempPath) 拿 base64
  - 把 data + encoding='base64' 注入 attachment
  - enrichedPendingAttachments 同时用于 readyAttachments + userMessage.attachments
  - 复用同一份 base64, 不重复读 file

后端接收 data 字段后整条链路已对齐:
- MessageMedia::normalizeInput 直接透传 data 字段
- MediaGuard::check data 非空时无条件放行, 不参与 URL 校验
- WbAgentOsClient::buildMediaBlocks (A 路) 路径 1 优先 data + image/audio
- WbAcpClient::buildMediaBlocks (B 路) data 非空也走 inline base64
- inline_media_sanity.php 39 个断言全部覆盖 (test 3.1, 3.2, 4.1, 4.2, 7.6)

测试:
- E2E test_inline_base64.py 10/10 通过
  - mock 上传返回 LAN URL 模拟 MediaGuard 拦截场景
  - 抓 /api/kefu/chat POST body, 验证 attachments[].data 是有效 PNG base64
  - magic bytes 89504E47 验证通过
- 既有 test_ai_chat_upload.py 20/20 通过 (无回归)

边界:
- >5MB 图片 fileToBase64 返回 null, 退回 URL 路径 (buildMediaBlocks 走
  路径 2 fetchInline / 路径 3 resource_link); LAN 部署下仍可能被 MediaGuard
  拦截, 但 5MB 以下的图片(主流微信/支付宝截图等)不受影响
- H5/MINIProgram/APP 三端都加了 #ifdef 编译条件
2026-09-16 17:55:49 +08:00
..
2026-09-16 11:52:28 +08:00
2026-08-26 18:13:00 +08:00
2026-07-25 17:53:39 +08:00