fscan/Plugins/test/plugin_test.go
ZacharyZcR 43f210ffc6 feat: 实现新一代插件注册系统完全替代传统手动注册模式
- 重构插件注册架构采用现代工厂模式和自动发现机制
- 新增完整的插件元数据管理系统支持版本能力标签等信息
- 实现智能插件适配器提供向后兼容的桥接功能
- 建立MySQL Redis SSH三个标准插件作为新架构参考实现
- 优化插件扫描逻辑支持按端口按类型的智能查询和过滤
- 添加国际化支持和完善的文档体系
- 代码量减少67%维护成本大幅降低扩展性显著提升

新架构特点:
- 零配置插件注册import即用
- 工厂模式延迟初始化和依赖注入
- 丰富元数据系统和能力声明
- 完全解耦的模块化设计
- 面向未来的可扩展架构

测试验证: MySQL和Redis插件功能完整包括弱密码检测未授权访问检测和自动利用攻击
2025-08-07 11:28:34 +08:00

302 lines
7.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
}