From fca2cbb39f3922bcaaf5fc0647dd35a6950309c2 Mon Sep 17 00:00:00 2001 From: zhx Date: Mon, 13 Jul 2026 23:32:44 +0800 Subject: [PATCH] =?UTF-8?q?=E9=81=BF=E5=85=8DIM=E4=BC=A0=E8=BE=93=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=B3=84=E9=9C=B2=E7=AD=BE=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/tencentim/rest_client.go | 9 ++++++++- pkg/tencentim/rest_client_test.go | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/pkg/tencentim/rest_client.go b/pkg/tencentim/rest_client.go index 330355ab..6710f796 100644 --- a/pkg/tencentim/rest_client.go +++ b/pkg/tencentim/rest_client.go @@ -464,7 +464,14 @@ func (c *RESTClient) post(ctx context.Context, command string, payload any, out response, err := c.httpClient.Do(request) if err != nil { - return err + var transportErr *url.Error + if errors.As(err, &transportErr) { + // net/http 的 url.Error.Error 会拼入完整请求 URL,而腾讯 IM UserSig 位于 + // query string。只向上保留底层网络原因,既维持 errors.Is 的超时/取消 + // 语义,又避免日志和 notice last_error 持久化可用签名。 + return fmt.Errorf("tencent im request failed: %w", transportErr.Err) + } + return fmt.Errorf("tencent im request failed: %w", err) } defer response.Body.Close() diff --git a/pkg/tencentim/rest_client_test.go b/pkg/tencentim/rest_client_test.go index 501d597f..bfb06e09 100644 --- a/pkg/tencentim/rest_client_test.go +++ b/pkg/tencentim/rest_client_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "io" "net/http" "strings" @@ -345,6 +346,26 @@ func TestRESTClientKickUserBuildsTencentRequest(t *testing.T) { } } +func TestRESTClientTransportErrorDoesNotExposeUserSig(t *testing.T) { + client := newTestRESTClient(t, func(request *http.Request) (*http.Response, error) { + if request.URL.Query().Get("usersig") == "" { + t.Fatal("test request must contain the signed Tencent IM query") + } + return nil, errors.New("synthetic EOF") + }) + + err := client.KickUser(context.Background(), 10002) + if err == nil { + t.Fatal("expected transport failure") + } + if strings.Contains(err.Error(), "usersig=") || strings.Contains(err.Error(), "sdkappid=") { + t.Fatalf("transport error exposed signed request URL: %q", err.Error()) + } + if !strings.Contains(err.Error(), "synthetic EOF") { + t.Fatalf("transport cause must remain actionable: %q", err.Error()) + } +} + func newTestRESTClient(t *testing.T, roundTrip roundTripFunc) *RESTClient { t.Helper()