Files
MyGoNavi/internal/app/connection_secret_resolution_test.go
Syngnat c7cf9526de 🐛 fix(security): 修复 macOS 无法打开应用及三平台依赖系统钥匙串的问题
- 密文存储:新增 dailysecret 本地存储引擎,连接/代理/AI 密钥不再依赖系统钥匙串
- 启动迁移:自动将已有钥匙串密文迁移到本地 JSON,用户无感知
- WebKit 迁移:从旧版 Wails WebKit LocalStorage 中恢复连接与代理数据
- DMG 修复:移除 --sandbox-safe 避免扩展属性污染签名,新增 xattr 清理与签名校验
- 安全适配:钥匙串不可用时标记完成而非回滚,消除无钥匙串环境下的阻塞
- 出口脱敏:所有连接/代理 API 返回前统一 sanitize 防止密文泄漏
2026-04-13 12:40:25 +08:00

179 lines
5.3 KiB
Go

package app
import (
"strings"
"testing"
"GoNavi-Wails/internal/connection"
)
func TestResolveConnectionConfigByIDLoadsSecretsFromStore(t *testing.T) {
store := newFakeAppSecretStore()
app := NewAppWithSecretStore(store)
app.configDir = t.TempDir()
repo := newSavedConnectionRepository(app.configDir, store)
view, err := repo.Save(connection.SavedConnectionInput{
ID: "conn-1",
Name: "Primary",
Config: connection.ConnectionConfig{
ID: "conn-1",
Type: "postgres",
Host: "db.local",
Port: 5432,
User: "postgres",
Password: "postgres-secret",
DSN: "postgres://user:pass@db.local/app",
},
})
if err != nil {
t.Fatalf("Save returned error: %v", err)
}
resolved, err := app.resolveConnectionSecrets(view.Config)
if err != nil {
t.Fatalf("resolveConnectionSecrets returned error: %v", err)
}
if resolved.Password != "postgres-secret" {
t.Fatalf("expected restored password, got %q", resolved.Password)
}
if resolved.DSN != "postgres://user:pass@db.local/app" {
t.Fatalf("expected restored DSN, got %q", resolved.DSN)
}
}
func TestResolveConnectionSecretsOnDarwinUsesInlineSavedSecrets(t *testing.T) {
app := NewAppWithSecretStore(failOnUseSecretStore{})
app.configDir = t.TempDir()
if _, err := app.SaveConnection(connection.SavedConnectionInput{
ID: "conn-darwin-inline",
Name: "Primary",
Config: connection.ConnectionConfig{
ID: "conn-darwin-inline",
Type: "postgres",
Host: "db.local",
Port: 5432,
User: "postgres",
Password: "postgres-secret",
DSN: "postgres://user:pass@db.local/app",
},
}); err != nil {
t.Fatalf("SaveConnection returned error: %v", err)
}
resolved, err := app.resolveConnectionSecrets(connection.ConnectionConfig{
ID: "conn-darwin-inline",
Type: "postgres",
Host: "db.local",
Port: 5432,
User: "postgres",
})
if err != nil {
t.Fatalf("resolveConnectionSecrets returned error: %v", err)
}
if resolved.Password != "postgres-secret" {
t.Fatalf("expected daily-stored password to be restored, got %q", resolved.Password)
}
if resolved.DSN != "postgres://user:pass@db.local/app" {
t.Fatalf("expected daily-stored DSN to be restored, got %q", resolved.DSN)
}
}
func TestResolveConnectionSecretsReturnsFriendlyMessageWhenSavedSecretSourceIsMissing(t *testing.T) {
store := newFakeAppSecretStore()
app := NewAppWithSecretStore(store)
app.configDir = t.TempDir()
_, err := app.resolveConnectionSecrets(connection.ConnectionConfig{
ID: "conn-missing",
Type: "postgres",
Host: "db.local",
Port: 5432,
User: "postgres",
})
if err == nil {
t.Fatal("expected resolveConnectionSecrets to fail for a missing saved connection")
}
if !strings.Contains(err.Error(), "已保存密文") {
t.Fatalf("expected a secret-specific error message, got %q", err.Error())
}
}
func TestResolveConnectionSecretsFallsBackToInlineSecretsWhenSavedConnectionIsMissing(t *testing.T) {
store := newFakeAppSecretStore()
app := NewAppWithSecretStore(store)
app.configDir = t.TempDir()
input := connection.ConnectionConfig{
ID: "legacy-inline",
Type: "postgres",
Host: "db.local",
Port: 5432,
User: "postgres",
Password: "inline-secret",
DSN: "postgres://postgres:inline-secret@db.local/app",
}
resolved, err := app.resolveConnectionSecrets(input)
if err != nil {
t.Fatalf("expected inline secrets to be used as fallback, got error: %v", err)
}
if resolved.Password != "inline-secret" {
t.Fatalf("expected inline password to be preserved, got %q", resolved.Password)
}
if resolved.DSN != "postgres://postgres:inline-secret@db.local/app" {
t.Fatalf("expected inline DSN to be preserved, got %q", resolved.DSN)
}
}
func TestResolveConnectionSecretsFallsBackToInlineSecretsWhenSavedSecretBundleIsMissing(t *testing.T) {
withTestGOOS(t, "linux")
store := newFakeAppSecretStore()
app := NewAppWithSecretStore(store)
app.configDir = t.TempDir()
view, err := app.SaveConnection(connection.SavedConnectionInput{
ID: "conn-inline-fallback",
Name: "Primary",
Config: connection.ConnectionConfig{
ID: "conn-inline-fallback",
Type: "postgres",
Host: "db.local",
Port: 5432,
User: "postgres",
Password: "stored-secret",
DSN: "postgres://postgres:stored-secret@db.local/app",
},
})
if err != nil {
t.Fatalf("SaveConnection returned error: %v", err)
}
if view.SecretRef != "" {
t.Fatalf("expected saved connection to avoid secret refs, got %q", view.SecretRef)
}
if err := app.dailySecretStore().DeleteConnection("conn-inline-fallback"); err != nil {
t.Fatalf("DeleteConnection returned error: %v", err)
}
resolved, err := app.resolveConnectionSecrets(connection.ConnectionConfig{
ID: "conn-inline-fallback",
Type: "postgres",
Host: "db.local",
Port: 5432,
User: "postgres",
Password: "inline-secret",
DSN: "postgres://postgres:inline-secret@db.local/app",
})
if err != nil {
t.Fatalf("expected inline secrets to be used when secret bundle is missing, got error: %v", err)
}
if resolved.Password != "inline-secret" {
t.Fatalf("expected inline password to be preserved, got %q", resolved.Password)
}
if resolved.DSN != "postgres://postgres:inline-secret@db.local/app" {
t.Fatalf("expected inline DSN to be preserved, got %q", resolved.DSN)
}
}