Skip to content

feat(weixin): support incoming image and file attachments#1765

Merged
piorpua merged 5 commits intoiOfficeAI:mainfrom
JerryLiu369:feat/weixin-attachments
Mar 29, 2026
Merged

feat(weixin): support incoming image and file attachments#1765
piorpua merged 5 commits intoiOfficeAI:mainfrom
JerryLiu369:feat/weixin-attachments

Conversation

@JerryLiu369
Copy link
Copy Markdown
Member

Summary

  • Download WeChat image/file attachments from the CDN (AES-128-ECB decryption, magic-byte type sniffing)
  • Store all attachments in dataDir/weixin-uploads/ with 72-hour TTL and 200 MB cap
  • Pass attachment paths to the agent as [Image: /path] or [File "name": /path] markers in the message text

Test plan

  • Send an image via WeChat — agent receives [Image: ...] and can describe it
  • Send a file via WeChat — agent receives [File "name": ...] and can read it
  • Files appear in weixin-uploads/ and are cleaned up after 72 hours

🤖 Generated with Claude Code

@JerryLiu369 JerryLiu369 force-pushed the feat/weixin-attachments branch from 773bb36 to 2b6738a Compare March 26, 2026 16:39
@sentry
Copy link
Copy Markdown

sentry bot commented Mar 27, 2026

Codecov Report

❌ Patch coverage is 66.66667% with 29 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...c/process/channels/plugins/weixin/WeixinMonitor.ts 64.19% 16 Missing and 13 partials ⚠️

📢 Thoughts on this report? Let us know!

@JerryLiu369 JerryLiu369 force-pushed the feat/weixin-attachments branch 3 times, most recently from 5b1fa50 to ef7c5b6 Compare March 27, 2026 15:17
JerryLiu369 and others added 4 commits March 28, 2026 13:20
- WeixinMonitor: detect image (type=2) and file (type=4) items, download
  from WeChat CDN with optional AES-128-ECB decryption, sniff file type
  from magic bytes. Passes attachments as a typed field on WeixinChatRequest
  (not embedded in text).
- WeixinAdapter: assembles the agent message — appends [Image: /path] or
  [File "name": /path] markers to the text so the agent can read the files.
- WeixinPlugin: resolves the uploads directory lazily with caching.
  Looks up the existing weixin conversation workspace via DB on first
  attachment and caches the path; falls back to dataDir/weixin-uploads/
  if no conversation exists yet (first ever message).
- TTL + size-cap cleanup runs after each batch of downloads.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Store all WeChat attachments in dataDir/weixin-uploads/ with 72-hour TTL.
Removes the workspace-resolution logic that was unreliable across agent switches.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
When a message contains only media items (no text) but all CDN downloads
fail, agent.chat was still called with empty text and no attachments.
Also reformats the code to pass the Oxfmt format check.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@JerryLiu369 JerryLiu369 force-pushed the feat/weixin-attachments branch from ef7c5b6 to af934eb Compare March 28, 2026 05:20
@piorpua piorpua added the bot:reviewing Review in progress (mutex) label Mar 29, 2026
@piorpua
Copy link
Copy Markdown
Contributor

piorpua commented Mar 29, 2026

Code Review:feat(weixin): support incoming image and file attachments (#1765)

变更概述

本 PR 为微信频道新增了图片和文件附件的接收支持:在 WeixinMonitor 中检测 IMAGE_ITEM_TYPE(type=2)和 FILE_ITEM_TYPE(type=4)消息体,从 WeChat CDN 下载原始数据、AES-128-ECB 解密、magic-byte 类型嗅探后保存到本地 weixin-uploads/,并将文件路径以 [Image: /path][File "name": /path] 格式追加到 IUnifiedIncomingMessage 的 text 字段交给 Agent 处理;新增 72 小时 TTL + 200 MB 容量上限的清理机制。


方案评估

结论⚠️ 方案有缺陷

整体方案思路合理:将附件路径以 marker 形式注入 text 字段,不改变 IPC/Agent 接口,改动影响面很小。下载 + 解密 + 存储的实现也与现有架构(main process 直接 fs 操作)一致。但 msgId 直接来自 WeChat API 响应,未经过滤就拼入文件路径,存在路径穿越风险,需要修复。


问题清单

🟠 HIGH — msgId 未过滤,存在路径穿越

文件src/process/channels/plugins/weixin/WeixinMonitor.ts,第 267 行

问题代码

const msgId = msg.msg_id ?? String(Date.now());
// ...
const fileName = `${msgId}-${idx}-${safeName}${ext}`;
const filePath = path.join(uploadsDir, fileName);

问题说明msgId 直接取自 WeChat API 响应,未做任何清理。若服务端(或中间人)返回 msg_id: "../../home/user/.ssh/authorized_keys" 等恶意值,path.join 会解析 \.\. 跳出 uploadsDir,导致文件被写入任意路径。declaredName 已经过 safeName 正则清理,msgId 同样需要相同处理。

修复建议

const rawMsgId = msg.msg_id ?? String(Date.now());
const safeMsgId = rawMsgId.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 48);
// ...
const fileName = `${safeMsgId}-${idx}-${safeName}${ext}`;

🔵 LOW — 现有测试名称与新行为不符

文件tests/unit/channels/weixinMonitor.test.ts,第 101 行

问题代码

it('does not call agent.chat for non-text items (image type=2)', async () => {

问题说明:PR 改动后,type=2 的 item 已经会进入下载流程,并非因为「非文本消息被跳过」而不触发 agent.chat,而是因为 image_item 为空导致下载抛异常(missing encrypt_query_param)、attachments 为空 + text 为空,才最终跳过。测试名称描述的行为与实际代码路径已不一致,容易误导后续维护者。

修复建议

it('skips agent.chat when media item has no encrypt_query_param', async () => {

汇总

# 严重级别 文件 问题
1 🟠 HIGH WeixinMonitor.ts:267 msgId 未过滤拼入文件路径,存在路径穿越风险
2 🔵 LOW weixinMonitor.test.ts:101 测试名称与新行为不符

结论

⚠️ 有条件批准 — 路径穿越问题(HIGH)需要修复后可合并,LOW 级测试名称建议一并更新。


本报告由本地 pr-review skill 生成,包含完整项目上下文,无截断限制。

CONCLUSION: CONDITIONAL
IS_CRITICAL_PATH: false
PR_NUMBER: 1765

@piorpua piorpua added bot:ready-to-fix CONDITIONAL review done, waiting for bot fix bot:fixing Fix in progress (mutex) and removed bot:reviewing Review in progress (mutex) bot:ready-to-fix CONDITIONAL review done, waiting for bot fix labels Mar 29, 2026
Prevent path traversal by stripping non-alphanumeric characters from
msgId (sourced from WeChat API) before constructing the upload filename,
matching the existing safeName treatment for declaredName.

Review follow-up for iOfficeAI#1765
@piorpua
Copy link
Copy Markdown
Contributor

piorpua commented Mar 29, 2026

PR Fix 验证报告

原始 PR: #1765
修复方式: 直接推送到 feat/weixin-attachments

# 严重级别 文件 问题 修复方式 状态
1 🟠 HIGH WeixinMonitor.ts:267 msgId 未过滤拼入文件路径,存在路径穿越风险 新增 safeMsgId = msgId.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 48),与 safeName 处理方式一致 ✅ 已修复

总结: ✅ 已修复 1 个 | ❌ 未能修复 0 个

🔵 LOW 级别问题(测试名称与新行为不符)已跳过(不阻塞合并)。

@piorpua piorpua enabled auto-merge (squash) March 29, 2026 04:45
@piorpua piorpua added bot:done Auto-merged by bot and removed bot:fixing Fix in progress (mutex) labels Mar 29, 2026
@piorpua piorpua merged commit 50e7bf7 into iOfficeAI:main Mar 29, 2026
6 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:done Auto-merged by bot

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants