Files
Cyrene_v2/plugins/channels/weixin_ilink/upstream_token_test.go
T
AskaEth fe3872ee28 fix(trace): 令牌守卫上移进 trace.UpstreamError,构造即安全
M48 复审的中危 finding:error.type 里的散文经 trace.UpstreamError 原样出镜,
因为守卫加在调用方(M46 的 model/voice 两处),构造函数自己不套守卫 ——
任何新调用点默认不安全,httpclient / vision / embedding / weixin 四条出口一直漏。

- trace.UpstreamError 构造时对 error.code / error.type 各走一次 ErrorToken
  ([A-Za-z0-9_.-]{<=64},不合规即空串):默认安全,不是可选加固。
- 构造后注入这条窄路收进 UpstreamErrorInfo.SetCode / SetType(同一道守卫),
  供 DashScope 顶层 code 与 Anthropic 流内 error.type 两处使用 —— 调用方不再
  各抄一份 ErrorToken。
- 删除因此冗余的调用方守卫:model.providerUpstreamError 与
  voice.dashscopeOKBodyError。保留 voice 的 request_id 守卫(它不是 code/type,
  构造函数看不见)。
- 四条漏的出口一行没改,只加用例证明守卫上移后自动安全;摘掉构造函数守卫时
  四条用例 + model/voice 旧用例同时变红。
2026-10-26 19:13:38 +08:00

51 lines
1.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package weixin_ilink
// 本文件是 M49 的**出口回归探针**:令牌守卫上移进 trace.UpstreamError 之后,
// iLink 适配器一行没改也应当自动安全 —— 上游把散文塞进 error.code / error.type
// 时,错误文本与日志里都搜不到它,而 status / body_bytes 照旧。
//
// 断言形状是「摘掉 trace.UpstreamError 里的守卫就变红」:这里没有任何本地守卫。
// iLink 会在 errmsg 里回显请求内容(发出去的就是她说的话),所以这一条不能漏。
import (
"bytes"
"context"
"log/slog"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
// TestDoJSON_StatusErrorDropsFreeTextUpstreamCode 4xx 报文的 error.code /
// error.type 里带散文 ⇒ 两个字段整体消失,状态码与体积摘要仍在。
func TestDoJSON_StatusErrorDropsFreeTextUpstreamCode(t *testing.T) {
t.Parallel()
const prose = "Invalid value: '她刚才说的那句话'"
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(`{"error":{"code":"` + prose + `","type":"` + prose + `"}}`))
}))
defer srv.Close()
ad := newAdapter(t, Config{BaseURL: srv.URL, BotToken: "tok"}, srv)
err := ad.doJSON(context.Background(), http.MethodPost, ad.apiURL("/ilink/bot/sendmessage"), nil, nil)
if err == nil {
t.Fatal("400 却报成功")
}
var logBuf bytes.Buffer
slog.New(slog.NewJSONHandler(&logBuf, nil)).Error("ilink call failed", "err", err)
for _, surface := range []string{err.Error(), logBuf.String()} {
if strings.Contains(surface, "她刚才说的那句话") {
t.Fatalf("上游 error.code/type 里的散文进了错误或日志:%s", surface)
}
}
for _, want := range []string{"status=400", "body_bytes="} {
if !strings.Contains(err.Error(), want) {
t.Fatalf("错误里缺少 %q:%v", want, err)
}
}
}