fix(automations): redact reminder message before logging
This commit is contained in:
1 parent
e0793debc3
commit
5a69d6e0ff
2 files changed
+72
-1
No files matched your search
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/bus"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/trace"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -206,6 +207,6 @@ func (rs *ReminderStore) fireDue(ctx context.Context) {
|
||||
rs.log.Error("publish reminder", "id", r.ID, "err", err)
|
||||
continue
|
||||
}
|
||||
rs.log.Info("reminder fired", "id", r.ID, "at", r.At, "message", r.Message)
|
||||
rs.log.Info("reminder fired", "id", r.ID, "at", r.At, "message", trace.RedactText(r.Message))
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package automations
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -166,3 +169,70 @@ func TestService_ReminderLifecycle(t *testing.T) {
|
||||
t.Fatalf("CancelReminder: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestReminder_FireLogIsRedacted 钉住提醒触发日志的出文本口径:Message 是模型/
|
||||
// 用户自由文本,可能顺手带着凭据(「把 api_key=… 发我」)。日志面必须打码,
|
||||
// 而总线上的 payload 是**投递面**,必须原样保留 —— 打码不能改投递内容。
|
||||
func TestReminder_FireLogIsRedacted(t *testing.T) {
|
||||
t.Parallel()
|
||||
const secret = "sk-live-9f8e7d6c5b4a3210"
|
||||
msg := "把 api_key=" + secret + " 发到群里"
|
||||
|
||||
var logBuf bytes.Buffer
|
||||
b := bus.New()
|
||||
defer b.Close()
|
||||
|
||||
got := make(chan bus.Event, 1)
|
||||
unsub, err := b.Subscribe(context.Background(), SubjectTrigger, func(_ context.Context, ev bus.Event) error {
|
||||
got <- ev
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("subscribe: %v", err)
|
||||
}
|
||||
defer unsub()
|
||||
|
||||
rs, err := NewReminder(Config{ReminderInterval: 10 * time.Millisecond}, Deps{
|
||||
Bus: b,
|
||||
Log: slog.New(slog.NewTextHandler(&logBuf, nil)),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("NewReminder: %v", err)
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
if err := rs.Add(ctx, Reminder{ID: "leak", At: time.Now().Add(10 * time.Millisecond), Message: msg}); err != nil {
|
||||
t.Fatalf("Add: %v", err)
|
||||
}
|
||||
if err := rs.Start(ctx); err != nil {
|
||||
t.Fatalf("Start: %v", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case ev := <-got:
|
||||
rem, ok := ev.Payload.(Reminder)
|
||||
if !ok {
|
||||
t.Fatalf("payload type = %T, want Reminder", ev.Payload)
|
||||
}
|
||||
if rem.Message != msg {
|
||||
t.Fatalf("总线 payload 的 message 被改动: %q, want %q", rem.Message, msg)
|
||||
}
|
||||
case <-time.After(3 * time.Second):
|
||||
t.Fatal("reminder did not fire")
|
||||
}
|
||||
// Stop 等轮询 goroutine 退出,触发日志必然已写完,避免与后台写缓冲竞争。
|
||||
if err := rs.Stop(ctx); err != nil {
|
||||
t.Fatalf("Stop: %v", err)
|
||||
}
|
||||
|
||||
out := logBuf.String()
|
||||
if strings.Contains(out, secret) {
|
||||
t.Fatalf("提醒日志里出现了凭据原文:%s", out)
|
||||
}
|
||||
for _, want := range []string{"reminder fired", "[redacted]"} {
|
||||
if !strings.Contains(out, want) {
|
||||
t.Fatalf("提醒日志 = %q,want 含 %q(打码不能把日志变哑)", out, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user