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

新增功能: - 支持通过-m icmp参数启用存活探测功能 - 与现有-ao参数功能等价,提供更直观的使用方式 - 添加参数冲突检查,当同时使用-ao和-m icmp时给出友好提示 - 完善参数验证,将icmp添加到有效扫描模式列表 技术实现: - 扩展ValidationParser支持icmp模式验证 - 修改Scanner策略选择逻辑支持icmp模式 - 新增参数冲突检查函数checkParameterConflicts - 更新帮助信息和国际化消息支持 向后兼容: - 保持-ao参数原有功能不变 - 两种参数方式都能正确触发存活探测 - 支持中英文双语界面和提示信息
291 lines
7.9 KiB
Go
291 lines
7.9 KiB
Go
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) > DefaultMaxPortRange {
|
||
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 {
|
||
validModes := []string{"all", "main", "web", "db", "service", "top1000", "custom", "icmp"}
|
||
|
||
for _, mode := range validModes {
|
||
if scanMode == mode {
|
||
return nil
|
||
}
|
||
}
|
||
|
||
return NewParseError("VALIDATION_ERROR",
|
||
fmt.Sprintf("无效的扫描模式: %s", scanMode), "scan_mode", 0, nil)
|
||
}
|
||
|
||
|
||
// =============================================================================================
|
||
// 已删除的死代码(未使用):Validate 和 GetStatistics 方法
|
||
// =============================================================================================
|