mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-25 12:41:53 +08:00
feat: v2.1.0 核心重构与功能增强
## 架构重构
- 全局变量消除,迁移至 Config/State 对象
- SMB 插件融合(smb/smb2/smbghost/smbinfo)
- 服务探测重构,实现 Nmap 风格 fallback 机制
- 输出系统重构,TXT 实时刷盘 + 双写机制
- i18n 框架升级至 go-i18n
## 性能优化
- 正则表达式预编译
- 内存优化 map[string]struct{}
- 并发指纹匹配
- SOCKS5 连接复用
- 滑动窗口调度 + 自适应线程池
## 新功能
- Web 管理界面
- 多格式 POC 适配(xray/afrog)
- 增强指纹库(3139条)
- Favicon hash 指纹识别
- 插件选择性编译(Build Tags)
- fscan-lab 靶场环境
- 默认端口扩展(62→133)
## 构建系统
- 添加 no_local tag 支持排除本地插件
- 多版本构建:fscan/fscan-nolocal/fscan-web
- CI 添加 snapshot 模式支持仅测试构建
## Bug 修复
- 修复 120+ 个问题,包括 RDP panic、批量扫描漏报、
JSON 输出格式、Redis 检测、Context 超时等
## 测试增强
- 单元测试覆盖率 74-100%
- 并发安全测试
- 集成测试(Web/端口/服务/SSH/ICMP)
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
package parsers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ValidationParser 参数验证解析器
|
||||
type ValidationParser struct {
|
||||
mu sync.RWMutex //nolint:unused // reserved for future thread safety
|
||||
options *ValidationParserOptions
|
||||
}
|
||||
|
||||
// ValidationParserOptions 验证解析器选项
|
||||
type ValidationParserOptions struct {
|
||||
StrictMode bool `json:"strict_mode"` // 严格模式
|
||||
AllowEmpty bool `json:"allow_empty"` // 允许空配置
|
||||
CheckConflicts bool `json:"check_conflicts"` // 检查参数冲突
|
||||
ValidateTargets bool `json:"validate_targets"` // 验证目标有效性
|
||||
ValidateNetwork bool `json:"validate_network"` // 验证网络配置
|
||||
MaxErrorCount int `json:"max_error_count"` // 最大错误数量
|
||||
}
|
||||
|
||||
// DefaultValidationParserOptions 默认验证解析器选项
|
||||
func DefaultValidationParserOptions() *ValidationParserOptions {
|
||||
return &ValidationParserOptions{
|
||||
StrictMode: DefaultStrictMode,
|
||||
AllowEmpty: DefaultAllowEmpty,
|
||||
CheckConflicts: DefaultCheckConflicts,
|
||||
ValidateTargets: DefaultValidateTargets,
|
||||
ValidateNetwork: DefaultValidateNetwork,
|
||||
MaxErrorCount: DefaultMaxErrorCount,
|
||||
}
|
||||
}
|
||||
|
||||
// NewValidationParser 创建验证解析器
|
||||
func NewValidationParser(options *ValidationParserOptions) *ValidationParser {
|
||||
if options == nil {
|
||||
options = DefaultValidationParserOptions()
|
||||
}
|
||||
|
||||
return &ValidationParser{
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidationInput 验证输入参数
|
||||
type ValidationInput struct {
|
||||
// 扫描模式
|
||||
ScanMode string `json:"scan_mode"`
|
||||
LocalMode bool `json:"local_mode"`
|
||||
|
||||
// 目标配置
|
||||
HasHosts bool `json:"has_hosts"`
|
||||
HasURLs bool `json:"has_urls"`
|
||||
HasPorts bool `json:"has_ports"`
|
||||
|
||||
// 网络配置
|
||||
HasProxy bool `json:"has_proxy"`
|
||||
DisablePing bool `json:"disable_ping"`
|
||||
|
||||
// 凭据配置
|
||||
HasCredentials bool `json:"has_credentials"`
|
||||
|
||||
// 特殊模式
|
||||
PocScan bool `json:"poc_scan"`
|
||||
BruteScan bool `json:"brute_scan"`
|
||||
LocalScan bool `json:"local_scan"`
|
||||
}
|
||||
|
||||
// ConflictRule 冲突规则
|
||||
type ConflictRule struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Fields []string `json:"fields"`
|
||||
Severity string `json:"severity"` // error, warning, info
|
||||
}
|
||||
|
||||
// ValidationRule 验证规则
|
||||
type ValidationRule struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Validator func(input *ValidationInput) error `json:"-"`
|
||||
Severity string `json:"severity"`
|
||||
}
|
||||
|
||||
// Parse 执行参数验证
|
||||
func (vp *ValidationParser) Parse(input *ValidationInput, config *ParsedConfig, options *ParserOptions) (*ParseResult, error) {
|
||||
if input == nil {
|
||||
return nil, NewParseError(ErrorTypeInputError, "验证输入为空", "", 0, ErrEmptyInput)
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
result := &ParseResult{
|
||||
Config: &ParsedConfig{
|
||||
Validation: &ValidationConfig{
|
||||
ScanMode: input.ScanMode,
|
||||
ConflictChecked: true,
|
||||
},
|
||||
},
|
||||
Success: true,
|
||||
}
|
||||
|
||||
var errors []error
|
||||
var warnings []string
|
||||
|
||||
// 基础验证
|
||||
basicErrors, basicWarnings := vp.validateBasic(input)
|
||||
errors = append(errors, basicErrors...)
|
||||
warnings = append(warnings, basicWarnings...)
|
||||
|
||||
// 冲突检查
|
||||
if vp.options.CheckConflicts {
|
||||
conflictErrors, conflictWarnings := vp.checkConflicts(input)
|
||||
errors = append(errors, conflictErrors...)
|
||||
warnings = append(warnings, conflictWarnings...)
|
||||
}
|
||||
|
||||
// 逻辑验证
|
||||
logicErrors, logicWarnings := vp.validateLogic(input, config)
|
||||
errors = append(errors, logicErrors...)
|
||||
warnings = append(warnings, logicWarnings...)
|
||||
|
||||
// 性能建议
|
||||
performanceWarnings := vp.checkPerformance(input, config)
|
||||
warnings = append(warnings, performanceWarnings...)
|
||||
|
||||
// 检查错误数量限制
|
||||
if len(errors) > vp.options.MaxErrorCount {
|
||||
errors = errors[:vp.options.MaxErrorCount]
|
||||
warnings = append(warnings, fmt.Sprintf("错误数量过多,仅显示前%d个", vp.options.MaxErrorCount))
|
||||
}
|
||||
|
||||
// 更新结果
|
||||
result.Config.Validation.Errors = errors
|
||||
result.Config.Validation.Warnings = warnings
|
||||
result.Errors = errors
|
||||
result.Warnings = warnings
|
||||
result.ParseTime = time.Since(startTime)
|
||||
result.Success = len(errors) == 0
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// validateBasic 基础验证
|
||||
func (vp *ValidationParser) validateBasic(input *ValidationInput) ([]error, []string) {
|
||||
var errors []error
|
||||
var warnings []string
|
||||
|
||||
// 检查是否有任何目标
|
||||
if !input.HasHosts && !input.HasURLs && !input.LocalMode {
|
||||
if !vp.options.AllowEmpty {
|
||||
errors = append(errors, NewParseError("VALIDATION_ERROR", "未指定任何扫描目标", "basic", 0, nil))
|
||||
} else {
|
||||
warnings = append(warnings, "未指定扫描目标,将使用默认配置")
|
||||
}
|
||||
}
|
||||
|
||||
// 检查扫描模式
|
||||
if input.ScanMode != "" {
|
||||
if err := vp.validateScanMode(input.ScanMode); err != nil {
|
||||
if vp.options.StrictMode {
|
||||
errors = append(errors, err)
|
||||
} else {
|
||||
warnings = append(warnings, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors, warnings
|
||||
}
|
||||
|
||||
// checkConflicts 检查参数冲突
|
||||
func (vp *ValidationParser) checkConflicts(input *ValidationInput) ([]error, []string) {
|
||||
var errors []error
|
||||
var warnings []string
|
||||
|
||||
// 定义冲突规则 (预留用于扩展)
|
||||
_ = []ConflictRule{
|
||||
{
|
||||
Name: "multiple_scan_modes",
|
||||
Description: "不能同时使用多种扫描模式",
|
||||
Fields: []string{"hosts", "urls", "local_mode"},
|
||||
Severity: "error",
|
||||
},
|
||||
{
|
||||
Name: "proxy_with_ping",
|
||||
Description: "使用代理时建议禁用Ping检测",
|
||||
Fields: []string{"proxy", "ping"},
|
||||
Severity: "warning",
|
||||
},
|
||||
}
|
||||
|
||||
// 检查扫描模式冲突
|
||||
scanModes := 0
|
||||
if input.HasHosts {
|
||||
scanModes++
|
||||
}
|
||||
if input.HasURLs {
|
||||
scanModes++
|
||||
}
|
||||
if input.LocalMode {
|
||||
scanModes++
|
||||
}
|
||||
|
||||
if scanModes > 1 {
|
||||
errors = append(errors, NewParseError("CONFLICT_ERROR",
|
||||
"不能同时指定多种扫描模式(主机扫描、URL扫描、本地模式)", "validation", 0, nil))
|
||||
}
|
||||
|
||||
// 检查代理和Ping冲突
|
||||
if input.HasProxy && !input.DisablePing {
|
||||
warnings = append(warnings, "代理模式下Ping检测可能失效")
|
||||
}
|
||||
|
||||
return errors, warnings
|
||||
}
|
||||
|
||||
// validateLogic 逻辑验证
|
||||
func (vp *ValidationParser) validateLogic(input *ValidationInput, config *ParsedConfig) ([]error, []string) {
|
||||
var errors []error
|
||||
var warnings []string
|
||||
|
||||
// 验证目标配置逻辑
|
||||
if vp.options.ValidateTargets && config != nil && config.Targets != nil {
|
||||
// 检查排除端口配置
|
||||
if len(config.Targets.ExcludePorts) > 0 && len(config.Targets.Ports) == 0 {
|
||||
warnings = append(warnings, "排除端口无效")
|
||||
}
|
||||
}
|
||||
|
||||
return errors, warnings
|
||||
}
|
||||
|
||||
// checkPerformance 性能检查
|
||||
func (vp *ValidationParser) checkPerformance(input *ValidationInput, config *ParsedConfig) []string {
|
||||
var warnings []string
|
||||
|
||||
if config == nil {
|
||||
return warnings
|
||||
}
|
||||
|
||||
// 检查目标数量
|
||||
if config.Targets != nil {
|
||||
totalTargets := len(config.Targets.Hosts) * len(config.Targets.Ports)
|
||||
if totalTargets > MaxTargetsThreshold {
|
||||
warnings = append(warnings, fmt.Sprintf("大量目标(%d),可能耗时较长", totalTargets))
|
||||
}
|
||||
|
||||
// 检查端口范围
|
||||
if len(config.Targets.Ports) > PortCountWarningThreshold {
|
||||
warnings = append(warnings, "端口数量过多")
|
||||
}
|
||||
}
|
||||
|
||||
// 检查超时配置
|
||||
if config.Network != nil {
|
||||
if config.Network.Timeout < MinTimeoutThreshold {
|
||||
warnings = append(warnings, "超时过短")
|
||||
}
|
||||
if config.Network.Timeout > MaxTimeoutThreshold {
|
||||
warnings = append(warnings, "超时过长")
|
||||
}
|
||||
}
|
||||
|
||||
return warnings
|
||||
}
|
||||
|
||||
// validateScanMode 验证扫描模式
|
||||
func (vp *ValidationParser) validateScanMode(scanMode string) error { //nolint:unparam
|
||||
validModes := []string{"all", "icmp"}
|
||||
|
||||
// 检查是否为预定义模式
|
||||
for _, mode := range validModes {
|
||||
if scanMode == mode {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 允许插件名称作为扫描模式,实际插件验证在运行时进行
|
||||
// 这里不做严格验证,避免维护两套插件列表
|
||||
return nil
|
||||
}
|
||||
|
||||
// =============================================================================================
|
||||
// 已删除的死代码(未使用):Validate 和 GetStatistics 方法
|
||||
// =============================================================================================
|
||||
Reference in New Issue
Block a user