feat(channels): IM 入站图片采集层(图片白名单 + 微信图片来源 + 复用 vision 内联入口)
- mediafetch 新增**图片白名单**(扩展名 + **字节嗅探兜底**;与文档/音频**三份名单互不污染**),
取回/裁决/上限全部走既有 Fetch(**来源白名单 deny-by-default** + 逐跳重定向校验 + 边读边限 + 超时)
⇒ 没有新造第四份取回实现
- 微信:给图片 URL 配来源白名单(沿用该渠道 CDN 根口径)
- vision 的 `data:` URI 内联入口为**只读复用**(未改 vision 一行)
- **行为变更**:图片现在**会**被尝试取回(**字节嗅探是唯一裁决权** —— 不是图片字节就不写 `Data`);
其它渠道尚无图片来源白名单 ⇒ 由 deny-by-default 拒绝(**安全**,只是暂读不到,走诚实降级)——
故同步更新了 kook 的既有测试(原断言「图片保持既有路径 = 不取回」已随口径演进)
- ⚠️ **`channelbridge` 一个字没碰**(接线点在切片报告 §7),等监工接线 + 补其它渠道白名单
- 测试:3 个新文件;变异 5 条
This commit is contained in:
1 parent
5de708a560
commit
6a545badbd
8 files changed
+1004
-37
No files matched your search
@@ -230,20 +230,43 @@ func TestInboundBodyURLIsNeverFetched(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestInboundImageMediaKeepsExistingPath 钉住边界:图片类媒体不走文档内联。
|
||||
func TestInboundImageMediaKeepsExistingPath(t *testing.T) {
|
||||
// kookPNG 是一份「字节嗅探认得出来」的 PNG 载荷(`http.DetectContentType` 只认 8 字节签名)。
|
||||
var kookPNG = []byte("\x89PNG\r\n\x1a\n" + "fake-png-body")
|
||||
|
||||
// TestInboundImageMediaIsInlinedOnlyWhenBytesAreImages 钉住切片 IM-IMG 之后的新边界:
|
||||
// 图片类媒体**现在也进内联链路**(取回字节,供桥接器内联成 data: URI 进视觉链路),但
|
||||
// 「字节嗅探是唯一裁决权」——假服务端回文本时它**绝不**被写进 `Data`,回真图字节时才写。
|
||||
// ⚠️ KOOK 自己的图片来源白名单与上游接线属后续切片(本切片只做微信)。
|
||||
func TestInboundImageMediaIsInlinedOnlyWhenBytesAreImages(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := newKookAssetServer(t)
|
||||
a, c, _ := newInlineKookAdapter(t, s)
|
||||
t.Run("字节不是图片: 取回但不内联", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := newKookAssetServer(t) // 默认正文是文本(Content-Type: text/plain)
|
||||
a, c, _ := newInlineKookAdapter(t, s)
|
||||
|
||||
if err := a.handleEvent(context.Background(), kookEventJSON(kindImage, s.srv.URL+"/asset/photo.png"), nil); err != nil {
|
||||
t.Fatalf("handleEvent: %v", err)
|
||||
}
|
||||
if err := a.handleEvent(context.Background(), kookEventJSON(kindImage, s.srv.URL+"/asset/photo.png"), nil); err != nil {
|
||||
t.Fatalf("handleEvent: %v", err)
|
||||
}
|
||||
|
||||
if s.hits.Load() != 0 {
|
||||
t.Fatalf("图片媒体被文档内联取回了 %d 次,want 0", s.hits.Load())
|
||||
}
|
||||
if len(c.message(t).Media[0].Data) != 0 {
|
||||
t.Fatal("图片媒体被写入了字节(应留给视觉链路)")
|
||||
}
|
||||
if s.hits.Load() != 1 {
|
||||
t.Fatalf("图片声明的媒体取回次数 = %d, want 1(图片现在也进内联链路)", s.hits.Load())
|
||||
}
|
||||
if len(c.message(t).Media[0].Data) != 0 {
|
||||
t.Fatal("非图片字节被写入了字节(嗅探是唯一裁决权)")
|
||||
}
|
||||
})
|
||||
t.Run("字节是真图: 内联", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := newKookAssetServer(t)
|
||||
s.body = kookPNG
|
||||
a, c, _ := newInlineKookAdapter(t, s)
|
||||
|
||||
if err := a.handleEvent(context.Background(), kookEventJSON(kindImage, s.srv.URL+"/asset/photo.png"), nil); err != nil {
|
||||
t.Fatalf("handleEvent: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(c.message(t).Media[0].Data, kookPNG) {
|
||||
t.Fatalf("Media.Data = %q, want 取回的图片字节", c.message(t).Media[0].Data)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user