Some checks are pending
Security Checks / security (push) Waiting to run
统一 frontend、backend、test、test-log 和 out 目录,更新构建、部署及测试入口;纳入配置管理兼容与首次安全引导修复;排除本机配置、密钥和测试产物。
71 lines
2.6 KiB
Go
71 lines
2.6 KiB
Go
package config
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestFileAuditWriterPersistsOnlyRedactedMetadata(t *testing.T) {
|
|
root := t.TempDir()
|
|
writer, err := NewFileAuditWriter(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
event := ConfigAuditEvent{
|
|
Operation: ConfigOperationUpdate, Section: SectionSMTP, ActorID: "user-1", ActorRole: "super_admin",
|
|
FromGeneration: 1, ToGeneration: 2, ChangedFields: []string{"relay.password"},
|
|
ConfirmationMethod: "recent_auth", Result: "success", CreatedAt: time.Now().UTC(),
|
|
}
|
|
if err := writer.WriteConfigAudit(context.Background(), event); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
data, err := os.ReadFile(writer.path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(data), "plaintext") || strings.Contains(string(data), "password-value") {
|
|
t.Fatal("audit file contains secret-like plaintext")
|
|
}
|
|
items, err := writer.Query(context.Background(), ConfigAuditQuery{ActorID: "user-1"})
|
|
if err != nil || len(items) != 1 || items[0].ChangedFields[0] != "relay.password" {
|
|
t.Fatalf("query result = %+v, error = %v", items, err)
|
|
}
|
|
jsonData, err := writer.ExportJSON(context.Background(), ConfigAuditQuery{})
|
|
if err != nil || !strings.Contains(string(jsonData), "relay.password") {
|
|
t.Fatalf("json export = %s, error = %v", jsonData, err)
|
|
}
|
|
csvData, err := writer.ExportCSV(context.Background(), ConfigAuditQuery{})
|
|
if err != nil || !strings.Contains(string(csvData), "changed_fields") {
|
|
t.Fatalf("csv export = %s, error = %v", csvData, err)
|
|
}
|
|
}
|
|
|
|
func TestFileAuditWriterCleanupKeepsRecentAndDeletesExpiredEvents(t *testing.T) {
|
|
root := t.TempDir()
|
|
writer, err := NewFileAuditWriter(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
now := time.Date(2026, 8, 26, 0, 0, 0, 0, time.UTC)
|
|
for index, createdAt := range []time.Time{now.Add(-ConfigAuditRetention - time.Nanosecond), now.Add(-ConfigAuditRetention + time.Nanosecond), now} {
|
|
event := ConfigAuditEvent{
|
|
Operation: ConfigOperationUpdate, Section: SectionEmail, ActorID: "user-" + string(rune('1'+index)), ActorRole: "super_admin",
|
|
FromGeneration: uint64(index + 1), ToGeneration: uint64(index + 2), Result: "success", CreatedAt: createdAt,
|
|
}
|
|
if err := writer.WriteConfigAudit(context.Background(), event); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
deleted, err := writer.Cleanup(context.Background(), now)
|
|
if err != nil || deleted != 1 {
|
|
t.Fatalf("cleanup deleted = %d, error = %v", deleted, err)
|
|
}
|
|
items, err := writer.Query(context.Background(), ConfigAuditQuery{})
|
|
if err != nil || len(items) != 2 {
|
|
t.Fatalf("remaining events = %d, error = %v", len(items), err)
|
|
}
|
|
}
|