дȫÏîĿÉó²éÎÊÌⲢ·¢²¼ v1.1.9
¸ÄÓñ¾»ú¶ÀÁ¢ CA£¬»ָ´ CLI TLS УÑ飬±£»¤ƾ¾ÝÓëÅäÖò¢·¢±£´档 д Cursor Êý¾ݡ¢»Ự·¾¶¡¢´úÀíÉúÃüÖÜÆڡ¢ģÐÍÇëÇóÓë Skills Îļþ²Ù×÷¡£ Ϊ¶ÀÁ¢ Tab ·þÎñÔö¼ӿͻ§¶˼øȨ£¬Éý¼¶ÒÀÀµÓ빤¾ßÁ´£¬ÍêÉƷ¢²¼ԪÐÅϢͬ²½¡£ Ñé֤£ºÁ½¸ö Go ģ¿éµIJâÊÔ/vet¡¢¹ؼüģ¿é race¡¢ǰ¶˲âÊÔ/Éú²ú¹¹½¨/Éó¼ơ¢ Go ¿ɴï©¶´ɨÃèÓë macOS/Linux ¿ÉÐÐģ¿齻²æ±àÒë¾ùͨ¹ý¡£ Windows ¸ôÀëÆô¶¯ͨ¹ý£»δÍê³ɵĵã»÷ðÑ̺ÍԭÉúƽ̨Ñé֤¼ûÉó²鱨¸档
This commit is contained in:
85
internal/logger/redact.go
Normal file
85
internal/logger/redact.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var secretRegistry = struct {
|
||||
sync.RWMutex
|
||||
values map[string]struct{}
|
||||
}{values: map[string]struct{}{}}
|
||||
var bearerPattern = regexp.MustCompile(`(?i)Bearer\s+[^\s"'<>]+`)
|
||||
var credentialPattern = regexp.MustCompile(`(?i)((?:api[_-]?key|token|authorization|cookie|password|secret)["']?\s*[:=]\s*["']?)[^"'\s,;}]+`)
|
||||
|
||||
func RegisterSecrets(values ...string) {
|
||||
secretRegistry.Lock()
|
||||
defer secretRegistry.Unlock()
|
||||
for _, v := range values {
|
||||
if v != "" {
|
||||
secretRegistry.values[v] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
func redact(s string) string {
|
||||
secretRegistry.RLock()
|
||||
values := make([]string, 0, len(secretRegistry.values))
|
||||
for value := range secretRegistry.values {
|
||||
values = append(values, value)
|
||||
}
|
||||
secretRegistry.RUnlock()
|
||||
sort.Slice(values, func(i, j int) bool { return len(values[i]) > len(values[j]) })
|
||||
for _, value := range values {
|
||||
s = strings.ReplaceAll(s, value, "[redacted]")
|
||||
}
|
||||
s = bearerPattern.ReplaceAllString(s, "Bearer [redacted]")
|
||||
return credentialPattern.ReplaceAllString(s, "${1}[redacted]")
|
||||
}
|
||||
|
||||
type redactingHandler struct{ next slog.Handler }
|
||||
|
||||
func (h redactingHandler) Enabled(ctx context.Context, level slog.Level) bool {
|
||||
return h.next.Enabled(ctx, level)
|
||||
}
|
||||
func redactAttr(a slog.Attr) slog.Attr {
|
||||
key := strings.ToLower(a.Key)
|
||||
for _, part := range []string{"apikey", "api_key", "token", "password", "secret", "authorization", "cookie", "payload", "headers", "body", "config"} {
|
||||
if strings.Contains(key, part) {
|
||||
return slog.String(a.Key, "[redacted]")
|
||||
}
|
||||
}
|
||||
v := a.Value.Resolve()
|
||||
switch v.Kind() {
|
||||
case slog.KindString:
|
||||
a.Value = slog.StringValue(redact(v.String()))
|
||||
case slog.KindAny:
|
||||
a.Value = slog.StringValue(redact(fmt.Sprint(v.Any())))
|
||||
case slog.KindGroup:
|
||||
attrs := v.Group()
|
||||
for i := range attrs {
|
||||
attrs[i] = redactAttr(attrs[i])
|
||||
}
|
||||
a.Value = slog.GroupValue(attrs...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
func (h redactingHandler) Handle(ctx context.Context, r slog.Record) error {
|
||||
next := slog.NewRecord(r.Time, r.Level, redact(r.Message), r.PC)
|
||||
r.Attrs(func(a slog.Attr) bool { next.AddAttrs(redactAttr(a)); return true })
|
||||
return h.next.Handle(ctx, next)
|
||||
}
|
||||
func (h redactingHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
copy := append([]slog.Attr(nil), attrs...)
|
||||
for i := range copy {
|
||||
copy[i] = redactAttr(copy[i])
|
||||
}
|
||||
return redactingHandler{h.next.WithAttrs(copy)}
|
||||
}
|
||||
func (h redactingHandler) WithGroup(name string) slog.Handler {
|
||||
return redactingHandler{h.next.WithGroup(name)}
|
||||
}
|
||||
Reference in New Issue
Block a user