mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-23 03:31:53 +08:00
## 架构重构
- 全局变量消除,迁移至 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)
247 lines
6.9 KiB
Go
247 lines
6.9 KiB
Go
package core
|
||
|
||
/*
|
||
service_probe_strategy_test.go - SmartProbeStrategy 策略逻辑测试
|
||
|
||
测试重点:
|
||
1. 新探测策略 - 使用 Probe.Ports 和 Rarity 排序
|
||
2. 动态超时 - 使用 TotalWaitMS
|
||
3. NULL 回退 - 隐式 NULL 探测器匹配
|
||
|
||
说明:
|
||
- 只测试策略逻辑,不测试实际的网络IO(那是集成测试的职责)
|
||
*/
|
||
|
||
import (
|
||
"testing"
|
||
"time"
|
||
|
||
"github.com/shadow1ng/fscan/common"
|
||
"github.com/shadow1ng/fscan/core/portfinger"
|
||
)
|
||
|
||
// =============================================================================
|
||
// 测试1:新探测策略(使用 Probe.Ports)
|
||
// =============================================================================
|
||
|
||
// TestNewStrategy_ProbePortsUsed 验证新策略使用 Probe.Ports 字段
|
||
func TestNewStrategy_ProbePortsUsed(t *testing.T) {
|
||
v := portfinger.GetGlobalVScan()
|
||
|
||
// 验证端口80有对应的探测器
|
||
probes := v.GetProbesForPort(80)
|
||
if len(probes) == 0 {
|
||
t.Error("端口 80 应该有探测器")
|
||
}
|
||
|
||
// 验证 GetRequest 探测器在列表中
|
||
found := false
|
||
for _, p := range probes {
|
||
if p.Name == "GetRequest" {
|
||
found = true
|
||
t.Logf("✓ GetRequest 探测器存在于端口 80 的探测器列表中 (rarity=%d)", p.Rarity)
|
||
break
|
||
}
|
||
}
|
||
if !found {
|
||
t.Error("GetRequest 探测器应该在端口 80 的列表中")
|
||
}
|
||
}
|
||
|
||
// TestNewStrategy_RaritySorting 验证探测器按 Rarity 排序
|
||
func TestNewStrategy_RaritySorting(t *testing.T) {
|
||
v := portfinger.GetGlobalVScan()
|
||
|
||
probes := v.GetProbesForPort(80)
|
||
if len(probes) < 2 {
|
||
t.Skip("端口 80 的探测器数量不足,跳过排序测试")
|
||
}
|
||
|
||
// 验证按 rarity 从低到高排序
|
||
for i := 1; i < len(probes); i++ {
|
||
prev := probes[i-1].Rarity
|
||
curr := probes[i].Rarity
|
||
// 0 视为 10
|
||
if prev == 0 {
|
||
prev = 10
|
||
}
|
||
if curr == 0 {
|
||
curr = 10
|
||
}
|
||
if prev > curr {
|
||
t.Errorf("探测器未按 rarity 排序: [%d]=%d > [%d]=%d",
|
||
i-1, probes[i-1].Rarity, i, probes[i].Rarity)
|
||
}
|
||
}
|
||
|
||
t.Logf("✓ 端口 80 的 %d 个探测器已按 rarity 排序", len(probes))
|
||
}
|
||
|
||
// =============================================================================
|
||
// 测试2:SSL 端口探测
|
||
// =============================================================================
|
||
|
||
// TestSSLProbes_Port443 验证 443 端口的 SSL 探测器
|
||
func TestSSLProbes_Port443(t *testing.T) {
|
||
v := portfinger.GetGlobalVScan()
|
||
|
||
// 获取 ports 包含 443 的探测器
|
||
probes := v.GetProbesForPort(443)
|
||
t.Logf("端口 443 的 ports 探测器: %d 个", len(probes))
|
||
|
||
// 获取 sslports 包含 443 的探测器
|
||
sslProbes := v.GetSSLProbesForPort(443)
|
||
t.Logf("端口 443 的 sslports 探测器: %d 个", len(sslProbes))
|
||
|
||
// 至少应该有一些 SSL 相关探测器
|
||
if len(probes) == 0 && len(sslProbes) == 0 {
|
||
t.Error("端口 443 应该有探测器")
|
||
}
|
||
|
||
// 验证 TLSSessionReq 存在
|
||
for _, p := range probes {
|
||
if p.Name == "TLSSessionReq" {
|
||
t.Logf("✓ TLSSessionReq 存在于 ports 列表")
|
||
return
|
||
}
|
||
}
|
||
for _, p := range sslProbes {
|
||
if p.Name == "TLSSessionReq" {
|
||
t.Logf("✓ TLSSessionReq 存在于 sslports 列表")
|
||
return
|
||
}
|
||
}
|
||
}
|
||
|
||
// =============================================================================
|
||
// 测试3:Intensity 过滤
|
||
// =============================================================================
|
||
|
||
// TestIntensityFilter 验证 intensity 过滤功能
|
||
func TestIntensityFilter(t *testing.T) {
|
||
// 创建测试探测器
|
||
probes := []*portfinger.Probe{
|
||
{Name: "p1", Rarity: 1},
|
||
{Name: "p2", Rarity: 5},
|
||
{Name: "p3", Rarity: 9},
|
||
}
|
||
|
||
// intensity=5 应该过滤掉 rarity=9 的探测器
|
||
filtered := portfinger.FilterProbesByIntensity(probes, 5)
|
||
if len(filtered) != 2 {
|
||
t.Errorf("intensity=5 应该返回 2 个探测器,实际返回 %d", len(filtered))
|
||
}
|
||
|
||
// 验证 rarity=9 的探测器被过滤
|
||
for _, p := range filtered {
|
||
if p.Rarity > 5 {
|
||
t.Errorf("rarity=%d 的探测器不应该通过 intensity=5 的过滤", p.Rarity)
|
||
}
|
||
}
|
||
|
||
t.Log("✓ Intensity 过滤功能正常")
|
||
}
|
||
|
||
// =============================================================================
|
||
// 测试4:Scanner 创建和基本功能
|
||
// =============================================================================
|
||
|
||
// TestSmartPortInfoScanner_Creation 验证 Scanner 可以正常创建
|
||
func TestSmartPortInfoScanner_Creation(t *testing.T) {
|
||
config := common.GetGlobalConfig()
|
||
if config == nil {
|
||
config = &common.Config{}
|
||
config.PortMap = make(map[int][]string)
|
||
}
|
||
|
||
// 使用 nil 连接(实际测试中会使用真实连接)
|
||
scanner := NewSmartPortInfoScanner("127.0.0.1", 80, nil, 3*time.Second, config)
|
||
|
||
if scanner == nil {
|
||
t.Fatal("Scanner 创建失败")
|
||
}
|
||
|
||
if scanner.Port != 80 {
|
||
t.Errorf("端口设置错误: 期望 80, 实际 %d", scanner.Port)
|
||
}
|
||
|
||
t.Log("✓ Scanner 创建成功")
|
||
}
|
||
|
||
// =============================================================================
|
||
// 测试5:动态超时常量
|
||
// =============================================================================
|
||
|
||
// TestDefaultConstants 验证默认常量值
|
||
func TestDefaultConstants(t *testing.T) {
|
||
// 验证默认等待时间
|
||
if defaultTotalWaitMS != 6000 {
|
||
t.Errorf("defaultTotalWaitMS 应该是 6000,实际是 %d", defaultTotalWaitMS)
|
||
}
|
||
|
||
// 验证默认 intensity
|
||
if defaultIntensity != 7 {
|
||
t.Errorf("defaultIntensity 应该是 7,实际是 %d", defaultIntensity)
|
||
}
|
||
|
||
t.Logf("✓ 默认常量: TotalWaitMS=%d, Intensity=%d", defaultTotalWaitMS, defaultIntensity)
|
||
}
|
||
|
||
// =============================================================================
|
||
// 测试6:端口范围解析
|
||
// =============================================================================
|
||
|
||
// TestPortInRange 验证端口范围解析
|
||
func TestPortInRange(t *testing.T) {
|
||
tests := []struct {
|
||
port int
|
||
portsStr string
|
||
expected bool
|
||
}{
|
||
{80, "80", true},
|
||
{80, "80,443", true},
|
||
{8080, "8000-9000", true},
|
||
{7999, "8000-9000", false},
|
||
{443, "80,443,8080", true},
|
||
{22, "80,443,8080", false},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
result := portfinger.PortInRange(tt.port, tt.portsStr)
|
||
if result != tt.expected {
|
||
t.Errorf("PortInRange(%d, %q) = %v, want %v",
|
||
tt.port, tt.portsStr, result, tt.expected)
|
||
}
|
||
}
|
||
|
||
t.Log("✓ 端口范围解析功能正常")
|
||
}
|
||
|
||
// =============================================================================
|
||
// 测试7:真实场景模拟
|
||
// =============================================================================
|
||
|
||
// TestRealWorldScenario_CommonPorts 验证常见端口的探测器配置
|
||
func TestRealWorldScenario_CommonPorts(t *testing.T) {
|
||
v := portfinger.GetGlobalVScan()
|
||
|
||
scenarios := []struct {
|
||
port int
|
||
description string
|
||
}{
|
||
{80, "HTTP"},
|
||
{443, "HTTPS"},
|
||
{8080, "HTTP-Alt"},
|
||
{8443, "HTTPS-Alt"},
|
||
}
|
||
|
||
for _, s := range scenarios {
|
||
probes := v.GetProbesForPort(s.port)
|
||
sslProbes := v.GetSSLProbesForPort(s.port)
|
||
total := len(probes) + len(sslProbes)
|
||
|
||
t.Logf("端口 %d (%s): ports=%d, sslports=%d, 总计=%d",
|
||
s.port, s.description, len(probes), len(sslProbes), total)
|
||
}
|
||
}
|