Files
MyGoNavi/internal/app/daily_secret_persistence.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

108 lines
3.3 KiB
Go

package app
import (
stdRuntime "runtime"
"GoNavi-Wails/internal/connection"
"GoNavi-Wails/internal/dailysecret"
)
var runtimeGOOS = func() string {
return stdRuntime.GOOS
}
func extractConnectionSecretBundle(config connection.ConnectionConfig) connectionSecretBundle {
return connectionSecretBundle{
Password: config.Password,
SSHPassword: config.SSH.Password,
ProxyPassword: config.Proxy.Password,
HTTPTunnelPassword: config.HTTPTunnel.Password,
MySQLReplicaPassword: config.MySQLReplicaPassword,
MongoReplicaPassword: config.MongoReplicaPassword,
OpaqueURI: config.URI,
OpaqueDSN: config.DSN,
}
}
func toDailyConnectionBundle(bundle connectionSecretBundle) dailysecret.ConnectionBundle {
return dailysecret.ConnectionBundle{
Password: bundle.Password,
SSHPassword: bundle.SSHPassword,
ProxyPassword: bundle.ProxyPassword,
HTTPTunnelPassword: bundle.HTTPTunnelPassword,
MySQLReplicaPassword: bundle.MySQLReplicaPassword,
MongoReplicaPassword: bundle.MongoReplicaPassword,
OpaqueURI: bundle.OpaqueURI,
OpaqueDSN: bundle.OpaqueDSN,
}
}
func fromDailyConnectionBundle(bundle dailysecret.ConnectionBundle) connectionSecretBundle {
return connectionSecretBundle{
Password: bundle.Password,
SSHPassword: bundle.SSHPassword,
ProxyPassword: bundle.ProxyPassword,
HTTPTunnelPassword: bundle.HTTPTunnelPassword,
MySQLReplicaPassword: bundle.MySQLReplicaPassword,
MongoReplicaPassword: bundle.MongoReplicaPassword,
OpaqueURI: bundle.OpaqueURI,
OpaqueDSN: bundle.OpaqueDSN,
}
}
func stripConnectionSecretFields(config connection.ConnectionConfig) connection.ConnectionConfig {
stripped := config
stripped.Password = ""
stripped.SSH.Password = ""
stripped.Proxy.Password = ""
stripped.HTTPTunnel.Password = ""
stripped.MySQLReplicaPassword = ""
stripped.MongoReplicaPassword = ""
stripped.URI = ""
stripped.DSN = ""
return stripped
}
func sanitizeSavedConnectionView(view connection.SavedConnectionView) connection.SavedConnectionView {
view.Config = stripConnectionSecretFields(view.Config)
return view
}
func sanitizeSavedConnectionViews(items []connection.SavedConnectionView) []connection.SavedConnectionView {
if len(items) == 0 {
return items
}
result := make([]connection.SavedConnectionView, 0, len(items))
for _, item := range items {
result = append(result, sanitizeSavedConnectionView(item))
}
return result
}
func extractGlobalProxySecretBundle(view connection.GlobalProxyView) globalProxySecretBundle {
return globalProxySecretBundle{
Password: view.Password,
}
}
func toDailyGlobalProxyBundle(bundle globalProxySecretBundle) dailysecret.GlobalProxyBundle {
return dailysecret.GlobalProxyBundle{Password: bundle.Password}
}
func fromDailyGlobalProxyBundle(bundle dailysecret.GlobalProxyBundle) globalProxySecretBundle {
return globalProxySecretBundle{Password: bundle.Password}
}
func sanitizeGlobalProxyView(view connection.GlobalProxyView) connection.GlobalProxyView {
view.Password = ""
return view
}
func shouldReadLegacySecretStoreForDailySecrets() bool {
return runtimeGOOS() != "darwin"
}
func (a *App) dailySecretStore() *dailysecret.Store {
return dailysecret.NewStore(a.configDir)
}