Smail/server/config/sensitive_test.go
smail 18b6dfbdf7
Some checks are pending
Security Checks / security (push) Waiting to run
完成安全整改与配置系统重构阶段开发
2026-08-28 00:42:28 +08:00

106 lines
3.6 KiB
Go

package config
import "testing"
func TestSensitiveFieldCatalog(t *testing.T) {
fields := SensitiveFields()
if len(fields) < 30 {
t.Fatalf("sensitive field catalog is incomplete: %d", len(fields))
}
seen := map[string]struct{}{}
for _, field := range fields {
if _, ok := seen[field.Path]; ok {
t.Fatalf("duplicate sensitive field: %s", field.Path)
}
seen[field.Path] = struct{}{}
switch field.Policy {
case PersistenceEncryptedOnly, PersistenceReferenceOnly, PersistenceMemoryOnly, PersistenceHashedOnly:
default:
t.Fatalf("invalid policy for %s", field.Path)
}
if field.Description == "" {
t.Fatalf("missing description for %s", field.Path)
}
if CanGenericSerialize(field.Path) {
t.Fatalf("sensitive field can be generically serialized: %s", field.Path)
}
}
}
func TestSensitiveWildcard(t *testing.T) {
field, ok := ClassifySensitive("plugins.telegram.token")
if !ok || field.Policy != PersistenceEncryptedOnly {
t.Fatal("plugin token was not classified")
}
if _, ok := ClassifySensitive("plugins.telegram.display_name"); ok {
t.Fatal("non-sensitive plugin field was classified")
}
}
func TestSensitivePersistenceRules(t *testing.T) {
tests := []struct {
path string
encrypted bool
want bool
}{
{path: "database.password", encrypted: false, want: false},
{path: "database.password", encrypted: true, want: true},
{path: "ssl.private_key", encrypted: true, want: false},
{path: "verification.code", encrypted: true, want: false},
{path: "verification.code_hmac", encrypted: true, want: false},
{path: "verification.process_key", encrypted: true, want: false},
{path: "public.display_name", encrypted: false, want: true},
{path: "verification.account_fingerprint", encrypted: true, want: false},
}
for _, test := range tests {
if got := CanPersist(test.path, test.encrypted); got != test.want {
t.Fatalf("CanPersist(%q, %t) = %t, want %t", test.path, test.encrypted, got, test.want)
}
}
}
func TestSensitiveRepresentations(t *testing.T) {
tests := []struct {
path string
representation string
want bool
}{
{path: "database.password", representation: "encrypted", want: true},
{path: "database.password", representation: "hash", want: false},
{path: "verification.code", representation: "hash", want: false},
{path: "verification.code", representation: "memory", want: true},
{path: "verification.email_fingerprint", representation: "hash", want: true},
{path: "ssl.private_key", representation: "reference", want: true},
}
for _, test := range tests {
if got := CanPersistRepresentation(test.path, test.representation); got != test.want {
t.Fatalf("CanPersistRepresentation(%q, %q) = %t, want %t", test.path, test.representation, got, test.want)
}
}
}
func TestRedactPublicValue(t *testing.T) {
value := map[string]any{
"database": map[string]any{
"password": map[string]any{"ciphertext": "enc-secret"},
"name": "smail",
},
"verification": map[string]any{
"code": "12345678",
"email_fingerprint": "fingerprint",
},
}
redacted, ok := RedactPublicValue(value).(map[string]any)
if !ok {
t.Fatalf("unexpected redacted value type: %T", RedactPublicValue(value))
}
database := redacted["database"].(map[string]any)
if database["password"] != RedactedValue || database["name"] != "smail" {
t.Fatalf("database public view was not redacted: %#v", database)
}
verification := redacted["verification"].(map[string]any)
if verification["code"] != RedactedValue || verification["email_fingerprint"] != RedactedValue {
t.Fatalf("verification public view was not redacted: %#v", verification)
}
}