mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00

- 重构插件注册架构采用现代工厂模式和自动发现机制 - 新增完整的插件元数据管理系统支持版本能力标签等信息 - 实现智能插件适配器提供向后兼容的桥接功能 - 建立MySQL Redis SSH三个标准插件作为新架构参考实现 - 优化插件扫描逻辑支持按端口按类型的智能查询和过滤 - 添加国际化支持和完善的文档体系 - 代码量减少67%维护成本大幅降低扩展性显著提升 新架构特点: - 零配置插件注册import即用 - 工厂模式延迟初始化和依赖注入 - 丰富元数据系统和能力声明 - 完全解耦的模块化设计 - 面向未来的可扩展架构 测试验证: MySQL和Redis插件功能完整包括弱密码检测未授权访问检测和自动利用攻击
260 lines
7.2 KiB
Go
260 lines
7.2 KiB
Go
package core
|
||
|
||
import (
|
||
"fmt"
|
||
"github.com/shadow1ng/fscan/common"
|
||
"github.com/shadow1ng/fscan/common/i18n"
|
||
"strings"
|
||
)
|
||
|
||
/*
|
||
BaseScanStrategy.go - 扫描策略基础类
|
||
|
||
提供所有扫描策略的通用功能,包括插件管理、验证、
|
||
日志输出等,减少代码重复并提升维护性。
|
||
*/
|
||
|
||
// PluginFilterType 插件过滤类型
|
||
type PluginFilterType int
|
||
|
||
const (
|
||
FilterNone PluginFilterType = iota // 不过滤
|
||
FilterLocal // 仅本地插件
|
||
FilterService // 仅服务插件(排除本地)
|
||
FilterWeb // 仅Web插件
|
||
)
|
||
|
||
// BaseScanStrategy 扫描策略基础类
|
||
type BaseScanStrategy struct {
|
||
strategyName string
|
||
filterType PluginFilterType
|
||
}
|
||
|
||
// NewBaseScanStrategy 创建基础扫描策略
|
||
func NewBaseScanStrategy(name string, filterType PluginFilterType) *BaseScanStrategy {
|
||
return &BaseScanStrategy{
|
||
strategyName: name,
|
||
filterType: filterType,
|
||
}
|
||
}
|
||
|
||
// =============================================================================
|
||
// 插件管理通用方法
|
||
// =============================================================================
|
||
|
||
// GetPlugins 获取插件列表(通用实现)
|
||
func (b *BaseScanStrategy) GetPlugins() ([]string, bool) {
|
||
// 如果指定了特定插件且不是"all"
|
||
if common.ScanMode != "" && common.ScanMode != "all" {
|
||
requestedPlugins := parsePluginList(common.ScanMode)
|
||
if len(requestedPlugins) == 0 {
|
||
requestedPlugins = []string{common.ScanMode}
|
||
}
|
||
|
||
// 验证插件是否存在(使用新插件系统)
|
||
var validPlugins []string
|
||
for _, name := range requestedPlugins {
|
||
if GlobalPluginAdapter.PluginExists(name) {
|
||
validPlugins = append(validPlugins, name)
|
||
}
|
||
}
|
||
|
||
return validPlugins, true
|
||
}
|
||
|
||
// 未指定或使用"all":获取所有插件,由IsPluginApplicable做类型过滤
|
||
return GlobalPluginAdapter.GetAllPluginNames(), false
|
||
}
|
||
|
||
// GetApplicablePlugins 获取适用的插件列表(用于日志显示)
|
||
func (b *BaseScanStrategy) GetApplicablePlugins(allPlugins []string, isCustomMode bool) []string {
|
||
if isCustomMode {
|
||
return allPlugins
|
||
}
|
||
|
||
var applicablePlugins []string
|
||
for _, pluginName := range allPlugins {
|
||
if !GlobalPluginAdapter.PluginExists(pluginName) {
|
||
continue
|
||
}
|
||
|
||
if b.isPluginTypeMatchedByName(pluginName) {
|
||
applicablePlugins = append(applicablePlugins, pluginName)
|
||
}
|
||
}
|
||
|
||
return applicablePlugins
|
||
}
|
||
|
||
// isPluginTypeMatchedByName 根据插件名称检查类型是否匹配过滤器
|
||
func (b *BaseScanStrategy) isPluginTypeMatchedByName(pluginName string) bool {
|
||
metadata := GlobalPluginAdapter.registry.GetMetadata(pluginName)
|
||
if metadata == nil {
|
||
return false
|
||
}
|
||
|
||
switch b.filterType {
|
||
case FilterLocal:
|
||
return metadata.Category == "local"
|
||
case FilterService:
|
||
return metadata.Category == "service"
|
||
case FilterWeb:
|
||
return metadata.Category == "web"
|
||
default:
|
||
return true
|
||
}
|
||
}
|
||
|
||
// isPluginTypeMatched 检查插件类型是否匹配过滤器
|
||
func (b *BaseScanStrategy) isPluginTypeMatched(plugin common.ScanPlugin) bool {
|
||
switch b.filterType {
|
||
case FilterLocal:
|
||
return plugin.HasType(common.PluginTypeLocal)
|
||
case FilterService:
|
||
return !plugin.HasType(common.PluginTypeLocal)
|
||
case FilterWeb:
|
||
return plugin.HasType(common.PluginTypeWeb)
|
||
default:
|
||
return true
|
||
}
|
||
}
|
||
|
||
// IsPluginApplicableByName 根据插件名称判断是否适用(新方法)
|
||
func (b *BaseScanStrategy) IsPluginApplicableByName(pluginName string, targetPort int, isCustomMode bool) bool {
|
||
// 自定义模式下运行所有明确指定的插件
|
||
if isCustomMode {
|
||
return true
|
||
}
|
||
|
||
metadata := GlobalPluginAdapter.registry.GetMetadata(pluginName)
|
||
if metadata == nil {
|
||
return false
|
||
}
|
||
|
||
// 检查类型匹配
|
||
if !b.isPluginTypeMatchedByName(pluginName) {
|
||
return false
|
||
}
|
||
|
||
// 检查端口匹配(如果指定了端口)
|
||
if targetPort > 0 && len(metadata.Ports) > 0 {
|
||
for _, port := range metadata.Ports {
|
||
if port == targetPort {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
// IsPluginApplicable 判断插件是否适用(通用实现)
|
||
func (b *BaseScanStrategy) IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool {
|
||
// 自定义模式下运行所有明确指定的插件
|
||
if isCustomMode {
|
||
return true
|
||
}
|
||
|
||
// 检查插件类型过滤
|
||
if !b.isPluginTypeMatched(plugin) {
|
||
return false
|
||
}
|
||
|
||
// 对于服务扫描,还需检查端口匹配
|
||
if b.filterType == FilterService {
|
||
// 无端口限制的插件适用于所有端口
|
||
if len(plugin.Ports) == 0 {
|
||
return true
|
||
}
|
||
// 有端口限制的插件:检查端口是否匹配
|
||
if targetPort > 0 {
|
||
return plugin.HasPort(targetPort)
|
||
}
|
||
// 如果没有提供目标端口,则不执行有端口限制的插件
|
||
return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
// =============================================================================
|
||
// 日志输出通用方法
|
||
// =============================================================================
|
||
|
||
// LogPluginInfo 输出插件信息(通用实现)
|
||
func (b *BaseScanStrategy) LogPluginInfo() {
|
||
allPlugins, isCustomMode := b.GetPlugins()
|
||
applicablePlugins := b.GetApplicablePlugins(allPlugins, isCustomMode)
|
||
|
||
// 生成日志消息
|
||
var messageKey, prefix string
|
||
switch b.filterType {
|
||
case FilterLocal:
|
||
messageKey = "scan_plugins_local"
|
||
prefix = i18n.GetText("scan_mode_local_prefix")
|
||
case FilterService:
|
||
messageKey = "scan_plugins_service"
|
||
prefix = i18n.GetText("scan_mode_service_prefix")
|
||
case FilterWeb:
|
||
messageKey = "scan_plugins_web"
|
||
prefix = i18n.GetText("scan_mode_web_prefix")
|
||
default:
|
||
messageKey = "scan_plugins_custom"
|
||
prefix = ""
|
||
}
|
||
|
||
if len(applicablePlugins) > 0 {
|
||
if isCustomMode {
|
||
common.LogBase(fmt.Sprintf("%s: %s", prefix,
|
||
i18n.GetText("scan_plugins_custom_specified", strings.Join(applicablePlugins, ", "))))
|
||
} else {
|
||
common.LogBase(fmt.Sprintf("%s: %s", prefix,
|
||
i18n.GetText(messageKey, strings.Join(applicablePlugins, ", "))))
|
||
}
|
||
} else {
|
||
noPluginsKey := fmt.Sprintf("scan_no_%s_plugins", b.getFilterTypeName())
|
||
common.LogBase(fmt.Sprintf("%s: %s", prefix, i18n.GetText(noPluginsKey)))
|
||
}
|
||
}
|
||
|
||
// getFilterTypeName 获取过滤器类型名称
|
||
func (b *BaseScanStrategy) getFilterTypeName() string {
|
||
switch b.filterType {
|
||
case FilterLocal:
|
||
return "local"
|
||
case FilterService:
|
||
return "service"
|
||
case FilterWeb:
|
||
return "web"
|
||
default:
|
||
return "general"
|
||
}
|
||
}
|
||
|
||
// =============================================================================
|
||
// 验证通用方法
|
||
// =============================================================================
|
||
|
||
// ValidateConfiguration 验证扫描配置(通用实现)
|
||
func (b *BaseScanStrategy) ValidateConfiguration() error {
|
||
return validateScanPlugins()
|
||
}
|
||
|
||
// =============================================================================
|
||
// 通用辅助方法
|
||
// =============================================================================
|
||
|
||
// LogScanStart 输出扫描开始信息
|
||
func (b *BaseScanStrategy) LogScanStart() {
|
||
switch b.filterType {
|
||
case FilterLocal:
|
||
common.LogBase(i18n.GetText("scan_local_start"))
|
||
case FilterService:
|
||
common.LogBase(i18n.GetText("scan_service_start"))
|
||
case FilterWeb:
|
||
common.LogBase(i18n.GetText("scan_web_start"))
|
||
default:
|
||
common.LogBase(i18n.GetText("scan_general_start"))
|
||
}
|
||
} |