Files
weichenandCursor bed0dbf076
ci / vet-and-test (push) Successful in 4s
ci / docker-checkout-hostgateway (push) Successful in 2s
Add a structured logger and collector so runs can be traced across processes.
Sync and async sinks, redaction, and a queryable HTTP service keep concurrent logs tied to a trace id.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-29 13:51:16 +00:00

57 lines
1.6 KiB
Go

// Package redact 在日志落盘前去掉常见秘密形状。
// 规则来自 agent-squad 的事件脱敏,并允许追加自定义正则。
package redact
import (
"fmt"
"regexp"
"strings"
)
// Redactor 替换秘密子串。宁可多脱,不漏已识别的形状。
type Redactor struct {
extra []*regexp.Regexp
}
var (
reJWT = regexp.MustCompile(`eyJ[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}`)
reSKKey = regexp.MustCompile(`(?:sk|rk|pk)-[A-Za-z0-9_\-]{12,}`)
reBearer = regexp.MustCompile(`(?i)bearer\s+[A-Za-z0-9._\-]{12,}`)
reAPIKey = regexp.MustCompile(`(?i)(api[_-]?key["':=\s]+)[A-Za-z0-9_\-\.]{12,}`)
rePass = regexp.MustCompile(`(?i)(password["':=\s]+)[^\s"',}]+`)
)
// New 编译额外正则。任一非法则失败,不返回半成品。
func New(extra []string) (*Redactor, error) {
compiled := make([]*regexp.Regexp, 0, len(extra))
for _, pattern := range extra {
if strings.TrimSpace(pattern) == "" {
continue
}
re, err := regexp.Compile(pattern)
if err != nil {
return nil, fmt.Errorf("redact: pattern %q: %w", pattern, err)
}
compiled = append(compiled, re)
}
return &Redactor{extra: compiled}, nil
}
// Apply 脱敏一段文本。
func (r *Redactor) Apply(s string) string {
if r == nil || s == "" {
return s
}
s = reJWT.ReplaceAllString(s, "***")
s = reSKKey.ReplaceAllString(s, "***")
s = reBearer.ReplaceAllString(s, "Bearer ***")
s = reAPIKey.ReplaceAllString(s, "${1}***")
if strings.Contains(strings.ToLower(s), "password") {
s = rePass.ReplaceAllString(s, "${1}***")
}
for _, re := range r.extra {
s = re.ReplaceAllString(s, "***")
}
return s
}