package kook // 本文件锁定 KOOK 入站**图片**的来源白名单口径(切片 IM-IMG 的渠道侧):图片与文件 // **共用同一份**媒体来源白名单(`assetAllowlist()`:`img.kookapp.cn` + 已配置的 API 根), // 因此图片不需要、也**不许**另立第三份名单。 // // 覆盖:白名单命中(平台图片 CDN、已配置的 API 根)、**反证**(非白名单来源一个字节都不 // 发出去 —— 用一台真在监听、真会计数的服务端做对照),以及白名单外的「0 次命中」。 // // KOOK 的图片地址来自消息的 `content` 字段(发送方可影响的内容),因此这份白名单是硬门: // 伪造的图片消息完全可以填任意地址,能挡住它的只有来源名单本身。 import ( "context" "net/http" "net/http/httptest" "sync/atomic" "testing" "git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter" ) // TestKookImageOriginsAreAllowlisted 钉住「图片来源复用既有媒体来源白名单」:平台图片 CDN // 与已配置的 API 根命中,其余一律不命中(deny-by-default)。 func TestKookImageOriginsAreAllowlisted(t *testing.T) { t.Parallel() s := newKookAssetServer(t) a, _, _ := newInlineKookAdapter(t, s) allow := a.assetAllowlist() for _, origin := range []string{ "https://img.kookapp.cn/assets/2023-03/abcdef.png", s.srv.URL + "/asset/photo.png", } { if !allow.Allowed(origin) { t.Fatalf("图片来源 %q 没有被既有白名单覆盖(图片与文件必须共用一份来源名单)", origin) } } for _, origin := range []string{ "https://evil.example.com/photo.png", "http://img.kookapp.cn/assets/2023-03/abcdef.png", // 该 CDN 只声明了 https 来源 "file:///etc/passwd", } { if allow.Allowed(origin) { t.Fatalf("非平台来源 %q 被白名单放行了", origin) } } } // TestKookImageOffAllowlistIsNotDownloaded 是**反证**:图片的 `content` 指向白名单之外的主机时 // **一个字节都不发出去** —— 对照服务端真的在监听、真的会计数(因此这条不是「连不上」的 // 假象),命中次数必须是 0,字节必须为空,且失败不被静默。 func TestKookImageOffAllowlistIsNotDownloaded(t *testing.T) { t.Parallel() var hits atomic.Int32 foreign := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { hits.Add(1) w.Header().Set("Content-Type", "image/png") _, _ = w.Write(kookPNG) })) t.Cleanup(foreign.Close) s := newKookAssetServer(t) // 它才是被配置进白名单的那个 API 根 a, c, _ := newInlineKookAdapter(t, s) if err := a.handleEvent(context.Background(), kookEventJSON(kindImage, foreign.URL+"/asset/photo.png"), nil); err != nil { t.Fatalf("handleEvent: %v", err) } if got := hits.Load(); got != 0 { t.Fatalf("非白名单来源的图片被取回 %d 次,want 0", got) } msg := c.message(t) if len(msg.Media) != 1 || msg.Media[0].Kind != adapter.MediaImage || len(msg.Media[0].Data) != 0 { t.Fatalf("非白名单来源的图片仍被写入了字节:%+v", msg.Media) } if s.hits.Load() != 0 { t.Fatalf("图片请求打到了已配置根 %d 次,want 0", s.hits.Load()) } } // TestKookImageIsInlinedFromPlatformCDN 正向(平台 CDN 形状):`img.kookapp.cn` 的图片地址 // 在名单内 ⇒ 取回并内联。(取回用的是假服务端:真 CDN 不在用例里碰,白名单本身由上面的 // 断言钉住。) func TestKookImageIsInlinedFromPlatformCDN(t *testing.T) { t.Parallel() s := newKookAssetServer(t) s.body = kookPNG a, c, _ := newInlineKookAdapter(t, s) // handleEvent 的图片地址取自 `content`,因此这里用假服务端地址(它同时是配置的 API 根, // 于是「名单内」这条前提成立);平台 CDN 主机名的命中由上面的白名单用例单独钉住。 if err := a.handleEvent(context.Background(), kookEventJSON(kindImage, s.srv.URL+"/asset/photo.png"), nil); err != nil { t.Fatalf("handleEvent: %v", err) } msg := c.message(t) if msg.ContentType != adapter.ContentImage || len(msg.Media) != 1 { t.Fatalf("图片消息被归一成了别的形状:%+v", msg) } if string(msg.Media[0].Data) != string(kookPNG) { t.Fatalf("Media.Data = %q, want 取回的图片字节", msg.Media[0].Data) } if s.hits.Load() != 1 { t.Fatalf("图片资产命中 = %d, want 1", s.hits.Load()) } // 图片地址是发送方可影响的 `content`,因此它**绝不**能进正文(正文只承载聊天文本)。 if msg.Content != "" { t.Fatalf("图片地址被写进了正文:%q(资产地址只配进 Media[].URL)", msg.Content) } }