Smail/test/backend/webauthn_config_test.go
smail f530ab0078
Some checks are pending
Security Checks / security (push) Waiting to run
修复通行密钥默认配置启动并补齐迁移校验
2026-09-06 22:39:07 +08:00

86 lines
3.3 KiB
Go

package config
import (
"reflect"
"testing"
)
func TestWebAuthnDefaultsUseConfiguredSiteOnly(t *testing.T) {
t.Setenv("SMAIL_HTTPS_PORT", "")
for _, item := range []struct {
name string
http HTTPConfig
want string
}{
{"legacy", HTTPConfig{}, "https://mail.example.com"},
{"direct", HTTPConfig{HTTPSPort: 9443, HTTPSEnabled: true}, "https://mail.example.com:9443"},
{"proxy", HTTPConfig{Port: 8080, TLSMode: HTTPSTLSModeProxy}, "https://mail.example.com"},
} {
t.Run(item.name, func(t *testing.T) {
root := RootConfig{Domains: DomainConfig{Web: "mail.example.com", AccessHosts: []string{"untrusted.example.net"}}, HTTP: item.http}
got, err := ResolveWebAuthnSecurity(root)
if err != nil {
t.Fatal(err)
}
if got.WebAuthnRPID != "mail.example.com" || got.WebAuthnRPDisplayName != "Smail" || !reflect.DeepEqual(got.WebAuthnRPOrigins, []string{item.want}) {
t.Fatalf("站点默认值错误:%+v", got)
}
if root.Security.WebAuthnRPID != "" || len(root.Security.WebAuthnRPOrigins) != 0 {
t.Fatal("解析默认值改写了原始配置")
}
})
}
}
func TestWebAuthnExplicitConfigurationIsPreserved(t *testing.T) {
root := RootConfig{Domains: DomainConfig{Web: "mail.example.com"}, Security: SecurityConfig{
WebAuthnRPID: "example.com", WebAuthnRPDisplayName: "企业邮箱", WebAuthnRPOrigins: []string{"https://mail.example.com:8443"},
}}
got, err := ResolveWebAuthnSecurity(root)
if err != nil || !reflect.DeepEqual(got, root.Security) {
t.Fatalf("显式配置被覆盖:%v", err)
}
got.WebAuthnRPOrigins[0] = "https://changed.example.com"
if root.Security.WebAuthnRPOrigins[0] != "https://mail.example.com:8443" {
t.Fatal("解析结果与原始配置共用来源切片")
}
}
func TestWebAuthnRejectsUnsafeConfiguredOrigins(t *testing.T) {
for _, origin := range []string{
"http://mail.example.com", "https://attacker.example.net", "https://badexample.com",
"https://mail.example.com/path", "https://mail.example.com?", "https://mail.example.com#",
"https://user@mail.example.com", "https://mail.example.com:65536", "https://*.example.com", "",
"https://mail.example.com:", "https://mail.example.com:0", "https://mail.example.com:invalid",
} {
t.Run(origin, func(t *testing.T) {
root := RootConfig{Domains: DomainConfig{Web: "mail.example.com"}, Security: SecurityConfig{
WebAuthnRPID: "example.com", WebAuthnRPOrigins: []string{origin},
}}
if _, err := ResolveWebAuthnSecurity(root); err == nil {
t.Fatal("不安全的显式来源未被拒绝")
}
})
}
if _, err := ResolveWebAuthnSecurity(RootConfig{}); err == nil {
t.Fatal("缺少站点配置时不应猜测信赖方")
}
}
func TestWebAuthnDefaultsRespectExplicitLocalDevelopmentOrigin(t *testing.T) {
root := RootConfig{Domains: DomainConfig{Web: "localhost"}, Security: SecurityConfig{
WebAuthnRPOrigins: []string{"http://localhost:15173"},
}}
if _, err := ResolveWebAuthnSecurity(root); err != nil {
t.Fatalf("显式 localhost 来源被拒绝:%v", err)
}
}
func TestRuntimeValidationRejectsInvalidWebAuthnBeforeSaving(t *testing.T) {
runtime := validRuntimeConfig()
runtime.Root.Security.WebAuthnRPOrigins = []string{"https://attacker.example.net"}
if err := ValidateRuntimeConfig(runtime, RuntimeValidationOptions{HasSuperAdmin: true, Production: true}); err == nil {
t.Fatal("候选配置接受了不属于站点信赖方的来源")
}
}