fix+perf: 修复10个bug & 10项性能优化

Bug修复:
- clustersend CEL结果判断从字符串比较改为类型断言
- Nuclei DSL matcher安全降级为false避免误报
- clusterpoc发现漏洞后返回true修正语义
- reverseCheck加10s超时防止ceye API阻塞
- doSearch/bmatches正则编译结果缓存到sync.Map
- evalset CEL求值失败时存空字符串而非原始表达式
- CEL wait()函数加nil Reverse指针检查防panic
- MongoDB readMongoMsg应用timeout参数设置读超时
- TXTWriter.Close确保Sync失败后仍调用file.Close

性能优化:
- 指纹regex缓存从RWMutex+map改为sync.Map消除锁竞争
- CaseInsensitive指纹词加载时预小写化避免匹配时分配
- 版本提取FindAllStringSubmatch限制返回数量
- i18n.Tr用strconv.Itoa替代Sprintf减少分配
- POC加载用atomic.Bool+DCLP消除热路径锁
- 结果缓冲map预分配容量减少rehash
- HTTP连接池参数随并发数动态调整
- getRuleHash去除反射+Headers排序保证确定性dedup
- POC并发加载用channel替代Mutex收集结果
This commit is contained in:
ZacharyZcR
2026-06-13 18:46:14 +08:00
parent 45ebe7040e
commit 6a1636112f
13 changed files with 169 additions and 111 deletions
+30 -30
View File
@@ -13,6 +13,7 @@ import (
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/shadow1ng/fscan/common"
@@ -43,7 +44,7 @@ var (
var pocsFS embed.FS
var (
pocMu sync.Mutex
pocLoaded bool
pocLoaded atomic.Bool
allPocs []*lib.Poc
cachedPocPath string
)
@@ -53,16 +54,18 @@ func WebScan(ctx context.Context, info *common.HostInfo, cfg *common.Config, ses
// 初始化POC配置(用于CEL回调函数)
lib.InitPOCConfig(cfg.DNSLog)
// 加载POC(互斥保护,避免并发 race
pocMu.Lock()
if !pocLoaded {
cachedPocPath = cfg.POC.PocPath
initPocs()
if len(allPocs) > 0 {
pocLoaded = true
// 加载POCDCLP: 快速路径无锁,慢路径互斥保护)
if !pocLoaded.Load() {
pocMu.Lock()
if !pocLoaded.Load() {
cachedPocPath = cfg.POC.PocPath
initPocs()
if len(allPocs) > 0 {
pocLoaded.Store(true)
}
}
pocMu.Unlock()
}
pocMu.Unlock()
// 验证输入
if info == nil {
@@ -316,7 +319,7 @@ func loadExternalPocs(pocPath string) {
loadPocsConcurrently(pocFiles, false, pocPath)
}
// loadPocsConcurrently 并发加载POC文件
// loadPocsConcurrently 并发加载POC文件channel 收集,无锁竞争)
func loadPocsConcurrently(pocFiles []string, isEmbedded bool, pocPath string) {
pocCount := len(pocFiles)
if pocCount == 0 {
@@ -324,48 +327,45 @@ func loadPocsConcurrently(pocFiles []string, isEmbedded bool, pocPath string) {
}
var wg sync.WaitGroup
var mu sync.Mutex
var successCount, failCount int
// 使用信号量控制并发数
results := make(chan *lib.Poc, pocCount)
semaphore := make(chan struct{}, concurrencyLimit)
for _, file := range pocFiles {
wg.Add(1)
semaphore <- struct{}{} // 获取信号量
semaphore <- struct{}{}
go func(filename string) {
defer func() {
<-semaphore // 释放信号量
<-semaphore
wg.Done()
}()
var poc *lib.Poc
var err error
// 根据不同的来源加载POC
if isEmbedded {
poc, err = lib.LoadPoc(filename, pocsFS)
} else {
poc, err = lib.LoadPocbyPath(filename)
}
mu.Lock()
defer mu.Unlock()
if err != nil {
failCount++
return
}
if poc != nil {
allPocs = append(allPocs, poc)
successCount++
if err == nil && poc != nil {
results <- poc
}
}(file)
}
wg.Wait()
go func() {
wg.Wait()
close(results)
}()
var successCount int
for poc := range results {
allPocs = append(allPocs, poc)
successCount++
}
failCount := pocCount - successCount
common.LogInfo(i18n.Tr("poc_load_complete", pocCount, successCount, failCount))
}