mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-11 01:03:51 +08:00
- SQL 导出:formatSQLValue 按方言转义反斜杠,修复 MySQL 系 dump 还原时静默改写数据、 且以反斜杠结尾的值吞掉闭合引号致源库恶意行可执行任意 SQL 的问题;非 MySQL 方言保持原样 - 明文凭据:daily_secrets.json 由 0o644 改为 0o600、目录改 0o700,并对历史文件显式 Chmod - SSH 隧道:RegisterSSHNetwork 改为确定性 network 名并复用缓存客户端,消除驱动全局 dialer 表随重连线性增长、永久钉住 ssh.Client 的连接与 goroutine 泄漏 - xlsx 导入:单元格 r 属性列号增加 OOXML 16384 上限并提前熔断,避免篡改文件放大分配致 OOM - 数据导出:三处非 SQL 导出入口捕获 file.Close 错误,不再在落盘失败时返回“导出成功” - 数据根迁移:copyFile 捕获 Close 错误并补 Sync,避免被截断的副本被判定为迁移成功 - 执行计划:节点 ID 改为按 ExplainResult 派生,移除进程级全局计数器与 reset, 修复并发诊断互相踩踏编号导致的重复 node ID 与错挂父子边 - 补充 5 个回归测试文件,含“导出→切分”闭环断言与未转义时的反向对照
62 lines
2.7 KiB
Go
62 lines
2.7 KiB
Go
package ssh
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
|
||
"GoNavi-Wails/internal/connection"
|
||
)
|
||
|
||
// TestSSHNetworkNameIsDeterministicPerTarget 覆盖 go-sql-driver 自定义 network 名的确定性。
|
||
//
|
||
// 回归背景:netName 原先是 fmt.Sprintf("ssh_%s_%d", host, time.Now().UnixNano())。
|
||
// mysql.RegisterDialContext 写入驱动内一张永不回收的全局 map(DeregisterDialContext 在本
|
||
// 仓库无任何调用点),因此每次(重)连接都会新增一条永久条目并钉住其闭包捕获的 ssh.Client,
|
||
// 形成随重连线性增长的 SSH 连接与 goroutine 泄漏。改为按目标确定性派生后,map 大小收敛为
|
||
// SSH 目标个数。
|
||
func TestSSHNetworkNameIsDeterministicPerTarget(t *testing.T) {
|
||
config := connection.SSHConfig{Host: "jump.example.com", Port: 22, User: "ops", Password: "s3cret"}
|
||
key := newSSHClientCacheKey(config)
|
||
|
||
first := sshNetworkName(key)
|
||
second := sshNetworkName(newSSHClientCacheKey(config))
|
||
if first != second {
|
||
t.Fatalf("同一目标两次派生出不同 network 名:%s vs %s(会导致驱动全局 map 无界增长)", first, second)
|
||
}
|
||
if !strings.HasPrefix(first, "ssh_") {
|
||
t.Errorf("network 名 %q 缺少 ssh_ 前缀", first)
|
||
}
|
||
}
|
||
|
||
// TestSSHNetworkNameDistinguishesTargets 不同目标必须得到不同 network 名,
|
||
// 否则两条连接会共用同一个 dialer,把流量打到错误的跳板机。
|
||
func TestSSHNetworkNameDistinguishesTargets(t *testing.T) {
|
||
base := connection.SSHConfig{Host: "jump.example.com", Port: 22, User: "ops", Password: "s3cret"}
|
||
variants := map[string]connection.SSHConfig{
|
||
"不同 host": {Host: "other.example.com", Port: 22, User: "ops", Password: "s3cret"},
|
||
"不同 port": {Host: "jump.example.com", Port: 2222, User: "ops", Password: "s3cret"},
|
||
"不同 user": {Host: "jump.example.com", Port: 22, User: "admin", Password: "s3cret"},
|
||
"不同 password": {Host: "jump.example.com", Port: 22, User: "ops", Password: "other"},
|
||
}
|
||
|
||
baseName := sshNetworkName(newSSHClientCacheKey(base))
|
||
for label, cfg := range variants {
|
||
if got := sshNetworkName(newSSHClientCacheKey(cfg)); got == baseName {
|
||
t.Errorf("%s 却派生出与基准相同的 network 名 %s", label, got)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestSSHNetworkNameDoesNotLeakSecrets network 名会进入 DSN 与日志,
|
||
// 不能包含主机名、用户名或口令明文。
|
||
func TestSSHNetworkNameDoesNotLeakSecrets(t *testing.T) {
|
||
config := connection.SSHConfig{Host: "jump.example.com", Port: 22, User: "ops", Password: "s3cretP@ss"}
|
||
name := sshNetworkName(newSSHClientCacheKey(config))
|
||
|
||
for _, secret := range []string{"jump.example.com", "ops", "s3cretP@ss"} {
|
||
if strings.Contains(name, secret) {
|
||
t.Errorf("network 名 %q 泄露了明文 %q", name, secret)
|
||
}
|
||
}
|
||
}
|