Files
fscan/core/base_scan_strategy.go
ZacharyZcR 713c86d84e refactor(core,grdp): 删除未使用的死代码
- 移除 BaseScanStrategy.LogPluginInfoWithPort 方法(无调用者)
- 移除 mcs.go 中被注释的旧 connect 函数实现
2026-01-21 23:26:12 +08:00

284 lines
7.9 KiB
Go
Raw Permalink 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 core
import (
"fmt"
"os"
"sort"
"strings"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/plugins"
)
// PluginFilterType 插件过滤类型
type PluginFilterType int
const (
// FilterNone 不过滤
FilterNone PluginFilterType = iota
// FilterLocal 仅本地插件
FilterLocal
// FilterService 仅服务插件(排除本地)
FilterService
// FilterWeb 仅Web插件
FilterWeb
)
// 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(config *common.Config) ([]string, bool) {
scanMode := config.Mode
// 如果指定了特定插件且不是"all"
if scanMode != "" && scanMode != "all" {
requestedPlugins := parsePluginList(scanMode)
if len(requestedPlugins) == 0 {
requestedPlugins = []string{scanMode}
}
// 验证插件是否存在
var validPlugins []string
var missingPlugins []string
for _, name := range requestedPlugins {
if b.pluginExists(name) {
validPlugins = append(validPlugins, name)
} else {
missingPlugins = append(missingPlugins, name)
}
}
// 警告用户显式指定的插件不存在
// 注意使用fmt.Fprintf直接输出到stderr确保错误消息不会被日志级别过滤
for _, name := range missingPlugins {
errMsg := i18n.Tr("scan_plugin_not_found", name)
fmt.Fprintf(os.Stderr, "[ERROR] %s\n", errMsg)
}
return validPlugins, true
}
// 未指定或使用"all":根据策略类型获取对应插件
return b.getPluginsByFilterType(), false
}
// IsPluginApplicableByName 根据插件名称判断是否适用
func (b *BaseScanStrategy) IsPluginApplicableByName(pluginName string, targetHost string, targetPort int, isCustomMode bool, config *common.Config) bool {
// 首先检查插件是否存在
if !b.pluginExists(pluginName) {
return false
}
// 检查端口匹配和过滤器类型
return b.isPluginApplicableToPortWithHost(pluginName, targetHost, targetPort) && b.isPluginPassesFilterType(pluginName, isCustomMode, config)
}
func (b *BaseScanStrategy) pluginExists(pluginName string) bool {
return plugins.Exists(pluginName)
}
func (b *BaseScanStrategy) getPluginPorts(pluginName string) []int {
return plugins.GetPluginPorts(pluginName)
}
func (b *BaseScanStrategy) isWebPlugin(pluginName string) bool {
return plugins.HasType(pluginName, plugins.PluginTypeWeb)
}
func (b *BaseScanStrategy) isLocalPlugin(pluginName string) bool {
return plugins.HasType(pluginName, plugins.PluginTypeLocal)
}
func (b *BaseScanStrategy) isLocalPluginExplicitlySpecified(pluginName string, config *common.Config) bool {
return config.LocalPlugin == pluginName
}
// isPluginApplicableToPortWithHost 检查插件是否适用于指定端口
func (b *BaseScanStrategy) isPluginApplicableToPortWithHost(pluginName string, targetHost string, targetPort int) bool {
if b.isWebPlugin(pluginName) {
return IsMarkedWebService(targetHost, targetPort)
}
pluginPorts := b.getPluginPorts(pluginName)
// 无端口限制的插件适用于所有端口
if len(pluginPorts) == 0 {
return true
}
// 有端口限制的插件:检查端口匹配
if targetPort > 0 {
for _, port := range pluginPorts {
if port == targetPort {
return true
}
}
}
return false
}
func (b *BaseScanStrategy) isPluginApplicableToPort(pluginName string, targetPort int) bool {
return b.isPluginApplicableToPortWithHost(pluginName, "", targetPort)
}
// isPluginPassesFilterType 检查插件是否通过过滤器类型检查
func (b *BaseScanStrategy) isPluginPassesFilterType(pluginName string, isCustomMode bool, config *common.Config) bool {
// 自定义模式下强制运行所有明确指定的插件
if isCustomMode {
return true
}
// 应用过滤器类型检查
switch b.filterType {
case FilterLocal:
// 本地扫描策略:只允许本地插件且必须通过-local参数明确指定
if b.isLocalPlugin(pluginName) {
return b.isLocalPluginExplicitlySpecified(pluginName, config)
}
return false
case FilterService:
// 服务扫描策略:排除本地插件
return !b.isLocalPlugin(pluginName)
case FilterWeb:
// Web扫描策略只允许Web插件
return b.isWebPlugin(pluginName)
default:
// 无过滤器:本地插件需要明确指定,其他插件都允许
if b.isLocalPlugin(pluginName) {
return b.isLocalPluginExplicitlySpecified(pluginName, config)
}
return true
}
}
// LogPluginInfo 输出插件信息
func (b *BaseScanStrategy) LogPluginInfo(config *common.Config) {
allPlugins, isCustomMode := b.GetPlugins(config)
var prefix string
switch b.filterType {
case FilterLocal:
prefix = i18n.GetText("concurrency_local_plugin")
case FilterService:
prefix = i18n.GetText("concurrency_service_plugin")
case FilterWeb:
prefix = i18n.GetText("concurrency_web_plugin")
default:
prefix = i18n.GetText("concurrency_plugin")
}
// 插件信息不再输出,减少干扰
_ = allPlugins
_ = isCustomMode
_ = prefix
}
// formatPluginList 格式化插件列表超过5个时精简显示
func formatPluginList(plugins []string) string {
if len(plugins) <= 5 {
return strings.Join(plugins, ", ")
}
return fmt.Sprintf("%s ... 等%d个", strings.Join(plugins[:5], ", "), len(plugins))
}
// ValidateConfiguration 验证扫描配置
func (b *BaseScanStrategy) ValidateConfiguration() error {
return nil
}
// LogScanStart 输出扫描开始信息(已精简,仅在非服务扫描模式下显示)
func (b *BaseScanStrategy) LogScanStart() {
// 服务扫描模式下不显示(插件信息已足够说明)
// 仅在本地/Web等特殊模式下显示
switch b.filterType {
case FilterLocal:
common.LogInfo(i18n.GetText("start_local_scan"))
case FilterWeb:
common.LogInfo(i18n.GetText("start_web_scan"))
}
}
// getPluginsByFilterType 根据过滤器类型获取插件列表
func (b *BaseScanStrategy) getPluginsByFilterType() []string {
allPlugins := plugins.All()
var filteredPlugins []string
switch b.filterType {
case FilterLocal:
// 本地扫描策略:只返回本地插件
for _, pluginName := range allPlugins {
if b.isLocalPlugin(pluginName) {
filteredPlugins = append(filteredPlugins, pluginName)
}
}
case FilterService:
// 服务扫描策略排除本地插件和纯Web插件保留服务插件
for _, pluginName := range allPlugins {
if !b.isLocalPlugin(pluginName) {
filteredPlugins = append(filteredPlugins, pluginName)
}
}
case FilterWeb:
// Web扫描策略只返回Web插件
for _, pluginName := range allPlugins {
if b.isWebPlugin(pluginName) {
filteredPlugins = append(filteredPlugins, pluginName)
}
}
// 确保 webtitle 在 webpoc 之前执行,避免指纹识别竞态
sort.Slice(filteredPlugins, func(i, j int) bool {
// webtitle 必须在 webpoc 之前
if filteredPlugins[i] == "webtitle" {
return true
}
if filteredPlugins[j] == "webtitle" {
return false
}
if filteredPlugins[i] == "webpoc" {
return false
}
if filteredPlugins[j] == "webpoc" {
return true
}
// 其他插件保持字母顺序
return filteredPlugins[i] < filteredPlugins[j]
})
default:
// 无过滤器:返回所有插件
filteredPlugins = allPlugins
}
return filteredPlugins
}
// parsePluginList 解析插件列表字符串
func parsePluginList(pluginStr string) []string {
if pluginStr == "" {
return []string{}
}
// 支持逗号分隔的插件列表
plugins := strings.Split(pluginStr, ",")
result := []string{} // 初始化为空切片而非nil
for _, plugin := range plugins {
plugin = strings.TrimSpace(plugin)
if plugin != "" {
result = append(result, plugin)
}
}
return result
}