Skip to content

fix(datasource): 语雀/Notion/IMA/飞书连接器 API 响应体补体积上限 - #3985

Merged
lyingbug merged 3 commits into
Tencent:mainfrom
yetuge:fix/connector-api-response-caps
Oct 8, 2026
Merged

lyingbug merged 3 commits into
Tencent:mainfrom
yetuge:fix/connector-api-response-caps

Conversation

@yetuge

@yetuge yetuge commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

问题

#3887 给 GitLab 连接器补了 readCapped 响应体上限,确立了「所有连接器 HTTP 响应体在缓冲前必须有界」的规范("a larger body means a broken or hostile server")。四个更早接入的连接器 API 路径仍是裸 io.ReadAll(resp.Body),破损或恶意端点可以让同步进程把任意大的响应体读进内存:

  • internal/datasource/connector/yuque/client.go — doRequest
  • internal/datasource/connector/notion/client.go — doRequest
  • internal/datasource/connector/ima/client.go — callAPIAt
  • internal/datasource/connector/feishu/core/client.go — DoRequest

这些路径拉取的元数据/文档/块载荷按契约都很小;同一批连接器的下载路径本来就有上限(maxDownloadSize / maxDownloadBytes / maxFeishuDownloadBytes),唯独 API 读取漏了。

修法

逐连接器完全比照 #3887:

  • maxJSONResponseBytes = 16 << 20 常量(与 GitLab 客户端同值);
  • 客户端结构体加 jsonLimit 字段,测试可以下调而不必真造几百 MB;
  • 本地 readCapped 帮助函数替换裸 io.ReadAll;
  • 超限以 response exceeds maximum size (N bytes) 显式报错,走各客户端既有的错误/重试路径。

飞书 429/5xx 错误路径上的 body, _ := io.ReadAll(...)(仅为日志截断预览服务的 best-effort 读取)刻意保持原样:纯诊断用途、形态是有意的,收窄它们超出本 PR 范围。

验证

每个连接器各一个测试:下调 jsonLimit 后由 httptest 服务端返回 1 MiB+1 的响应体,断言请求以显式超限错误失败而不是静默缓冲:

  • TestClient_DoRequest_RejectsOversizedResponse(yuque)
  • TestDoRequest_RejectsOversizedResponse(notion、feishu/core)
  • TestCallAPI_RejectsOversizedResponse(ima)

把 cap 调用还原成 io.ReadAll 时四个新测试全部失败(无界缓冲行为),恢复修复后全部通过。与 main 基线对照,四个包的失败集完全一致(仅 TestParseYuqueConfig、TestDownloadFile_RejectsLoopbackURL 两个与本次改动无关的环境型失败),零回归。验证环境:golang 1.27 容器(对应 go.mod 的 1.26 指令),go test ./internal/datasource/connector/{yuque,notion,ima}/... ./internal/datasource/connector/feishu/core/...。

#3887 给 GitLab 客户端补了 readCapped 上限;语雀、Notion、IMA、飞书四个
连接器的 API 读取仍是裸 io.ReadAll,破损或恶意端点可让同步进程缓冲无界
响应体。各连接器比照 GitLab 同步同一上限(16 MiB)与显式超限报错,limit
做成字段以便测试下调。飞书 429/5xx 错误路径的 best-effort 读取刻意不动。
Copilot AI balanced review requested due to automatic review settings October 6, 2026 18:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

测试里直接用字面量构造的 Client 不经过 NewClient,jsonLimit 为 0,
readCapped 会拒绝任何响应,导致 feishu/core 11 个现有测试失败。
readCapped 遇到非正数 limit 时改用 maxJSONResponseBytes。
@lyingbug

lyingbug commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

感谢 @yetuge!我推了一个小补丁:测试里直接用字面量构造的 Client 不经过 NewClient,jsonLimit 是 0,会拒掉所有响应,feishu/core 有 11 个现有测试因此失败。现在 readCapped 遇到非正数 limit 时回退到默认的 16 MiB,测试和 lint 都过了。

- yuque/ima/feishu/notion 的 readCapped 改用哨兵错误 errResponseTooLarge,
  文案不变;yuque doRequest、ima callAPIAt、feishu DoRequest 遇到超限直接返回,
  不再按退避重试 3 次(同一请求必然再次超限,只会白耗限流额度)。
- 飞书 GetTenantAccessToken 改为 readCapped + json.Unmarshal,和其他 API 响应同一上限。
- 飞书 downloadRawBytes 的 429/5xx/非 200 错误预览只读 4 KiB,
  download failed 错误不再携带完整响应体。
@lyingbug

lyingbug commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

感谢 @yetuge!合之前做了一轮交叉复审,在你的基础上又追加了一个提交:

  1. 超限是确定性错误,之前在语雀、IMA、飞书会被当成读失败按 2s/4s/8s 重试 3 次,每次都读满上限,还白耗限流额度。现在 readCapped 改用哨兵错误,这几处遇到超限直接返回,测试里也断言了服务端只命中 1 次。
  2. 顺手补了同一个飞书 client 里剩下的两处:GetTenantAccessToken 的解码也走同一上限;下载失败时 429/5xx/非 200 的诊断读取限制在 4 KiB,download failed 错误不再带完整响应体。

你在描述里说过下载诊断读取刻意不改,这里还是一起补了,因为截断是在整段读入内存之后才做的,内存风险并不会因此变小。飞书 blocks 模式内嵌表格按行窗口读取的问题我另开 issue 跟进。

@lyingbug
lyingbug merged commit a75e8e9 into Tencent:main Oct 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants