package test import ( "fmt" "testing" "github.com/shadow1ng/fscan/plugins/base" // 导入插件包以触发自动注册 _ "github.com/shadow1ng/fscan/plugins/services/mysql" _ "github.com/shadow1ng/fscan/plugins/services/redis" _ "github.com/shadow1ng/fscan/plugins/services/ssh" ) // TestPluginRegistry 测试插件注册表 func TestPluginRegistry(t *testing.T) { // 获取所有注册的插件 plugins := base.GlobalPluginRegistry.GetAll() if len(plugins) == 0 { t.Error("没有注册任何插件") return } t.Logf("已注册插件数量: %d", len(plugins)) // 测试每个插件 for _, name := range plugins { t.Run(name, func(t *testing.T) { testSinglePlugin(t, name) }) } } // testSinglePlugin 测试单个插件 func testSinglePlugin(t *testing.T, pluginName string) { // 创建插件实例 plugin, err := base.GlobalPluginRegistry.Create(pluginName) if err != nil { t.Errorf("创建插件 %s 失败: %v", pluginName, err) return } // 测试元数据 metadata := plugin.GetMetadata() if metadata == nil { t.Errorf("插件 %s 没有元数据", pluginName) return } if metadata.Name != pluginName { t.Errorf("插件名称不匹配,期望: %s, 实际: %s", pluginName, metadata.Name) } // 测试能力 capabilities := plugin.GetCapabilities() t.Logf("插件 %s 能力数量: %d", pluginName, len(capabilities)) // 测试利用方法 exploitMethods := plugin.GetExploitMethods() t.Logf("插件 %s 利用方法数量: %d", pluginName, len(exploitMethods)) t.Logf("插件 %s 测试通过", pluginName) } // TestPluginMetadata 测试插件元数据 func TestPluginMetadata(t *testing.T) { testCases := []struct { name string expected *base.PluginMetadata }{ { name: "mysql", expected: &base.PluginMetadata{ Name: "mysql", Category: "service", Ports: []int{3306}, Protocols: []string{"tcp"}, }, }, { name: "redis", expected: &base.PluginMetadata{ Name: "redis", Category: "service", Ports: []int{6379}, Protocols: []string{"tcp"}, }, }, { name: "ssh", expected: &base.PluginMetadata{ Name: "ssh", Category: "service", Ports: []int{22}, Protocols: []string{"tcp"}, }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { metadata := base.GlobalPluginRegistry.GetMetadata(tc.name) if metadata == nil { t.Errorf("插件 %s 没有元数据", tc.name) return } if metadata.Name != tc.expected.Name { t.Errorf("插件名称不匹配,期望: %s, 实际: %s", tc.expected.Name, metadata.Name) } if metadata.Category != tc.expected.Category { t.Errorf("插件类别不匹配,期望: %s, 实际: %s", tc.expected.Category, metadata.Category) } if len(metadata.Ports) != len(tc.expected.Ports) { t.Errorf("端口数量不匹配,期望: %d, 实际: %d", len(tc.expected.Ports), len(metadata.Ports)) } }) } } // TestCredentialGeneration 测试凭据生成 func TestCredentialGeneration(t *testing.T) { usernames := []string{"admin", "root", "test"} passwords := []string{"password", "123456", "{user}"} credentials := base.GenerateCredentials(usernames, passwords) expectedCount := len(usernames) * len(passwords) if len(credentials) != expectedCount { t.Errorf("凭据数量不匹配,期望: %d, 实际: %d", expectedCount, len(credentials)) } // 检查 {user} 占位符替换 foundReplacement := false for _, cred := range credentials { if cred.Username == "admin" && cred.Password == "admin" { foundReplacement = true break } } if !foundReplacement { t.Error("没有找到 {user} 占位符替换的凭据") } t.Logf("凭据生成测试通过,生成了 %d 个凭据", len(credentials)) } // TestPasswordOnlyCredentials 测试仅密码凭据生成 func TestPasswordOnlyCredentials(t *testing.T) { passwords := []string{"password", "123456", "admin"} credentials := base.GeneratePasswordOnlyCredentials(passwords) if len(credentials) != len(passwords) { t.Errorf("凭据数量不匹配,期望: %d, 实际: %d", len(passwords), len(credentials)) } // 检查凭据内容 for i, cred := range credentials { if cred.Password != passwords[i] { t.Errorf("密码不匹配,期望: %s, 实际: %s", passwords[i], cred.Password) } if cred.Username != "" { t.Errorf("用户名应为空,实际: %s", cred.Username) } } t.Logf("仅密码凭据生成测试通过,生成了 %d 个凭据", len(credentials)) } // TestExploitMethods 测试利用方法 func TestExploitMethods(t *testing.T) { // 测试MySQL利用方法 plugin, err := base.GlobalPluginRegistry.Create("mysql") if err != nil { t.Skip("MySQL插件未注册,跳过测试") return } methods := plugin.GetExploitMethods() if len(methods) == 0 { t.Error("MySQL插件应该有利用方法") return } // 检查是否支持数据提取 if !plugin.IsExploitSupported(base.ExploitDataExtraction) { t.Error("MySQL插件应该支持数据提取") } t.Logf("MySQL插件利用方法测试通过,方法数量: %d", len(methods)) // 测试Redis利用方法 redisPlugin, err := base.GlobalPluginRegistry.Create("redis") if err != nil { t.Skip("Redis插件未注册,跳过测试") return } redisMethods := redisPlugin.GetExploitMethods() if len(redisMethods) == 0 { t.Error("Redis插件应该有利用方法") return } // 检查是否支持文件写入 if !redisPlugin.IsExploitSupported(base.ExploitFileWrite) { t.Error("Redis插件应该支持文件写入") } t.Logf("Redis插件利用方法测试通过,方法数量: %d", len(redisMethods)) } // TestPluginCapabilities 测试插件能力 func TestPluginCapabilities(t *testing.T) { testCases := []struct { pluginName string expected []base.Capability }{ { pluginName: "mysql", expected: []base.Capability{ base.CapWeakPassword, base.CapDataExtraction, base.CapFileWrite, }, }, { pluginName: "redis", expected: []base.Capability{ base.CapWeakPassword, base.CapUnauthorized, base.CapFileWrite, base.CapCommandExecution, }, }, } for _, tc := range testCases { t.Run(tc.pluginName, func(t *testing.T) { plugin, err := base.GlobalPluginRegistry.Create(tc.pluginName) if err != nil { t.Skipf("插件 %s 未注册,跳过测试", tc.pluginName) return } capabilities := plugin.GetCapabilities() // 检查是否包含预期的能力 for _, expectedCap := range tc.expected { found := false for _, cap := range capabilities { if cap == expectedCap { found = true break } } if !found { t.Errorf("插件 %s 缺少能力: %s", tc.pluginName, expectedCap) } } t.Logf("插件 %s 能力测试通过,能力数量: %d", tc.pluginName, len(capabilities)) }) } } // BenchmarkPluginCreation 插件创建性能基准测试 func BenchmarkPluginCreation(b *testing.B) { plugins := []string{"mysql", "redis", "ssh"} for _, pluginName := range plugins { b.Run(pluginName, func(b *testing.B) { for i := 0; i < b.N; i++ { plugin, err := base.GlobalPluginRegistry.Create(pluginName) if err != nil { b.Errorf("创建插件失败: %v", err) } _ = plugin } }) } } // BenchmarkCredentialGeneration 凭据生成性能基准测试 func BenchmarkCredentialGeneration(b *testing.B) { usernames := []string{"root", "admin", "user", "test", "mysql", "redis", "postgres"} passwords := make([]string, 50) // 50个密码 for i := range passwords { passwords[i] = fmt.Sprintf("password%d", i) } b.ResetTimer() for i := 0; i < b.N; i++ { credentials := base.GenerateCredentials(usernames, passwords) _ = credentials } }