Skip to content

Commit 2c0d838

Browse files
authored
fix(feishu): stop logging tenant access token fragments (#4399)
1 parent 44d90be commit 2c0d838

2 files changed

Lines changed: 62 additions & 10 deletions

File tree

‎internal/datasource/connector/feishu/core/client.go‎

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -151,16 +151,8 @@ func (c *Client) GetTenantAccessToken(ctx context.Context) (string, error) {
151151
}
152152
c.tokenExpAt = time.Now().Add(ttl)
153153

154-
prefixLen := 8
155-
if len(result.TenantAccessToken) < prefixLen {
156-
prefixLen = len(result.TenantAccessToken)
157-
}
158-
suffixLen := 4
159-
if len(result.TenantAccessToken) < suffixLen {
160-
suffixLen = len(result.TenantAccessToken)
161-
}
162-
logger.Infof(ctx, "[Feishu] got tenant_access_token: %s...%s expire=%ds",
163-
result.TenantAccessToken[:prefixLen], result.TenantAccessToken[len(result.TenantAccessToken)-suffixLen:], result.Expire)
154+
// Never log token material: even a prefix/suffix narrows the bearer credential.
155+
logger.Infof(ctx, "[Feishu] tenant access token refreshed expire=%ds", result.Expire)
164156

165157
return c.tokenCache, nil
166158
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package core
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"net/http"
8+
"net/http/httptest"
9+
"strings"
10+
"testing"
11+
12+
"github.com/Tencent/WeKnora/internal/types"
13+
"github.com/sirupsen/logrus"
14+
)
15+
16+
func TestTenantAccessTokenNeverAppearsInLogs(t *testing.T) {
17+
for _, token := range []string{"synthetic-access-value-for-logging-test", "xyz"} {
18+
t.Run(token, func(t *testing.T) {
19+
var logs bytes.Buffer
20+
recorder := logrus.New()
21+
recorder.SetOutput(&logs)
22+
recorder.SetLevel(logrus.DebugLevel)
23+
ctx := context.WithValue(context.Background(), types.LoggerContextKey, logrus.NewEntry(recorder))
24+
calls := 0
25+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
26+
calls++
27+
if r.URL.Path != "/open-apis/auth/v3/tenant_access_token/internal" || r.Method != http.MethodPost {
28+
t.Errorf("unexpected token request")
29+
}
30+
w.Header().Set("Content-Type", "application/json")
31+
_ = json.NewEncoder(w).Encode(map[string]any{"code": 0, "tenant_access_token": token, "expire": 7200})
32+
}))
33+
defer server.Close()
34+
client := &Client{
35+
baseURL: server.URL,
36+
appID: "test-app",
37+
appSecret: "synthetic-credential",
38+
httpClient: server.Client(),
39+
}
40+
for i := 0; i < 2; i++ {
41+
got, err := client.GetTenantAccessToken(ctx)
42+
if err != nil || got != token {
43+
t.Fatalf("token retrieval or cache failed: %v", err)
44+
}
45+
}
46+
if calls != 1 {
47+
t.Errorf("cached token should avoid a second request; got %d", calls)
48+
}
49+
prefix, suffix := token[:min(8, len(token))], token[max(0, len(token)-4):]
50+
for _, secret := range []string{token, prefix, suffix, "synthetic-credential"} {
51+
if strings.Contains(logs.String(), secret) {
52+
t.Error("authentication secret or fragment was logged")
53+
}
54+
}
55+
if !strings.Contains(logs.String(), "expire=7200s") {
56+
t.Error("safe token expiry diagnostics must remain")
57+
}
58+
})
59+
}
60+
}

0 commit comments

Comments
 (0)