191 lines
4.7 KiB
Go
191 lines
4.7 KiB
Go
package logx
|
|
|
|
import (
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
func TestMaskAPIKeys(t *testing.T) {
|
|
for _, input := range []string{"X-API-Key: fictional-secret", "api_key=fictional-secret", `{"apiKey":"fictional-secret"}`} {
|
|
if strings.Contains(Mask(input), "fictional-secret") {
|
|
t.Fatal("API Key escaped log masking")
|
|
}
|
|
}
|
|
}
|
|
|
|
// 这是本包最重要的测试:敏感内容绝不能出现在日志里。
|
|
// 每新增一条脱敏规则,都要在这里补一个用例。
|
|
func TestMaskHidesSecrets(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
in string
|
|
// leak 是绝对不能出现在结果里的片段
|
|
leak string
|
|
}{
|
|
{
|
|
name: "淘宝 MTOP token",
|
|
in: "cookie 里 _m_h5_tk=abc123def456_1699999999999 已读取",
|
|
leak: "abc123def456",
|
|
},
|
|
{
|
|
name: "淘宝 token enc",
|
|
in: "_m_h5_tk_enc=zzz999yyy888",
|
|
leak: "zzz999yyy888",
|
|
},
|
|
{
|
|
name: "淘宝 tb_token",
|
|
in: "_tb_token_=e3b0c44298fc; path=/",
|
|
leak: "e3b0c44298fc",
|
|
},
|
|
{
|
|
name: "Authorization 头",
|
|
in: "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.fake.token",
|
|
leak: "eyJhbGciOiJIUzI1NiJ9",
|
|
},
|
|
{
|
|
name: "Cookie 请求头",
|
|
in: "Cookie: a=1; b=2; secret=3",
|
|
leak: "secret=3",
|
|
},
|
|
{
|
|
name: "JSON 里的密码",
|
|
in: `{"account":"13500000000","password":"不该出现的密码"}`,
|
|
leak: "不该出现的密码",
|
|
},
|
|
{
|
|
name: "等号形式的密码",
|
|
in: "password=hunter2&next=1",
|
|
leak: "hunter2",
|
|
},
|
|
{
|
|
name: "中文冒号密码",
|
|
in: "登录失败,密码:明文密码值",
|
|
leak: "明文密码值",
|
|
},
|
|
{
|
|
name: "图片 base64",
|
|
in: "上传 data:image/jpeg;base64,AAAABBBBCCCCDDDD 完成",
|
|
leak: "AAAABBBBCCCCDDDD",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := Mask(tc.in)
|
|
if strings.Contains(got, tc.leak) {
|
|
t.Fatalf("脱敏后仍然泄漏了 %q\n原文:%s\n结果:%s", tc.leak, tc.in, got)
|
|
}
|
|
if !strings.Contains(got, "***") {
|
|
t.Fatalf("应当出现 *** 占位符,结果是:%s", got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// 普通文本不应该被误伤。
|
|
func TestMaskKeepsNormalText(t *testing.T) {
|
|
in := "[3/6] 商品 40571188442 图搜返回 48 个同款,取前 20 个"
|
|
if got := Mask(in); got != in {
|
|
t.Fatalf("普通文本不应被改动\n原文:%s\n结果:%s", in, got)
|
|
}
|
|
}
|
|
|
|
// 写日志时也必须脱敏,不能只有直接调用 Mask 才生效。
|
|
func TestLoggerMasksOnWrite(t *testing.T) {
|
|
l := New(10)
|
|
l.Info("登录请求 password=真实密码")
|
|
|
|
entries := l.Entries()
|
|
if len(entries) != 1 {
|
|
t.Fatalf("应当有 1 条日志,实际 %d 条", len(entries))
|
|
}
|
|
if strings.Contains(entries[0].Message, "真实密码") {
|
|
t.Fatalf("日志里泄漏了密码:%s", entries[0].Message)
|
|
}
|
|
}
|
|
|
|
// 超过上限时要丢掉最旧的,防止长时间任务把内存吃光。
|
|
func TestLoggerRespectsLimit(t *testing.T) {
|
|
l := New(3)
|
|
for i := 0; i < 10; i++ {
|
|
l.Info("第 %d 条", i)
|
|
}
|
|
|
|
entries := l.Entries()
|
|
if len(entries) != 3 {
|
|
t.Fatalf("应当只保留 3 条,实际 %d 条", len(entries))
|
|
}
|
|
// 保留的应当是最后 3 条。
|
|
if !strings.Contains(entries[0].Message, "第 7 条") {
|
|
t.Fatalf("应当保留最新的日志,第一条却是:%s", entries[0].Message)
|
|
}
|
|
}
|
|
|
|
// 下载任务会有多个协程同时写日志,不能崩。
|
|
func TestLoggerIsConcurrentSafe(t *testing.T) {
|
|
l := New(1000)
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 20; i++ {
|
|
wg.Add(1)
|
|
go func(n int) {
|
|
defer wg.Done()
|
|
for j := 0; j < 50; j++ {
|
|
l.Info("协程 %d 第 %d 条", n, j)
|
|
}
|
|
}(i)
|
|
}
|
|
wg.Wait()
|
|
|
|
if len(l.Entries()) != 1000 {
|
|
t.Fatalf("应当有 1000 条日志,实际 %d 条", len(l.Entries()))
|
|
}
|
|
}
|
|
|
|
func TestLoggerLevels(t *testing.T) {
|
|
l := New(10)
|
|
l.Info("信息")
|
|
l.Success("成功")
|
|
l.Warn("警告")
|
|
l.Error("失败")
|
|
|
|
want := []Level{LevelInfo, LevelSuccess, LevelWarn, LevelError}
|
|
entries := l.Entries()
|
|
for i, level := range want {
|
|
if entries[i].Level != level {
|
|
t.Fatalf("第 %d 条级别应当是 %s,实际 %s", i, level, entries[i].Level)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLoggerClearAndText(t *testing.T) {
|
|
l := New(10)
|
|
l.Info("第一条")
|
|
l.Info("第二条")
|
|
|
|
text := l.Text()
|
|
if !strings.Contains(text, "第一条") || !strings.Contains(text, "第二条") {
|
|
t.Fatalf("导出文本应当包含全部日志,实际:%s", text)
|
|
}
|
|
|
|
l.Clear()
|
|
if len(l.Entries()) != 0 {
|
|
t.Fatalf("清空后不应还有日志")
|
|
}
|
|
}
|
|
|
|
// 日志写入后要能推给前端。
|
|
func TestLoggerHandler(t *testing.T) {
|
|
l := New(10)
|
|
var got []Entry
|
|
l.SetHandler(func(e Entry) { got = append(got, e) })
|
|
|
|
l.Info("一条日志")
|
|
if len(got) != 1 {
|
|
t.Fatalf("回调应当被调用 1 次,实际 %d 次", len(got))
|
|
}
|
|
if got[0].Message != "一条日志" {
|
|
t.Fatalf("回调收到的内容不对:%s", got[0].Message)
|
|
}
|
|
}
|