Merge pull request #41 from chainreactors/dev

merge v0.9.8
This commit is contained in:
M09Ic 2024-06-24 14:40:36 +08:00 committed by GitHub
commit 0d4a3652ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 754 additions and 440 deletions

View File

@ -33,7 +33,7 @@ jobs:
with: with:
distribution: goreleaser distribution: goreleaser
version: latest version: latest
args: release --rm-dist --skip-validate args: release
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GOPATH: "/home/runner/go" GOPATH: "/home/runner/go"

View File

@ -3,7 +3,6 @@ project_name: spray
before: before:
hooks: hooks:
- go mod tidy - go mod tidy
- go get sigs.k8s.io/yaml
- go generate - go generate
builds: builds:
@ -37,6 +36,9 @@ upx:
- -
enabled: true enabled: true
goos: [linux, windows] goos: [linux, windows]
goarch:
- amd64
- "386"
archives: archives:
- -

View File

@ -14,10 +14,9 @@
* 超强的性能, 在本地测试极限性能的场景下, 能超过ffuf与feroxbruster的性能50%以上. 实际情况受到网络的影响, 感受没有这么明确. 但在多目标下可以感受到明显的区别. * 超强的性能, 在本地测试极限性能的场景下, 能超过ffuf与feroxbruster的性能50%以上. 实际情况受到网络的影响, 感受没有这么明确. 但在多目标下可以感受到明显的区别.
* 基于掩码的字典生成 * 基于掩码的字典生成
* 基于规则的字典生成 * 基于规则的字典生成
* 动态智能过滤 * 动态智能过滤, 自定义过滤策略
* 全量[gogo](https://github.com/chainreactors/gogo)的指纹识别, 全量的[fingerprinthub](https://github.com/0x727/FingerprintHub)指纹 * 全量[gogo](https://github.com/chainreactors/gogo)的指纹识别, 全量的[fingerprinthub](https://github.com/0x727/FingerprintHub),[wappalyzer](https://github.com/projectdiscovery/wappalyzergo)指纹
* 自定义信息提取, 如ip,js, title, hash以及自定义的正则表达式 * 自定义信息提取, 内置敏感信息提取规则
* 自定义过滤策略
* 自定义输出格式与内容 * 自定义输出格式与内容
* *nix的命令行设计, 轻松与其他工具联动 * *nix的命令行设计, 轻松与其他工具联动
* 多角度的自动被ban,被waf判断 * 多角度的自动被ban,被waf判断
@ -27,27 +26,57 @@
[**Document**](https://chainreactors.github.io/wiki/spray/start) [**Document**](https://chainreactors.github.io/wiki/spray/start)
基本使用, 从字典中读取目录进行爆破 ### 基本使用
**从字典中读取目录进行爆破**
`spray -u http://example.com -d wordlist1.txt -d wordlist2.txt` `spray -u http://example.com -d wordlist1.txt -d wordlist2.txt`
通过掩码生成字典进行爆破 **通过掩码生成字典进行爆破**
`spray -u http://example.com -w "/aaa/bbb{?l#4}/ccc"` `spray -u http://example.com -w "/aaa/bbb{?l#4}/ccc"`
通过规则生成字典爆破. 规则文件格式参考hashcat的字典生成规则 **通过规则生成字典爆破**
规则文件格式参考hashcat的字典生成规则
`spray -u http://example.com -r rule.txt -d 1.txt` `spray -u http://example.com -r rule.txt -d 1.txt`
批量爆破 **批量爆破多个目标**
`spray -l url.txt -r rule.txt -d 1.txt` `spray -l url.txt -r rule.txt -d 1.txt`
断点续传 **断点续传**
`spray --resume stat.json` `spray --resume stat.json`
被动url收集 ### 高级用法
**check-only 模式**
类似ehole/httpx这类对单页面信息收集的模式. 会有针对性的性能优化. 默认使用[templates](https://github.com/chainreactors/templates/tree/master/fingers)指纹库. 可以使用`--finger`打开第三方指纹库的匹配
`spray -l url.txt --check-only`
**启用拓展指纹识别**
会进行主动探测常见的指纹目录, 并额外启用fingerprinthub与wappalyzer拓展指纹库
`spray -u http://example.com --finger `
**启用爬虫**
`spray -u http://example.com --crawl`
**扫描备份文件与常见通用文件**
`spray -u http://example.com --bak --common`
**启用所有插件**
`spray -u http://example.com -a`
**被动url收集**
参见: https://github.com/chainreactors/urlfounder 参见: https://github.com/chainreactors/urlfounder
@ -84,14 +113,16 @@ go build .
1. [x] 模糊对比 1. [x] 模糊对比
2. [x] 断点续传 2. [x] 断点续传
3. [x] 简易爬虫 3. [x] 简易爬虫
4. [ ] 支持http2 4. [x] 支持http2
5. [ ] auto-tune, 自动调整并发数量 5. [ ] auto-tune, 自动调整并发数量
6. [x] 可自定义的递归配置 6. [x] 可自定义的递归配置
7. [x] 参考[feroxbuster](https://github.com/epi052/feroxbuster)的`--collect-backups`, 自动爆破有效目录的备份 7. [x] 参考[feroxbuster](https://github.com/epi052/feroxbuster)的`--collect-backups`, 自动爆破有效目录的备份
8. [ ] 支持socks/http代理, 不建议使用, 优先级较低. 代理的keep-alive会带来严重的性能下降 8. [x] 支持socks/http代理, 不建议使用, 优先级较低. 代理的keep-alive会带来严重的性能下降
9. [ ] 云函数化, chainreactors工具链的通用分布式解决方案. 9. [ ] 云函数化, chainreactors工具链的通用分布式解决方案.
## Thanks ## Thanks
* [fuzzuli](https://github.com/musana/fuzzuli) 提供了一个备份文件字典生成思路 * [fuzzuli](https://github.com/musana/fuzzuli) 提供了一个备份文件字典生成思路
* [fingerprinthub](https://github.com/0x727/FingerprintHub) 作为指纹库的补充 * [fingerprinthub](https://github.com/0x727/FingerprintHub) 作为指纹库的补充
* [wappalyzer](https://github.com/projectdiscovery/wappalyzergo) 作为指纹库补充
* [dirsearch](https://github.com/maurosoria/dirsearch) 提供了默认字典

View File

@ -3,6 +3,7 @@ package cmd
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/chainreactors/files"
"github.com/chainreactors/logs" "github.com/chainreactors/logs"
"github.com/chainreactors/spray/internal" "github.com/chainreactors/spray/internal"
"github.com/chainreactors/spray/internal/ihttp" "github.com/chainreactors/spray/internal/ihttp"
@ -16,10 +17,29 @@ import (
"time" "time"
) )
var ver = "v0.9.5" var ver = "v0.9.6"
var DefaultConfig = "config.yaml"
func init() {
logs.Log.SetColorMap(map[logs.Level]func(string) string{
logs.Info: logs.PurpleBold,
logs.Important: logs.GreenBold,
pkg.LogVerbose: logs.Green,
})
}
func Spray() { func Spray() {
var option internal.Option var option internal.Option
if files.IsExist(DefaultConfig) {
logs.Log.Debug("config.yaml exist, loading")
err := internal.LoadConfig(DefaultConfig, &option)
if err != nil {
logs.Log.Error(err.Error())
return
}
}
parser := flags.NewParser(&option, flags.Default) parser := flags.NewParser(&option, flags.Default)
parser.Usage = ` parser.Usage = `
@ -57,19 +77,30 @@ func Spray() {
} else if len(option.Verbose) > 0 { } else if len(option.Verbose) > 0 {
logs.Log.SetLevel(pkg.LogVerbose) logs.Log.SetLevel(pkg.LogVerbose)
} }
if option.InitConfig {
logs.Log.SetColorMap(map[logs.Level]func(string) string{ configStr := internal.InitDefaultConfig(&option, 0)
logs.Info: logs.PurpleBold, err := os.WriteFile(DefaultConfig, []byte(configStr), 0o744)
logs.Important: logs.GreenBold, if err != nil {
pkg.LogVerbose: logs.Green, logs.Log.Warn("cannot create config: config.yaml, " + err.Error())
}) return
}
if files.IsExist(DefaultConfig) {
logs.Log.Warn("override default config: ./config.yaml")
}
logs.Log.Info("init default config: ./config.yaml")
return
}
if option.Config != "" { if option.Config != "" {
err := internal.LoadConfig(option.Config, &option) err := internal.LoadConfig(option.Config, &option)
if err != nil { if err != nil {
logs.Log.Error(err.Error()) logs.Log.Error(err.Error())
return return
} }
if files.IsExist(DefaultConfig) {
logs.Log.Warnf("custom config %s, override default config", option.Config)
} else {
logs.Log.Important("load config: " + option.Config)
}
} }
if option.Version { if option.Version {
@ -89,7 +120,12 @@ func Spray() {
// 初始化全局变量 // 初始化全局变量
pkg.Distance = uint8(option.SimhashDistance) pkg.Distance = uint8(option.SimhashDistance)
if option.MaxBodyLength == -1 {
ihttp.DefaultMaxBodySize = -1
} else {
ihttp.DefaultMaxBodySize = option.MaxBodyLength * 1024 ihttp.DefaultMaxBodySize = option.MaxBodyLength * 1024
}
pool.MaxCrawl = option.CrawlDepth pool.MaxCrawl = option.CrawlDepth
var runner *internal.Runner var runner *internal.Runner
@ -103,7 +139,7 @@ func Spray() {
return return
} }
if option.ReadAll || runner.Crawl { if option.ReadAll || runner.Crawl {
ihttp.DefaultMaxBodySize = 0 ihttp.DefaultMaxBodySize = -1
} }
ctx, canceler := context.WithTimeout(context.Background(), time.Duration(runner.Deadline)*time.Second) ctx, canceler := context.WithTimeout(context.Background(), time.Duration(runner.Deadline)*time.Second)

View File

@ -1,94 +1,154 @@
input: input:
append-files: [] # Files, when found valid path, use append file new word with current path # Files, Multi,dict files, e.g.: -d 1.txt -d 2.txt
append-rules: [] # Files, when found valid path, use append rule generator new word with current path dictionaries: []
dictionaries: [] # Files, Multi,dict files, e.g.: -d 1.txt -d 2.txt # Bool, no dictionary
filter-rule: "" # String, filter rule, e.g.: --rule-filter '>8 <4' no-dict: false
rules: [] # Files, rule files, e.g.: -r rule1.txt -r rule2.txt # String, word generate dsl, e.g.: -w test{?ld#4}
word: "" # String, word generate dsl, e.g.: -w test{?ld#4} word: ""
# Files, rule files, e.g.: -r rule1.txt -r rule2.txt
rules: []
# Files, when found valid path , use append rule generator new word with current path
append-rules: []
# String, filter rule, e.g.: --rule-filter '>8 <4'
filter-rule: ""
# Files, when found valid path , use append file new word with current path
append-files: []
functions: functions:
extension: "" # String, add extensions (separated by commas), e.g.: -e jsp,jspx # String, add extensions (separated by commas), e.g.: -e jsp,jspx
exclude-extension: "" # String, exclude extensions (separated by commas), e.g.: --exclude-extension jsp,jspx extension: ""
force-extension: false # Bool, force add extensions # Bool, force add extensions
remove-extension: "" # String, remove extensions (separated by commas), e.g.: --remove-extension jsp,jspx force-extension: false
prefix: [] # Strings, add prefix, e.g.: --prefix aaa --prefix bbb # String, exclude extensions (separated by commas), e.g.: --exclude-extension jsp,jspx
suffix: [] # Strings, add suffix, e.g.: --suffix aaa --suffix bbb exclude-extension: ""
upper: false # Bool, upper wordlist, e.g.: --uppercase # String, remove extensions (separated by commas), e.g.: --remove-extension jsp,jspx
lower: false # Bool, lower wordlist, e.g.: --lowercase remove-extension: ""
replace: null # Strings, replace string, e.g.: --replace aaa:bbb --replace ccc:ddd # Bool, upper wordlist, e.g.: --uppercase
skip: [ ] # String, skip word when generate. rule, e.g.: --skip aaa upper: false
# Bool, lower wordlist, e.g.: --lowercase
misc: lower: false
mod: path # String, path/host spray # Strings, add prefix, e.g.: --prefix aaa --prefix bbb
client: auto # String, Client type prefix: []
thread: 20 # Int, number of threads per pool # Strings, add suffix, e.g.: --suffix aaa --suffix bbb
pool: 5 # Int, Pool size suffix: []
timeout: 5 # Int, timeout with request (seconds) # Strings, replace string, e.g.: --replace aaa:bbb --replace ccc:ddd
deadline: 999999 # Int, deadline (seconds) replace: {}
proxy: "" # String, proxy address, e.g.: --proxy socks5://127.0.0.1:1080 # String, skip word when generate. rule, e.g.: --skip aaa
quiet: false # Bool, Quiet skip: []
debug: false # Bool, output debug info
verbose: [] # Bool, log verbose level, default 0, level1: -v, level2 -vv
no-bar: false # Bool, No progress bar
no-color: false # Bool, no color
mode:
# status
black-status: "400,410" # Strings (comma split), custom black status
fuzzy-status: "500,501,502,503" # Strings (comma split), custom fuzzy status
unique-status: "403,200,404" # Strings (comma split), custom unique status
white-status: "200" # Strings (comma split), custom white status
# check
check-only: false # Bool, check only
check-period: 200 # Int, check period when request
error-period: 10 # Int, check period when error
error-threshold: 20 # Int, break when the error exceeds the threshold
# recursive
recursive: current.IsDir() # String, custom recursive rule, e.g.: --recursive current.IsDir()
depth: 0 # Int, recursive depth
# crawl
scope: [] # String, custom scope, e.g.: --scope *.example.com
no-scope: false # Bool, no scope
# other
index: / # String, custom index path
random: "" # String, custom random path
unique: false # Bool, unique response
distance: 5 # Int, simhash distance for unique response
force: false # Bool, skip error break
rate-limit: 0 # Int, request rate limit (rate/s), e.g.: --rate-limit 100
retry: 0 # Int, retry count
output: output:
output-file: "" # String, output filename # String, custom match function, e.g.: --match 'current.Status != 200''
auto-file: false # Bool, auto generator output and fuzzy filename match: ""
dump: false # Bool, dump all request # String, custom filter function, e.g.: --filter 'current.Body contains "hello"'
dump-file: "" # String, dump all request, and write to filename filter: ""
fuzzy: false # Bool, open fuzzy output # String, open fuzzy output
fuzzy-file: "" # String, fuzzy output filename fuzzy: false
filter: "" # String, custom filter function, e.g.: --filter 'current.Body contains "hello"' # String, output filename
match: "" # String, custom match function, e.g.: --match 'current.Status != 200'' output-file: ""
format: "" # String, output format, e.g.: --format 1.json # String, fuzzy output filename
output_probe: "" # String, output probes fuzzy-file: ""
# String, dump all request, and write to filename
dump-file: ""
# Bool, dump all request
dump: false
# Bool, auto generator output and fuzzy filename
auto-file: false
# String, output format, e.g.: --format 1.json
format: ""
# String, output format
output_probe: ""
# Bool, Quiet
quiet: false
# Bool, no color
no-color: false
# Bool, No progress bar
no-bar: false
plugins: plugins:
all: false # Bool, enable all plugin # Bool, enable all plugin
bak: false # Bool, enable bak found all: false
common: false # Bool, enable common file found # Strings, extract response, e.g.: --extract js --extract ip --extract version:(.*?)
crawl: false # Bool, enable crawl extract: []
crawl-depth: 3 # Int, crawl depth # String, extract config filename
extract: [] # Strings, extract response, e.g.: --extract js --extract ip --extract version:(.*?) extract-config: ""
file-bak: false # Bool, enable valid result bak found, equal --append-rule rule/filebak.txt # Bool, enable recon
finger: false # Bool, enable active finger detect recon: false
recon: false # Bool, enable recon # Bool, enable active finger detect
finger: false
# Bool, enable bak found
bak: false
# Bool, enable valid result bak found, equal --append-rule rule/filebak.txt
file-bak: false
# Bool, enable common file found
common: false
# Bool, enable crawl
crawl: false
# Int, crawl depth
crawl-depth: 3
request: request:
cookies: [] # Strings, custom cookie # Strings, custom headers, e.g.: --headers 'Auth: example_auth'
headers: [] # Strings, custom headers, e.g.: --headers 'Auth: example_auth' headers: []
max-body-length: 100 # Int, max response body length (kb), default 100k, e.g. -max-length 1000 # String, custom user-agent, e.g.: --user-agent Custom
useragent: "" # String, custom user-agent, e.g.: --user-agent Custom useragent: ""
random-useragent: false # Bool, use random with default user-agent # Bool, use random with default user-agent
read-all: false # Bool, read all response body random-useragent: false
# Strings, custom cookie
cookies: []
# Bool, read all response body
read-all: false
# Int, max response body length (kb), -1 read-all, 0 not read body, default 100k, e.g. --max-length 1000
max-length: 100
mode:
# Int, request rate limit (rate/s), e.g.: --rate-limit 100
rate-limit: 0
# Bool, skip error break
force: false
# Bool, check only
check-only: false
# Bool, no scope
no-scope: false
# String, custom scope, e.g.: --scope *.example.com
scope: []
# String,custom recursive rule, e.g.: --recursive current.IsDir()
recursive: current.IsDir()
# Int, recursive depth
depth: 0
# String, custom index path
index: /
# String, custom random path
random: ""
# Int, check period when request
check-period: 200
# Int, check period when error
error-period: 10
# Int, break when the error exceeds the threshold
error-threshold: 20
# Strings (comma split),custom black status
black-status: 400,410
# Strings (comma split), custom white status
white-status: 200
# Strings (comma split), custom fuzzy status
fuzzy-status: 500,501,502,503
# Strings (comma split), custom unique status
unique-status: 403,200,404
# Bool, unique response
unique: false
# Int, retry count
retry: 0
distance: 5
misc:
# String, path/host spray
mod: path
# String, Client type
client: auto
# Int, deadline (seconds)
deadline: 999999
# Int, timeout with request (seconds)
timeout: 5
# Int, Pool size
pool: 5
# Int, number of threads per pool
thread: 20
# Bool, output debug info
debug: false
# Bool, log verbose level ,default 0, level1: -v level2 -vv
verbose: []
# String, proxy address, e.g.: --proxy socks5://127.0.0.1:1080
proxy: ""

54
go.mod
View File

@ -1,49 +1,57 @@
module github.com/chainreactors/spray module github.com/chainreactors/spray
go 1.21.3 go 1.22
require github.com/chainreactors/go-metrics v0.0.0-20220926021830-24787b7a10f8 toolchain go1.22.2
require ( require (
github.com/antonmedv/expr v1.12.5
github.com/chainreactors/files v0.0.0-20231123083421-cea5b4ad18a8 github.com/chainreactors/files v0.0.0-20231123083421-cea5b4ad18a8
github.com/chainreactors/fingers v0.0.0-20240304115656-fa8ca9fc375f github.com/chainreactors/fingers v0.0.0-20240603064620-e83951a40541
github.com/chainreactors/logs v0.0.0-20240207121836-c946f072f81f github.com/chainreactors/logs v0.0.0-20240207121836-c946f072f81f
github.com/chainreactors/parsers v0.0.0-20240304115854-f71473f7b510 github.com/chainreactors/parsers v0.0.0-20240422094636-b88693700dfc
github.com/chainreactors/utils v0.0.0-20240302165634-2b8494c9cfc3 github.com/chainreactors/utils v0.0.0-20240528085651-ba1b255482c1
github.com/chainreactors/words v0.4.1-0.20240220104223-153f52e53f37 github.com/chainreactors/words v0.4.1-0.20240510105042-5ba5c2edc508
github.com/goccy/go-yaml v1.11.2 github.com/expr-lang/expr v1.16.9
github.com/gookit/config/v2 v2.2.5 github.com/gookit/config/v2 v2.2.5
github.com/gosuri/uiprogress v0.0.1
github.com/jessevdk/go-flags v1.5.0 github.com/jessevdk/go-flags v1.5.0
github.com/panjf2000/ants/v2 v2.7.0 github.com/panjf2000/ants/v2 v2.9.1
github.com/valyala/fasthttp v1.43.0 github.com/valyala/fasthttp v1.53.0
golang.org/x/net v0.21.0 github.com/vbauerster/mpb/v8 v8.7.3
golang.org/x/time v0.3.0 golang.org/x/net v0.25.0
golang.org/x/time v0.5.0
sigs.k8s.io/yaml v1.4.0 sigs.k8s.io/yaml v1.4.0
) )
require ( require (
dario.cat/mergo v1.0.0 // indirect dario.cat/mergo v1.0.0 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/brotli v1.1.0 // indirect
github.com/fatih/color v1.15.0 // indirect github.com/fatih/color v1.17.0 // indirect
github.com/go-dedup/megophone v0.0.0-20170830025436-f01be21026f5 // indirect github.com/go-dedup/megophone v0.0.0-20170830025436-f01be21026f5 // indirect
github.com/go-dedup/simhash v0.0.0-20170904020510-9ecaca7b509c // indirect github.com/go-dedup/simhash v0.0.0-20170904020510-9ecaca7b509c // indirect
github.com/go-dedup/text v0.0.0-20170907015346-8bb1b95e3cb7 // indirect github.com/go-dedup/text v0.0.0-20170907015346-8bb1b95e3cb7 // indirect
github.com/go-playground/validator/v10 v10.14.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-yaml v1.11.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/gookit/color v1.5.4 // indirect github.com/gookit/color v1.5.4 // indirect
github.com/gookit/goutil v0.6.15 // indirect github.com/gookit/goutil v0.6.15 // indirect
github.com/gosuri/uilive v0.0.4 // indirect github.com/klauspost/compress v1.17.8 // indirect
github.com/klauspost/compress v1.17.4 // indirect github.com/kr/pretty v0.3.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/twmb/murmur3 v1.1.8 // indirect github.com/twmb/murmur3 v1.1.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sync v0.6.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
golang.org/x/sys v0.17.0 // indirect golang.org/x/sync v0.7.0 // indirect
golang.org/x/term v0.17.0 // indirect golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/term v0.20.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect golang.org/x/text v0.15.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
) )

132
go.sum
View File

@ -1,34 +1,38 @@
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/antonmedv/expr v1.12.5 h1:Fq4okale9swwL3OeLLs9WD9H6GbgBLJyN/NUHRv+n0E=
github.com/antonmedv/expr v1.12.5/go.mod h1:FPC8iWArxls7axbVLsW+kpg1mz29A1b2M6jt+hZfDkU=
github.com/chainreactors/files v0.0.0-20230731174853-acee21c8c45a/go.mod h1:/Xa9YXhjBlaC33JTD6ZTJFig6pcplak2IDcovf42/6A= github.com/chainreactors/files v0.0.0-20230731174853-acee21c8c45a/go.mod h1:/Xa9YXhjBlaC33JTD6ZTJFig6pcplak2IDcovf42/6A=
github.com/chainreactors/files v0.0.0-20231102192550-a652458cee26/go.mod h1:/Xa9YXhjBlaC33JTD6ZTJFig6pcplak2IDcovf42/6A= github.com/chainreactors/files v0.0.0-20231102192550-a652458cee26/go.mod h1:/Xa9YXhjBlaC33JTD6ZTJFig6pcplak2IDcovf42/6A=
github.com/chainreactors/files v0.0.0-20231123083421-cea5b4ad18a8 h1:8Plpi6haQbU8NzH+JtU6bkGDWF/OeC+GFj8DIDuY5yk= github.com/chainreactors/files v0.0.0-20231123083421-cea5b4ad18a8 h1:8Plpi6haQbU8NzH+JtU6bkGDWF/OeC+GFj8DIDuY5yk=
github.com/chainreactors/files v0.0.0-20231123083421-cea5b4ad18a8/go.mod h1:/Xa9YXhjBlaC33JTD6ZTJFig6pcplak2IDcovf42/6A= github.com/chainreactors/files v0.0.0-20231123083421-cea5b4ad18a8/go.mod h1:/Xa9YXhjBlaC33JTD6ZTJFig6pcplak2IDcovf42/6A=
github.com/chainreactors/fingers v0.0.0-20240304115656-fa8ca9fc375f h1:dkg/RUxRptaUdP8ZoQ9maN0vDyNSSuf8k4mTOHddD7c=
github.com/chainreactors/fingers v0.0.0-20240304115656-fa8ca9fc375f/go.mod h1:cO2a79lRNSaM6hu17xIyws5eWCWxjcRxY9IFPlss2lE= github.com/chainreactors/fingers v0.0.0-20240304115656-fa8ca9fc375f/go.mod h1:cO2a79lRNSaM6hu17xIyws5eWCWxjcRxY9IFPlss2lE=
github.com/chainreactors/go-metrics v0.0.0-20220926021830-24787b7a10f8 h1:kMFr1Hj+rkp1wBPIw2pcQvelO5GnA7r7wY3h6vJ1joA= github.com/chainreactors/fingers v0.0.0-20240603064620-e83951a40541 h1:aDY5A+G53En6t3Pr4tbl+vxJle2p1VJsqHVlyhnWU8s=
github.com/chainreactors/go-metrics v0.0.0-20220926021830-24787b7a10f8/go.mod h1:7NDvFERNiXsujaBPD6s4WXj52uKdfnF2zVHQtKXIEV4= github.com/chainreactors/fingers v0.0.0-20240603064620-e83951a40541/go.mod h1:s3lvNYcSW7NfM1inpgyn/wY3UEqQIvp6gE6BthFaOVo=
github.com/chainreactors/logs v0.0.0-20231027080134-7a11bb413460/go.mod h1:VZFqkFDGmp7/JOMeraW+YI7kTGcgz9fgc/HArVFnrGQ= github.com/chainreactors/logs v0.0.0-20231027080134-7a11bb413460/go.mod h1:VZFqkFDGmp7/JOMeraW+YI7kTGcgz9fgc/HArVFnrGQ=
github.com/chainreactors/logs v0.0.0-20240207121836-c946f072f81f h1:tcfp+CEdgiMvjyUzWab5edJtxUwRMSMEIkLybupIx0k= github.com/chainreactors/logs v0.0.0-20240207121836-c946f072f81f h1:tcfp+CEdgiMvjyUzWab5edJtxUwRMSMEIkLybupIx0k=
github.com/chainreactors/logs v0.0.0-20240207121836-c946f072f81f/go.mod h1:6Mv6W70JrtL6VClulZhmMRZnoYpcTahcDTKLMNEjK0o= github.com/chainreactors/logs v0.0.0-20240207121836-c946f072f81f/go.mod h1:6Mv6W70JrtL6VClulZhmMRZnoYpcTahcDTKLMNEjK0o=
github.com/chainreactors/parsers v0.0.0-20240304115854-f71473f7b510 h1:4o04P6MSAWkW1my4ogThJQgc0LeStaZo+3EvV24P+cU= github.com/chainreactors/parsers v0.0.0-20240422094636-b88693700dfc h1:lGgglOE1FGWD7gVZuF0cufxd7i9HJ2gltUewxXCfvs4=
github.com/chainreactors/parsers v0.0.0-20240304115854-f71473f7b510/go.mod h1:BuI21VlpmYHFr1jva/IN5I5jFvvCtYRyeldGK80wYCg= github.com/chainreactors/parsers v0.0.0-20240422094636-b88693700dfc/go.mod h1:BuI21VlpmYHFr1jva/IN5I5jFvvCtYRyeldGK80wYCg=
github.com/chainreactors/utils v0.0.0-20240302165634-2b8494c9cfc3 h1:oFpBQBegsjNhKrzcjJUrqOgIJoa3Bc9OUmdRmYFHcXg=
github.com/chainreactors/utils v0.0.0-20240302165634-2b8494c9cfc3/go.mod h1:JA4eiQZm+7AsfjXBcIzIdVKBEhDCb16eNtWFCGTxlvs= github.com/chainreactors/utils v0.0.0-20240302165634-2b8494c9cfc3/go.mod h1:JA4eiQZm+7AsfjXBcIzIdVKBEhDCb16eNtWFCGTxlvs=
github.com/chainreactors/words v0.4.1-0.20240220104223-153f52e53f37 h1:QdH1w8MnoAEnXp+CGqwroCRhAs+gu5OnIyW+qnK8Ibg= github.com/chainreactors/utils v0.0.0-20240528085651-ba1b255482c1 h1:+awuysRKLmdLQbVK+HPSOGvO3dFGdNSbM2jyLh+VYOA=
github.com/chainreactors/words v0.4.1-0.20240220104223-153f52e53f37/go.mod h1:DUDx7PdsMEm5PvVhzkFyppzpiUhQb8dOJaWjVc1SMVk= github.com/chainreactors/utils v0.0.0-20240528085651-ba1b255482c1/go.mod h1:JA4eiQZm+7AsfjXBcIzIdVKBEhDCb16eNtWFCGTxlvs=
github.com/chainreactors/words v0.4.1-0.20240510105042-5ba5c2edc508 h1:iT4HWkoZzUAfQYcQMRH8XyrMau9tCVE0zSuFQnkhrqw=
github.com/chainreactors/words v0.4.1-0.20240510105042-5ba5c2edc508/go.mod h1:DUDx7PdsMEm5PvVhzkFyppzpiUhQb8dOJaWjVc1SMVk=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-dedup/megophone v0.0.0-20170830025436-f01be21026f5 h1:4U+x+EB1P66zwYgTjxWXSOT8vF+651Ksr1lojiCZnT8= github.com/go-dedup/megophone v0.0.0-20170830025436-f01be21026f5 h1:4U+x+EB1P66zwYgTjxWXSOT8vF+651Ksr1lojiCZnT8=
github.com/go-dedup/megophone v0.0.0-20170830025436-f01be21026f5/go.mod h1:poR/Cp00iqtqu9ltFwl6C00sKC0HY13u/Gh05ZBmP54= github.com/go-dedup/megophone v0.0.0-20170830025436-f01be21026f5/go.mod h1:poR/Cp00iqtqu9ltFwl6C00sKC0HY13u/Gh05ZBmP54=
github.com/go-dedup/simhash v0.0.0-20170904020510-9ecaca7b509c h1:mucYYQn+sMGNSxidhleonzAdwL203RxhjJGnxQU4NWU= github.com/go-dedup/simhash v0.0.0-20170904020510-9ecaca7b509c h1:mucYYQn+sMGNSxidhleonzAdwL203RxhjJGnxQU4NWU=
@ -39,12 +43,13 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/goccy/go-yaml v1.11.2 h1:joq77SxuyIs9zzxEjgyLBugMQ9NEgTWxXfz2wVqwAaQ= github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I=
github.com/goccy/go-yaml v1.11.2/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gookit/config/v2 v2.2.5 h1:RECbYYbtherywmzn3LNeu9NA5ZqhD7MSKEMsJ7l+MpU= github.com/gookit/config/v2 v2.2.5 h1:RECbYYbtherywmzn3LNeu9NA5ZqhD7MSKEMsJ7l+MpU=
@ -53,35 +58,46 @@ github.com/gookit/goutil v0.6.15 h1:mMQ0ElojNZoyPD0eVROk5QXJPh2uKR4g06slgPDF5Jo=
github.com/gookit/goutil v0.6.15/go.mod h1:qdKdYEHQdEtyH+4fNdQNZfJHhI0jUZzHxQVAV3DaMDY= github.com/gookit/goutil v0.6.15/go.mod h1:qdKdYEHQdEtyH+4fNdQNZfJHhI0jUZzHxQVAV3DaMDY=
github.com/gookit/ini/v2 v2.2.3 h1:nSbN+x9OfQPcMObTFP+XuHt8ev6ndv/fWWqxFhPMu2E= github.com/gookit/ini/v2 v2.2.3 h1:nSbN+x9OfQPcMObTFP+XuHt8ev6ndv/fWWqxFhPMu2E=
github.com/gookit/ini/v2 v2.2.3/go.mod h1:Vu6p7P7xcfmb8KYu3L0ek8bqu/Im63N81q208SCCZY4= github.com/gookit/ini/v2 v2.2.3/go.mod h1:Vu6p7P7xcfmb8KYu3L0ek8bqu/Im63N81q208SCCZY4=
github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY=
github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI=
github.com/gosuri/uiprogress v0.0.1 h1:0kpv/XY/qTmFWl/SkaJykZXrBBzwwadmW8fRb7RJSxw=
github.com/gosuri/uiprogress v0.0.1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0=
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/panjf2000/ants/v2 v2.7.0 h1:Y3Bgpfo9HDkBoHNVFbMfY5mAvi5TAA17y3HbzQ74p5Y= github.com/panjf2000/ants/v2 v2.9.1 h1:Q5vh5xohbsZXGcD6hhszzGqB7jSSc2/CRr3QKIga8Kw=
github.com/panjf2000/ants/v2 v2.7.0/go.mod h1:KIBmYG9QQX5U2qzFP/yQJaq/nSb6rahS9iEHkrCMgM8= github.com/panjf2000/ants/v2 v2.9.1/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
@ -89,74 +105,76 @@ github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.43.0 h1:Gy4sb32C98fbzVWZlTM1oTMdLWGyvxR03VhM6cBIU4g= github.com/valyala/fasthttp v1.53.0 h1:lW/+SUkOxCx2vlIu0iaImv4JLrVRnbbkpCoaawvA4zc=
github.com/valyala/fasthttp v1.43.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= github.com/valyala/fasthttp v1.53.0/go.mod h1:6dt4/8olwq9QARP/TDuPmWyWcl4byhpvTJ4AAtcz+QM=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vbauerster/mpb/v8 v8.7.3 h1:n/mKPBav4FFWp5fH4U0lPpXfiOmCEgl5Yx/NM3tKJA0=
github.com/vbauerster/mpb/v8 v8.7.3/go.mod h1:9nFlNpDGVoTmQ4QvNjSLtwLmAFjwmq0XaAF26toHGNM=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -2,10 +2,10 @@ package internal
import ( import (
"fmt" "fmt"
"github.com/goccy/go-yaml"
"github.com/gookit/config/v2" "github.com/gookit/config/v2"
"reflect" "reflect"
"strconv" "strconv"
"strings"
) )
//var ( //var (
@ -101,14 +101,14 @@ func setFieldValue(field reflect.Value) interface{} {
} }
} }
// extractConfigAndDefaults 提取带有 `config` 和 `default` 标签的字段 func extractConfigAndDefaults(v reflect.Value, result map[string]interface{}, comments map[string]string) {
func extractConfigAndDefaults(v reflect.Value, result map[string]interface{}) {
t := v.Type() t := v.Type()
for i := 0; i < v.NumField(); i++ { for i := 0; i < v.NumField(); i++ {
field := v.Field(i) field := v.Field(i)
fieldType := t.Field(i) fieldType := t.Field(i)
configTag := fieldType.Tag.Get("config") configTag := fieldType.Tag.Get("config")
defaultTag := fieldType.Tag.Get("default") defaultTag := fieldType.Tag.Get("default")
descriptionTag := fieldType.Tag.Get("description") // 读取description标签
if configTag != "" { if configTag != "" {
var value interface{} var value interface{}
@ -117,30 +117,86 @@ func extractConfigAndDefaults(v reflect.Value, result map[string]interface{}) {
} else { } else {
value = setFieldValue(field) value = setFieldValue(field)
} }
fullPath := configTag // 在递归情况下,您可能需要构建完整的路径
if field.Kind() == reflect.Struct { if field.Kind() == reflect.Struct {
nestedResult := make(map[string]interface{}) nestedResult := make(map[string]interface{})
extractConfigAndDefaults(field, nestedResult) nestedComments := make(map[string]string)
extractConfigAndDefaults(field, nestedResult, nestedComments)
result[configTag] = nestedResult result[configTag] = nestedResult
for k, v := range nestedComments {
comments[fullPath+"."+k] = v // 保留嵌套注释的路径
}
} else { } else {
result[configTag] = value result[configTag] = value
if descriptionTag != "" {
comments[fullPath] = descriptionTag
}
} }
} }
} }
} }
func initDefaultConfig(cfg interface{}) (string, error) { func InitDefaultConfig(cfg interface{}, indentLevel int) string {
var yamlStr strings.Builder
v := reflect.ValueOf(cfg) v := reflect.ValueOf(cfg)
if v.Kind() != reflect.Struct { if v.Kind() == reflect.Ptr {
return "", fmt.Errorf("expected a struct, got %s", v.Kind()) v = v.Elem() // 解引用指针
}
t := v.Type()
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
fieldType := t.Field(i)
configTag := fieldType.Tag.Get("config")
if configTag == "" {
continue // 忽略没有config标签的字段
} }
result := make(map[string]interface{}) defaultTag := fieldType.Tag.Get("default")
extractConfigAndDefaults(v, result) descriptionTag := fieldType.Tag.Get("description")
yamlData, err := yaml.Marshal(result) // 添加注释
if err != nil { if descriptionTag != "" {
return "", err yamlStr.WriteString(fmt.Sprintf("%s# %s\n", strings.Repeat(" ", indentLevel*2), descriptionTag))
} }
return string(yamlData), nil // 准备值
valueStr := prepareValue(fieldType.Type.Kind(), defaultTag)
// 根据字段类型进行处理
switch field.Kind() {
case reflect.Struct:
// 对于嵌套结构体递归生成YAML
yamlStr.WriteString(fmt.Sprintf("%s%s:\n%s", strings.Repeat(" ", indentLevel*2), configTag, InitDefaultConfig(field.Interface(), indentLevel+1)))
default:
// 直接生成键值对
yamlStr.WriteString(fmt.Sprintf("%s%s: %s\n", strings.Repeat(" ", indentLevel*2), configTag, valueStr))
}
}
return yamlStr.String()
}
// prepareValue 根据字段类型和default标签的值准备最终的值字符串
func prepareValue(kind reflect.Kind, defaultVal string) string {
if defaultVal != "" {
return defaultVal
}
// 根据类型返回默认空值
switch kind {
case reflect.Bool:
return "false"
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return "0"
case reflect.Float32, reflect.Float64:
return "0.0"
case reflect.Slice, reflect.Array:
return "[]"
case reflect.String:
return `""`
case reflect.Struct, reflect.Map:
return "{}"
default:
return `""`
}
} }

View File

@ -16,9 +16,19 @@ import (
) )
var ( var (
DefaultMaxBodySize = 1024 * 100 // 100k DefaultMaxBodySize int64 = 1024 * 100 // 100k
) )
func CheckBodySize(size int64) bool {
if DefaultMaxBodySize == -1 {
return true
}
if DefaultMaxBodySize == 0 {
return false
}
return size < DefaultMaxBodySize
}
const ( const (
Auto = iota Auto = iota
FAST FAST
@ -38,10 +48,10 @@ func NewClient(config *ClientConfig) *Client {
MaxConnsPerHost: config.Thread * 3 / 2, MaxConnsPerHost: config.Thread * 3 / 2,
MaxIdleConnDuration: config.Timeout, MaxIdleConnDuration: config.Timeout,
//MaxConnWaitTimeout: time.Duration(timeout) * time.Second, //MaxConnWaitTimeout: time.Duration(timeout) * time.Second,
//ReadTimeout: time.Duration(timeout) * time.Second, //ReadTimeout: config.Timeout * time.Second,
//WriteTimeout: time.Duration(timeout) * time.Second, //WriteTimeout: config.Timeout * time.Second,
ReadBufferSize: 16384, // 16k ReadBufferSize: 16384, // 16k
MaxResponseBodySize: DefaultMaxBodySize, MaxResponseBodySize: int(DefaultMaxBodySize),
NoDefaultUserAgentHeader: true, NoDefaultUserAgentHeader: true,
DisablePathNormalizing: true, DisablePathNormalizing: true,
DisableHeaderNamesNormalizing: true, DisableHeaderNamesNormalizing: true,
@ -58,6 +68,7 @@ func NewClient(config *ClientConfig) *Client {
Renegotiation: tls.RenegotiateOnceAsClient, Renegotiation: tls.RenegotiateOnceAsClient,
InsecureSkipVerify: true, InsecureSkipVerify: true,
}, },
TLSHandshakeTimeout: config.Timeout,
MaxConnsPerHost: config.Thread * 3 / 2, MaxConnsPerHost: config.Thread * 3 / 2,
IdleConnTimeout: config.Timeout, IdleConnTimeout: config.Timeout,
ReadBufferSize: 16384, // 16k ReadBufferSize: 16384, // 16k
@ -133,9 +144,17 @@ func customDialFunc(proxyAddr string, timeout time.Duration) fasthttp.DialFunc {
return nil return nil
} }
if strings.ToLower(u.Scheme) == "socks5" { if strings.ToLower(u.Scheme) == "socks5" {
return func(addr string) (net.Conn, error) { return func(addr string) (net.Conn, error) {
dialer, err := proxy.SOCKS5("tcp", u.Host, nil, proxy.Direct) var auth *proxy.Auth
username := u.User.Username()
password, ok := u.User.Password()
if ok {
auth = &proxy.Auth{
User: username,
Password: password,
}
}
dialer, err := proxy.SOCKS5("tcp", u.Host, auth, proxy.Direct)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -155,6 +174,6 @@ func customDialFunc(proxyAddr string, timeout time.Duration) fasthttp.DialFunc {
return conn, nil return conn, nil
} }
} else { } else {
return fasthttpproxy.FasthttpHTTPDialerTimeout(proxyAddr, timeout) return fasthttpproxy.FasthttpHTTPDialerTimeout(u.Host, timeout)
} }
} }

View File

@ -5,13 +5,14 @@ import (
"net/http" "net/http"
) )
func BuildPathRequest(clientType int, base, path string) (*Request, error) { func BuildPathRequest(clientType int, base, path, method string) (*Request, error) {
if clientType == FAST { if clientType == FAST {
req := fasthttp.AcquireRequest() req := fasthttp.AcquireRequest()
req.Header.SetMethod(method)
req.SetRequestURI(base + path) req.SetRequestURI(base + path)
return &Request{FastRequest: req, ClientType: FAST}, nil return &Request{FastRequest: req, ClientType: FAST}, nil
} else { } else {
req, err := http.NewRequest("GET", base+path, nil) req, err := http.NewRequest(method, base+path, nil)
return &Request{StandardRequest: req, ClientType: STANDARD}, err return &Request{StandardRequest: req, ClientType: STANDARD}, err
} }
} }

View File

@ -29,7 +29,7 @@ func (r *Response) Body() []byte {
if r.FastResponse != nil { if r.FastResponse != nil {
return r.FastResponse.Body() return r.FastResponse.Body()
} else if r.StandardResponse != nil { } else if r.StandardResponse != nil {
if DefaultMaxBodySize == 0 { if DefaultMaxBodySize == -1 {
body, err := io.ReadAll(r.StandardResponse.Body) body, err := io.ReadAll(r.StandardResponse.Body)
if err != nil { if err != nil {
return nil return nil
@ -37,10 +37,10 @@ func (r *Response) Body() []byte {
return body return body
} else { } else {
var body []byte var body []byte
if r.StandardResponse.ContentLength > 0 && r.StandardResponse.ContentLength < int64(DefaultMaxBodySize) { if r.StandardResponse.ContentLength > 0 && CheckBodySize(r.StandardResponse.ContentLength) {
body = make([]byte, r.StandardResponse.ContentLength) body = make([]byte, r.StandardResponse.ContentLength)
} else { } else {
body = make([]byte, DefaultMaxBodySize) return nil
} }
n, err := io.ReadFull(r.StandardResponse.Body, body) n, err := io.ReadFull(r.StandardResponse.Body, body)
@ -62,11 +62,11 @@ func (r *Response) Body() []byte {
} }
} }
func (r *Response) ContentLength() int { func (r *Response) ContentLength() int64 {
if r.FastResponse != nil { if r.FastResponse != nil {
return r.FastResponse.Header.ContentLength() return int64(r.FastResponse.Header.ContentLength())
} else if r.StandardResponse != nil { } else if r.StandardResponse != nil {
return int(r.StandardResponse.ContentLength) return r.StandardResponse.ContentLength
} else { } else {
return 0 return 0
} }

View File

@ -1,10 +1,10 @@
package internal package internal
import ( import (
"bufio"
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"github.com/antonmedv/expr"
"github.com/chainreactors/files" "github.com/chainreactors/files"
"github.com/chainreactors/logs" "github.com/chainreactors/logs"
"github.com/chainreactors/parsers" "github.com/chainreactors/parsers"
@ -15,14 +15,18 @@ import (
"github.com/chainreactors/utils/iutils" "github.com/chainreactors/utils/iutils"
"github.com/chainreactors/words/mask" "github.com/chainreactors/words/mask"
"github.com/chainreactors/words/rule" "github.com/chainreactors/words/rule"
"github.com/gosuri/uiprogress" "github.com/expr-lang/expr"
"github.com/vbauerster/mpb/v8"
"io/ioutil" "io/ioutil"
"net/http"
"net/url" "net/url"
"os" "os"
"path/filepath"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time"
) )
var ( var (
@ -31,8 +35,8 @@ var (
) )
type Option struct { type Option struct {
InputOptions `group:"Input Options" config:"input" default:""` InputOptions `group:"Input Options" config:"input" `
FunctionOptions `group:"Function Options" config:"functions" default:""` FunctionOptions `group:"Function Options" config:"functions" `
OutputOptions `group:"Output Options" config:"output"` OutputOptions `group:"Output Options" config:"output"`
PluginOptions `group:"Plugin Options" config:"plugins"` PluginOptions `group:"Plugin Options" config:"plugins"`
RequestOptions `group:"Request Options" config:"request"` RequestOptions `group:"Request Options" config:"request"`
@ -47,15 +51,16 @@ type InputOptions struct {
URLFile string `short:"l" long:"list" description:"File, input filename"` URLFile string `short:"l" long:"list" description:"File, input filename"`
PortRange string `short:"p" long:"port" description:"String, input port range, e.g.: 80,8080-8090,db"` PortRange string `short:"p" long:"port" description:"String, input port range, e.g.: 80,8080-8090,db"`
CIDRs string `long:"cidr" description:"String, input cidr, e.g.: 1.1.1.1/24 "` CIDRs string `long:"cidr" description:"String, input cidr, e.g.: 1.1.1.1/24 "`
//Raw string `long:"raw" description:"File, input raw request filename"` RawFile string `long:"raw" description:"File, input raw request filename"`
Dictionaries []string `short:"d" long:"dict" description:"Files, Multi,dict files, e.g.: -d 1.txt -d 2.txt" config:"dictionaries"` Dictionaries []string `short:"d" long:"dict" description:"Files, Multi,dict files, e.g.: -d 1.txt -d 2.txt" config:"dictionaries"`
Offset int `long:"offset" description:"Int, wordlist offset"` NoDict bool `long:"no-dict" description:"Bool, no dictionary" config:"no-dict"`
Limit int `long:"limit" description:"Int, wordlist limit, start with offset. e.g.: --offset 1000 --limit 100"`
Word string `short:"w" long:"word" description:"String, word generate dsl, e.g.: -w test{?ld#4}" config:"word"` Word string `short:"w" long:"word" description:"String, word generate dsl, e.g.: -w test{?ld#4}" config:"word"`
Rules []string `short:"r" long:"rules" description:"Files, rule files, e.g.: -r rule1.txt -r rule2.txt" config:"rules"` Rules []string `short:"r" long:"rules" description:"Files, rule files, e.g.: -r rule1.txt -r rule2.txt" config:"rules"`
AppendRule []string `long:"append-rule" description:"Files, when found valid path , use append rule generator new word with current path" config:"append-rules"` AppendRule []string `long:"append-rule" description:"Files, when found valid path , use append rule generator new word with current path" config:"append-rules"`
FilterRule string `long:"filter-rule" description:"String, filter rule, e.g.: --rule-filter '>8 <4'" config:"filter-rule"` FilterRule string `long:"filter-rule" description:"String, filter rule, e.g.: --rule-filter '>8 <4'" config:"filter-rule"`
AppendFile []string `long:"append-file" description:"Files, when found valid path , use append file new word with current path" config:"append-files"` AppendFile []string `long:"append-file" description:"Files, when found valid path , use append file new word with current path" config:"append-files"`
Offset int `long:"offset" description:"Int, wordlist offset"`
Limit int `long:"limit" description:"Int, wordlist limit, start with offset. e.g.: --offset 1000 --limit 100"`
} }
type FunctionOptions struct { type FunctionOptions struct {
@ -83,15 +88,19 @@ type OutputOptions struct {
AutoFile bool `long:"auto-file" description:"Bool, auto generator output and fuzzy filename" config:"auto-file"` AutoFile bool `long:"auto-file" description:"Bool, auto generator output and fuzzy filename" config:"auto-file"`
Format string `short:"F" long:"format" description:"String, output format, e.g.: --format 1.json" config:"format"` Format string `short:"F" long:"format" description:"String, output format, e.g.: --format 1.json" config:"format"`
OutputProbe string `short:"o" long:"probe" description:"String, output format" config:"output_probe"` OutputProbe string `short:"o" long:"probe" description:"String, output format" config:"output_probe"`
Quiet bool `short:"q" long:"quiet" description:"Bool, Quiet" config:"quiet"`
NoColor bool `long:"no-color" description:"Bool, no color" config:"no-color"`
NoBar bool `long:"no-bar" description:"Bool, No progress bar" config:"no-bar"`
} }
type RequestOptions struct { type RequestOptions struct {
Method string `short:"x" long:"method" default:"GET" description:"String, request method, e.g.: --method POST" config:"method"`
Headers []string `long:"header" description:"Strings, custom headers, e.g.: --headers 'Auth: example_auth'" config:"headers"` Headers []string `long:"header" description:"Strings, custom headers, e.g.: --headers 'Auth: example_auth'" config:"headers"`
UserAgent string `long:"user-agent" description:"String, custom user-agent, e.g.: --user-agent Custom" config:"useragent"` UserAgent string `long:"user-agent" description:"String, custom user-agent, e.g.: --user-agent Custom" config:"useragent"`
RandomUserAgent bool `long:"random-agent" description:"Bool, use random with default user-agent" config:"random-useragent"` RandomUserAgent bool `long:"random-agent" description:"Bool, use random with default user-agent" config:"random-useragent"`
Cookie []string `long:"cookie" description:"Strings, custom cookie" config:"cookies"` Cookie []string `long:"cookie" description:"Strings, custom cookie" config:"cookies"`
ReadAll bool `long:"read-all" description:"Bool, read all response body" config:"read-all"` ReadAll bool `long:"read-all" description:"Bool, read all response body" config:"read-all"`
MaxBodyLength int `long:"max-length" default:"100" description:"Int, max response body length (kb), default 100k, e.g. -max-length 1000" config:"max-body-length"` MaxBodyLength int64 `long:"max-length" default:"100" description:"Int, max response body length (kb), -1 read-all, 0 not read body, default 100k, e.g. --max-length 1000" config:"max-length"`
} }
type PluginOptions struct { type PluginOptions struct {
@ -133,16 +142,14 @@ type MiscOptions struct {
Mod string `short:"m" long:"mod" default:"path" choice:"path" choice:"host" description:"String, path/host spray" config:"mod"` Mod string `short:"m" long:"mod" default:"path" choice:"path" choice:"host" description:"String, path/host spray" config:"mod"`
Client string `short:"C" long:"client" default:"auto" choice:"fast" choice:"standard" choice:"auto" description:"String, Client type" config:"client"` Client string `short:"C" long:"client" default:"auto" choice:"fast" choice:"standard" choice:"auto" description:"String, Client type" config:"client"`
Deadline int `long:"deadline" default:"999999" description:"Int, deadline (seconds)" config:"deadline"` // todo 总的超时时间,适配云函数的deadline Deadline int `long:"deadline" default:"999999" description:"Int, deadline (seconds)" config:"deadline"` // todo 总的超时时间,适配云函数的deadline
Timeout int `long:"timeout" default:"5" description:"Int, timeout with request (seconds)" config:"timeout"` Timeout int `short:"T" long:"timeout" default:"5" description:"Int, timeout with request (seconds)" config:"timeout"`
PoolSize int `short:"P" long:"pool" default:"5" description:"Int, Pool size" config:"pool"` PoolSize int `short:"P" long:"pool" default:"5" description:"Int, Pool size" config:"pool"`
Threads int `short:"t" long:"thread" default:"20" description:"Int, number of threads per pool" config:"thread"` Threads int `short:"t" long:"thread" default:"20" description:"Int, number of threads per pool" config:"thread"`
Debug bool `long:"debug" description:"Bool, output debug info" config:"debug"` Debug bool `long:"debug" description:"Bool, output debug info" config:"debug"`
Version bool `long:"version" description:"Bool, show version"` Version bool `long:"version" description:"Bool, show version"`
Verbose []bool `short:"v" description:"Bool, log verbose level ,default 0, level1: -v level2 -vv " config:"verbose"` Verbose []bool `short:"v" description:"Bool, log verbose level ,default 0, level1: -v level2 -vv " config:"verbose"`
Quiet bool `short:"q" long:"quiet" description:"Bool, Quiet" config:"quiet"` Proxy string `long:"proxy" description:"String, proxy address, e.g.: --proxy socks5://127.0.0.1:1080" config:"proxy"`
NoColor bool `long:"no-color" description:"Bool, no color" config:"no-color"` InitConfig bool `long:"init" description:"Bool, init config file"`
NoBar bool `long:"no-bar" description:"Bool, No progress bar" config:"no-bar"`
Proxy string `long:"proxy" default:"" description:"String, proxy address, e.g.: --proxy socks5://127.0.0.1:1080" config:"proxy"`
} }
func (opt *Option) PrepareRunner() (*Runner, error) { func (opt *Option) PrepareRunner() (*Runner, error) {
@ -151,7 +158,6 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
return nil, err return nil, err
} }
r := &Runner{ r := &Runner{
Progress: uiprogress.New(),
Threads: opt.Threads, Threads: opt.Threads,
PoolSize: opt.PoolSize, PoolSize: opt.PoolSize,
Mod: opt.Mod, Mod: opt.Mod,
@ -159,12 +165,13 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
RateLimit: opt.RateLimit, RateLimit: opt.RateLimit,
Deadline: opt.Deadline, Deadline: opt.Deadline,
Headers: make(map[string]string), Headers: make(map[string]string),
Method: opt.Method,
Offset: opt.Offset, Offset: opt.Offset,
Total: opt.Limit, Total: opt.Limit,
taskCh: make(chan *Task), taskCh: make(chan *Task),
outputCh: make(chan *pkg.Baseline, 100), outputCh: make(chan *pkg.Baseline, 256),
outwg: &sync.WaitGroup{}, outwg: &sync.WaitGroup{},
fuzzyCh: make(chan *pkg.Baseline, 100), fuzzyCh: make(chan *pkg.Baseline, 256),
Fuzzy: opt.Fuzzy, Fuzzy: opt.Fuzzy,
Force: opt.Force, Force: opt.Force,
CheckOnly: opt.CheckOnly, CheckOnly: opt.CheckOnly,
@ -194,8 +201,8 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
r.Color = false r.Color = false
} }
if !(opt.Quiet || opt.NoBar) { if !(opt.Quiet || opt.NoBar) {
r.Progress.Start() r.Progress = mpb.New(mpb.WithRefreshRate(100 * time.Millisecond))
logs.Log.SetOutput(r.Progress.Bypass()) logs.Log.SetOutput(r.Progress)
} }
// configuration // configuration
@ -307,7 +314,7 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
// prepare word // prepare word
dicts := make([][]string, len(opt.Dictionaries)) dicts := make([][]string, len(opt.Dictionaries))
if len(opt.Dictionaries) == 0 { if len(opt.Dictionaries) == 0 && opt.Word == "" && !opt.NoDict {
dicts = append(dicts, pkg.LoadDefaultDict()) dicts = append(dicts, pkg.LoadDefaultDict())
logs.Log.Warn("not set any dictionary, use default dictionary: https://github.com/maurosoria/dirsearch/blob/master/db/dicc.txt") logs.Log.Warn("not set any dictionary, use default dictionary: https://github.com/maurosoria/dirsearch/blob/master/db/dicc.txt")
} else { } else {
@ -332,13 +339,13 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
opt.Word += "}" opt.Word += "}"
} }
if opt.Suffixes != nil { if len(opt.Suffixes) != 0 {
mask.SpecialWords["suffix"] = opt.Suffixes mask.SpecialWords["suffix"] = opt.Suffixes
opt.Word += "{@suffix}" opt.Word += "{?@suffix}"
} }
if opt.Prefixes != nil { if len(opt.Prefixes) != 0 {
mask.SpecialWords["prefix"] = opt.Prefixes mask.SpecialWords["prefix"] = opt.Prefixes
opt.Word = "{@prefix}" + opt.Word opt.Word = "{?@prefix}" + opt.Word
} }
if opt.ForceExtension && opt.Extensions != "" { if opt.ForceExtension && opt.Extensions != "" {
@ -349,7 +356,7 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
} }
} }
mask.SpecialWords["ext"] = exts mask.SpecialWords["ext"] = exts
opt.Word += "{@ext}" opt.Word += "{?@ext}"
} }
r.Wordlist, err = mask.Run(opt.Word, dicts, nil) r.Wordlist, err = mask.Run(opt.Word, dicts, nil)
@ -360,7 +367,7 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
logs.Log.Logf(pkg.LogVerbose, "Parsed %d words by %s", len(r.Wordlist), opt.Word) logs.Log.Logf(pkg.LogVerbose, "Parsed %d words by %s", len(r.Wordlist), opt.Word)
} }
if opt.Rules != nil { if len(opt.Rules) != 0 {
rules, err := loadRuleAndCombine(opt.Rules) rules, err := loadRuleAndCombine(opt.Rules)
if err != nil { if err != nil {
return nil, err return nil, err
@ -389,7 +396,7 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
Total: r.Total, Total: r.Total,
} }
if opt.AppendRule != nil { if len(opt.AppendRule) != 0 {
content, err := loadRuleAndCombine(opt.AppendRule) content, err := loadRuleAndCombine(opt.AppendRule)
if err != nil { if err != nil {
return nil, err return nil, err
@ -397,7 +404,7 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
r.AppendRules = rule.Compile(string(content), "") r.AppendRules = rule.Compile(string(content), "")
} }
if opt.AppendFile != nil { if len(opt.AppendFile) != 0 {
var bs bytes.Buffer var bs bytes.Buffer
for _, f := range opt.AppendFile { for _, f := range opt.AppendFile {
content, err := ioutil.ReadFile(f) content, err := ioutil.ReadFile(f)
@ -415,7 +422,6 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
} }
ports := utils.ParsePort(opt.PortRange) ports := utils.ParsePort(opt.PortRange)
// prepare task // prepare task
tasks := make(chan *Task, opt.PoolSize) tasks := make(chan *Task, opt.PoolSize)
var taskfrom string var taskfrom string
@ -437,15 +443,12 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
// 根据不同的输入类型生成任务 // 根据不同的输入类型生成任务
if len(opt.URL) == 1 { if len(opt.URL) == 1 {
u, err := url.Parse(opt.URL[0])
if err != nil {
u, _ = url.Parse("http://" + opt.URL[0])
}
go func() { go func() {
opt.GenerateTasks(tasks, u.String(), ports) opt.GenerateTasks(tasks, opt.URL[0], ports)
close(tasks) close(tasks)
}() }()
taskfrom = u.Host parsed, _ := url.Parse(opt.URL[0])
taskfrom = parsed.Host
r.Count = 1 r.Count = 1
} else if len(opt.URL) > 1 { } else if len(opt.URL) > 1 {
go func() { go func() {
@ -457,6 +460,24 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
taskfrom = "cmd" taskfrom = "cmd"
r.Count = len(opt.URL) r.Count = len(opt.URL)
} else if opt.RawFile != "" {
raw, err := os.Open(opt.RawFile)
if err != nil {
return nil, err
}
req, err := http.ReadRequest(bufio.NewReader(raw))
if err != nil {
return nil, err
}
go func() {
opt.GenerateTasks(tasks, fmt.Sprintf("http://%s%s", req.Host, req.URL.String()), ports)
close(tasks)
}()
r.Method = req.Method
for k, _ := range req.Header {
r.Headers[k] = req.Header.Get(k)
}
} else if opt.CIDRs != "" { } else if opt.CIDRs != "" {
if len(ports) == 0 { if len(ports) == 0 {
ports = []string{"80", "443"} ports = []string{"80", "443"}
@ -484,14 +505,13 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
} else if opt.URLFile != "" { } else if opt.URLFile != "" {
file, err = os.Open(opt.URLFile) file, err = os.Open(opt.URLFile)
if err != nil { if err != nil {
logs.Log.Error(err.Error()) return nil, err
} }
taskfrom = opt.URLFile taskfrom = filepath.Base(opt.URLFile)
} else if files.HasStdin() { } else if files.HasStdin() {
file = os.Stdin file = os.Stdin
taskfrom = "stdin" taskfrom = "stdin"
} }
if file != nil { if file != nil {
content, err := ioutil.ReadAll(file) content, err := ioutil.ReadAll(file)
if err != nil { if err != nil {
@ -612,7 +632,7 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
logs.Log.Logf(pkg.LogVerbose, "Loaded %d dictionaries and %d decorators", len(opt.Dictionaries), len(r.Fns)) logs.Log.Logf(pkg.LogVerbose, "Loaded %d dictionaries and %d decorators", len(opt.Dictionaries), len(r.Fns))
if opt.Match != "" { if opt.Match != "" {
exp, err := expr.Compile(opt.Match, expr.Patch(&bytesPatcher{})) exp, err := expr.Compile(opt.Match)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -620,7 +640,7 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
} }
if opt.Filter != "" { if opt.Filter != "" {
exp, err := expr.Compile(opt.Filter, expr.Patch(&bytesPatcher{})) exp, err := expr.Compile(opt.Filter)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -642,7 +662,7 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
} }
if express != "" { if express != "" {
exp, err := expr.Compile(express, expr.Patch(&bytesPatcher{})) exp, err := expr.Compile(express)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -738,7 +758,7 @@ func (opt *Option) Validate() error {
return errors.New("--resume and --depth cannot be used at the same time") return errors.New("--resume and --depth cannot be used at the same time")
} }
if opt.ResumeFrom == "" && opt.URL == nil && opt.URLFile == "" && opt.CIDRs == "" { if opt.ResumeFrom == "" && opt.URL == nil && opt.URLFile == "" && opt.CIDRs == "" && opt.RawFile == "" {
return fmt.Errorf("without any target, please use -u/-l/-c/--resume to set targets") return fmt.Errorf("without any target, please use -u/-l/-c/--resume to set targets")
} }
return nil return nil
@ -748,7 +768,7 @@ func (opt *Option) Validate() error {
func (opt *Option) GenerateTasks(ch chan *Task, u string, ports []string) { func (opt *Option) GenerateTasks(ch chan *Task, u string, ports []string) {
parsed, err := url.Parse(u) parsed, err := url.Parse(u)
if err != nil { if err != nil {
logs.Log.Warn(err.Error()) logs.Log.Warnf("parse %s, %s ", u, err.Error())
return return
} }
@ -761,7 +781,7 @@ func (opt *Option) GenerateTasks(ch chan *Task, u string, ports []string) {
} }
if len(ports) == 0 { if len(ports) == 0 {
ch <- &Task{baseUrl: u} ch <- &Task{baseUrl: parsed.String()}
return return
} }

View File

@ -2,6 +2,7 @@ package pool
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/chainreactors/logs" "github.com/chainreactors/logs"
"github.com/chainreactors/parsers" "github.com/chainreactors/parsers"
@ -37,7 +38,7 @@ func NewBrutePool(ctx context.Context, config *Config) (*BrutePool, error) {
pctx, cancel := context.WithCancel(ctx) pctx, cancel := context.WithCancel(ctx)
pool := &BrutePool{ pool := &BrutePool{
Baselines: NewBaselines(), Baselines: NewBaselines(),
This: &This{ BasePool: &BasePool{
Config: config, Config: config,
ctx: pctx, ctx: pctx,
Cancel: cancel, Cancel: cancel,
@ -49,6 +50,7 @@ func NewBrutePool(ctx context.Context, config *Config) (*BrutePool, error) {
}), }),
additionCh: make(chan *Unit, config.Thread), additionCh: make(chan *Unit, config.Thread),
closeCh: make(chan struct{}), closeCh: make(chan struct{}),
processCh: make(chan *pkg.Baseline, config.Thread),
wg: sync.WaitGroup{}, wg: sync.WaitGroup{},
}, },
base: u.Scheme + "://" + u.Host, base: u.Scheme + "://" + u.Host,
@ -57,7 +59,6 @@ func NewBrutePool(ctx context.Context, config *Config) (*BrutePool, error) {
scopeurls: make(map[string]struct{}), scopeurls: make(map[string]struct{}),
uniques: make(map[uint16]struct{}), uniques: make(map[uint16]struct{}),
handlerCh: make(chan *pkg.Baseline, config.Thread),
checkCh: make(chan struct{}, config.Thread), checkCh: make(chan struct{}, config.Thread),
initwg: sync.WaitGroup{}, initwg: sync.WaitGroup{},
limiter: rate.NewLimiter(rate.Limit(config.RateLimit), 1), limiter: rate.NewLimiter(rate.Limit(config.RateLimit), 1),
@ -83,14 +84,13 @@ func NewBrutePool(ctx context.Context, config *Config) (*BrutePool, error) {
type BrutePool struct { type BrutePool struct {
*Baselines *Baselines
*This *BasePool
base string // url的根目录, 在爬虫或者redirect时, 会需要用到根目录进行拼接 base string // url的根目录, 在爬虫或者redirect时, 会需要用到根目录进行拼接
isDir bool isDir bool
url *url.URL url *url.URL
reqPool *ants.PoolWithFunc reqPool *ants.PoolWithFunc
scopePool *ants.PoolWithFunc scopePool *ants.PoolWithFunc
handlerCh chan *pkg.Baseline // 待处理的baseline
checkCh chan struct{} // 独立的check管道 防止与redirect/crawl冲突 checkCh chan struct{} // 独立的check管道 防止与redirect/crawl冲突
closed bool closed bool
wordOffset int wordOffset int
@ -125,7 +125,7 @@ func (pool *BrutePool) genReq(mod SprayMod, s string) (*ihttp.Request, error) {
if mod == HostSpray { if mod == HostSpray {
return ihttp.BuildHostRequest(pool.ClientType, pool.BaseURL, s) return ihttp.BuildHostRequest(pool.ClientType, pool.BaseURL, s)
} else if mod == PathSpray { } else if mod == PathSpray {
return ihttp.BuildPathRequest(pool.ClientType, pool.base, s) return ihttp.BuildPathRequest(pool.ClientType, pool.base, s, pool.Method)
} }
return nil, fmt.Errorf("unknown mod") return nil, fmt.Errorf("unknown mod")
} }
@ -237,7 +237,7 @@ Loop:
pool.Statistor.End++ pool.Statistor.End++
if w == "" { if w == "" {
pool.Statistor.Skipped++ pool.Statistor.Skipped++
continue pool.Bar.Done()
} }
pool.wordOffset++ pool.wordOffset++
@ -311,7 +311,9 @@ func (pool *BrutePool) Invoke(v interface{}) {
} }
req.SetHeaders(pool.Headers) req.SetHeaders(pool.Headers)
if pool.RandomUserAgent {
req.SetHeader("User-Agent", pkg.RandomUA()) req.SetHeader("User-Agent", pkg.RandomUA())
}
start := time.Now() start := time.Now()
resp, reqerr := pool.client.Do(pool.ctx, req) resp, reqerr := pool.client.Do(pool.ctx, req)
@ -322,7 +324,7 @@ func (pool *BrutePool) Invoke(v interface{}) {
// compare与各种错误处理 // compare与各种错误处理
var bl *pkg.Baseline var bl *pkg.Baseline
if reqerr != nil && reqerr != fasthttp.ErrBodyTooLarge { if reqerr != nil && !errors.Is(reqerr, fasthttp.ErrBodyTooLarge) {
atomic.AddInt32(&pool.failedCount, 1) atomic.AddInt32(&pool.failedCount, 1)
atomic.AddInt32(&pool.Statistor.FailedNumber, 1) atomic.AddInt32(&pool.Statistor.FailedNumber, 1)
bl = &pkg.Baseline{ bl = &pkg.Baseline{
@ -356,7 +358,7 @@ func (pool *BrutePool) Invoke(v interface{}) {
pool.doRedirect(bl, unit.depth) pool.doRedirect(bl, unit.depth)
} }
if ihttp.DefaultMaxBodySize != 0 && bl.BodyLength > ihttp.DefaultMaxBodySize { if !ihttp.CheckBodySize(int64(bl.BodyLength)) {
bl.ExceedLength = true bl.ExceedLength = true
} }
bl.Source = unit.source bl.Source = unit.source
@ -403,7 +405,7 @@ func (pool *BrutePool) Invoke(v interface{}) {
case parsers.WordSource: case parsers.WordSource:
// 异步进行性能消耗较大的深度对比 // 异步进行性能消耗较大的深度对比
pool.handlerCh <- bl pool.processCh <- bl
if int(pool.Statistor.ReqTotal)%pool.CheckPeriod == 0 { if int(pool.Statistor.ReqTotal)%pool.CheckPeriod == 0 {
pool.doCheck() pool.doCheck()
} else if pool.failedCount%pool.ErrPeriod == 0 { } else if pool.failedCount%pool.ErrPeriod == 0 {
@ -413,16 +415,16 @@ func (pool *BrutePool) Invoke(v interface{}) {
pool.Bar.Done() pool.Bar.Done()
case parsers.RedirectSource: case parsers.RedirectSource:
bl.FrontURL = unit.frontUrl bl.FrontURL = unit.frontUrl
pool.handlerCh <- bl pool.processCh <- bl
default: default:
pool.handlerCh <- bl pool.processCh <- bl
} }
} }
func (pool *BrutePool) NoScopeInvoke(v interface{}) { func (pool *BrutePool) NoScopeInvoke(v interface{}) {
defer pool.wg.Done() defer pool.wg.Done()
unit := v.(*Unit) unit := v.(*Unit)
req, err := ihttp.BuildPathRequest(pool.ClientType, unit.path, "") req, err := ihttp.BuildPathRequest(pool.ClientType, unit.path, "", pool.Method)
if err != nil { if err != nil {
logs.Log.Error(err.Error()) logs.Log.Error(err.Error())
return return
@ -451,7 +453,7 @@ func (pool *BrutePool) NoScopeInvoke(v interface{}) {
} }
func (pool *BrutePool) Handler() { func (pool *BrutePool) Handler() {
for bl := range pool.handlerCh { for bl := range pool.processCh {
if bl.IsValid { if bl.IsValid {
pool.addFuzzyBaseline(bl) pool.addFuzzyBaseline(bl)
} }
@ -708,7 +710,7 @@ func (pool *BrutePool) addFuzzyBaseline(bl *pkg.Baseline) {
func (pool *BrutePool) doBak() { func (pool *BrutePool) doBak() {
defer pool.wg.Done() defer pool.wg.Done()
worder, err := words.NewWorderWithDsl("{?0}.{@bak_ext}", [][]string{pkg.BakGenerator(pool.url.Host)}, nil) worder, err := words.NewWorderWithDsl("{?0}.{?@bak_ext}", [][]string{pkg.BakGenerator(pool.url.Host)}, nil)
if err != nil { if err != nil {
return return
} }
@ -720,7 +722,7 @@ func (pool *BrutePool) doBak() {
}) })
} }
worder, err = words.NewWorderWithDsl("{@bak_name}.{@bak_ext}", nil, nil) worder, err = words.NewWorderWithDsl("{?@bak_name}.{?@bak_ext}", nil, nil)
if err != nil { if err != nil {
return return
} }
@ -764,16 +766,3 @@ func (pool *BrutePool) resetFailed() {
pool.failedCount = 1 pool.failedCount = 1
pool.FailedBaselines = nil pool.FailedBaselines = nil
} }
func NewBaselines() *Baselines {
return &Baselines{
baselines: map[int]*pkg.Baseline{},
}
}
type Baselines struct {
FailedBaselines []*pkg.Baseline
random *pkg.Baseline
index *pkg.Baseline
baselines map[int]*pkg.Baseline
}

View File

@ -7,7 +7,6 @@ import (
"github.com/chainreactors/spray/internal/ihttp" "github.com/chainreactors/spray/internal/ihttp"
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/pkg"
"github.com/panjf2000/ants/v2" "github.com/panjf2000/ants/v2"
"github.com/valyala/fasthttp"
"net/url" "net/url"
"strings" "strings"
"sync" "sync"
@ -17,9 +16,11 @@ import (
// 类似httpx的无状态, 无scope, 无并发池的检测模式 // 类似httpx的无状态, 无scope, 无并发池的检测模式
func NewCheckPool(ctx context.Context, config *Config) (*CheckPool, error) { func NewCheckPool(ctx context.Context, config *Config) (*CheckPool, error) {
pctx, cancel := context.WithCancel(ctx) pctx, cancel := context.WithCancel(ctx)
config.ClientType = ihttp.STANDARD
pool := &CheckPool{ pool := &CheckPool{
&This{ &BasePool{
Config: config, Config: config,
Statistor: pkg.NewStatistor(""),
ctx: pctx, ctx: pctx,
Cancel: cancel, Cancel: cancel,
client: ihttp.NewClient(&ihttp.ClientConfig{ client: ihttp.NewClient(&ihttp.ClientConfig{
@ -29,19 +30,21 @@ func NewCheckPool(ctx context.Context, config *Config) (*CheckPool, error) {
ProxyAddr: config.ProxyAddr, ProxyAddr: config.ProxyAddr,
}), }),
wg: sync.WaitGroup{}, wg: sync.WaitGroup{},
additionCh: make(chan *Unit, 100), additionCh: make(chan *Unit, 1024),
closeCh: make(chan struct{}), closeCh: make(chan struct{}),
processCh: make(chan *pkg.Baseline, config.Thread),
}, },
} }
pool.Headers = map[string]string{"Connection": "close"} pool.Headers = map[string]string{"Connection": "close"}
p, _ := ants.NewPoolWithFunc(config.Thread, pool.Invoke) p, _ := ants.NewPoolWithFunc(config.Thread, pool.Invoke)
pool.This.Pool = p pool.BasePool.Pool = p
go pool.Handler()
return pool, nil return pool, nil
} }
type CheckPool struct { type CheckPool struct {
*This *BasePool
} }
func (pool *CheckPool) Run(ctx context.Context, offset, limit int) { func (pool *CheckPool) Run(ctx context.Context, offset, limit int) {
@ -79,12 +82,12 @@ Loop:
} }
pool.wg.Add(1) pool.wg.Add(1)
_ = pool.This.Pool.Invoke(newUnit(u, parsers.CheckSource)) _ = pool.BasePool.Pool.Invoke(newUnit(u, parsers.CheckSource))
case u, ok := <-pool.additionCh: case u, ok := <-pool.additionCh:
if !ok { if !ok {
continue continue
} }
_ = pool.This.Pool.Invoke(u) _ = pool.BasePool.Pool.Invoke(u)
case <-pool.closeCh: case <-pool.closeCh:
break Loop break Loop
case <-ctx.Done(): case <-ctx.Done():
@ -98,21 +101,32 @@ Loop:
} }
func (pool *CheckPool) Invoke(v interface{}) { func (pool *CheckPool) Invoke(v interface{}) {
defer func() {
pool.reqCount++
pool.wg.Done()
}()
unit := v.(*Unit) unit := v.(*Unit)
req, err := pool.genReq(unit.path) req, err := pool.genReq(unit.path)
if err != nil { if err != nil {
logs.Log.Error(err.Error()) logs.Log.Debug(err.Error())
bl := &pkg.Baseline{
SprayResult: &parsers.SprayResult{
UrlString: unit.path,
IsValid: false,
ErrString: err.Error(),
Reason: pkg.ErrUrlError.Error(),
ReqDepth: unit.depth,
},
}
pool.processCh <- bl
return
} }
req.SetHeaders(pool.Headers) req.SetHeaders(pool.Headers)
start := time.Now() start := time.Now()
var bl *pkg.Baseline var bl *pkg.Baseline
resp, reqerr := pool.client.Do(pool.ctx, req) resp, reqerr := pool.client.Do(pool.ctx, req)
if pool.ClientType == ihttp.FAST { if reqerr != nil {
defer fasthttp.ReleaseResponse(resp.FastResponse)
defer fasthttp.ReleaseRequest(req.FastRequest)
}
if reqerr != nil && reqerr != fasthttp.ErrBodyTooLarge {
pool.failedCount++ pool.failedCount++
bl = &pkg.Baseline{ bl = &pkg.Baseline{
SprayResult: &parsers.SprayResult{ SprayResult: &parsers.SprayResult{
@ -123,13 +137,8 @@ func (pool *CheckPool) Invoke(v interface{}) {
ReqDepth: unit.depth, ReqDepth: unit.depth,
}, },
} }
logs.Log.Debugf("%s, %s", unit.path, reqerr.Error())
if strings.Contains(reqerr.Error(), "timed out") || strings.Contains(reqerr.Error(), "actively refused") {
} else {
pool.doUpgrade(bl) pool.doUpgrade(bl)
}
} else { } else {
bl = pkg.NewBaseline(req.URI(), req.Host(), resp) bl = pkg.NewBaseline(req.URI(), req.Host(), resp)
bl.Collect() bl.Collect()
@ -137,11 +146,14 @@ func (pool *CheckPool) Invoke(v interface{}) {
bl.ReqDepth = unit.depth bl.ReqDepth = unit.depth
bl.Source = unit.source bl.Source = unit.source
bl.Spended = time.Since(start).Milliseconds() bl.Spended = time.Since(start).Milliseconds()
pool.processCh <- bl
}
// 手动处理重定向 func (pool *CheckPool) Handler() {
for bl := range pool.processCh {
if bl.IsValid { if bl.IsValid {
if bl.RedirectURL != "" { if bl.RedirectURL != "" {
pool.doRedirect(bl, unit.depth) pool.doRedirect(bl, bl.ReqDepth)
pool.putToFuzzy(bl) pool.putToFuzzy(bl)
} else if bl.Status == 400 { } else if bl.Status == 400 {
pool.doUpgrade(bl) pool.doUpgrade(bl)
@ -150,16 +162,17 @@ func (pool *CheckPool) Invoke(v interface{}) {
params := map[string]interface{}{ params := map[string]interface{}{
"current": bl, "current": bl,
} }
if pool.MatchExpr == nil || pkg.CompareWithExpr(pool.MatchExpr, params) { if pool.MatchExpr != nil && pkg.CompareWithExpr(pool.MatchExpr, params) {
bl.IsValid = true
}
}
}
if bl.Source == parsers.CheckSource {
pool.Bar.Done()
}
pool.putToOutput(bl) pool.putToOutput(bl)
} }
} }
}
pool.reqCount++
pool.wg.Done()
pool.Bar.Done()
}
func (pool *CheckPool) doRedirect(bl *pkg.Baseline, depth int) { func (pool *CheckPool) doRedirect(bl *pkg.Baseline, depth int) {
if depth >= MaxRedirect { if depth >= MaxRedirect {

View File

@ -1,9 +1,9 @@
package pool package pool
import ( import (
"github.com/antonmedv/expr/vm"
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/pkg"
"github.com/chainreactors/words/rule" "github.com/chainreactors/words/rule"
"github.com/expr-lang/expr/vm"
"sync" "sync"
) )
@ -27,6 +27,7 @@ type Config struct {
Thread int Thread int
Wordlist []string Wordlist []string
Timeout int Timeout int
ProcessCh chan *pkg.Baseline
OutputCh chan *pkg.Baseline OutputCh chan *pkg.Baseline
FuzzyCh chan *pkg.Baseline FuzzyCh chan *pkg.Baseline
OutLocker *sync.WaitGroup OutLocker *sync.WaitGroup

View File

@ -14,15 +14,16 @@ import (
"sync" "sync"
) )
type This struct { type BasePool struct {
*Config *Config
Statistor *pkg.Statistor Statistor *pkg.Statistor
Pool *ants.PoolWithFunc Pool *ants.PoolWithFunc
Bar *pkg.Bar Bar *pkg.Bar
Worder *words.Worder Worder *words.Worder
Cancel context.CancelFunc
client *ihttp.Client client *ihttp.Client
ctx context.Context ctx context.Context
Cancel context.CancelFunc processCh chan *pkg.Baseline // 待处理的baseline
dir string dir string
reqCount int reqCount int
failedCount int failedCount int
@ -31,7 +32,7 @@ type This struct {
wg sync.WaitGroup wg sync.WaitGroup
} }
func (pool *This) doRedirect(bl *pkg.Baseline, depth int) { func (pool *BasePool) doRedirect(bl *pkg.Baseline, depth int) {
if depth >= MaxRedirect { if depth >= MaxRedirect {
return return
} }
@ -48,7 +49,7 @@ func (pool *This) doRedirect(bl *pkg.Baseline, depth int) {
}() }()
} }
func (pool *This) doRule(bl *pkg.Baseline) { func (pool *BasePool) doRule(bl *pkg.Baseline) {
if pool.AppendRule == nil { if pool.AppendRule == nil {
pool.wg.Done() pool.wg.Done()
return return
@ -69,7 +70,7 @@ func (pool *This) doRule(bl *pkg.Baseline) {
}() }()
} }
func (pool *This) doAppendWords(bl *pkg.Baseline) { func (pool *BasePool) doAppendWords(bl *pkg.Baseline) {
if pool.AppendWords == nil { if pool.AppendWords == nil {
pool.wg.Done() pool.wg.Done()
return return
@ -90,7 +91,7 @@ func (pool *This) doAppendWords(bl *pkg.Baseline) {
}() }()
} }
func (pool *This) doRetry(bl *pkg.Baseline) { func (pool *BasePool) doRetry(bl *pkg.Baseline) {
if bl.Retry >= pool.Retry { if bl.Retry >= pool.Retry {
return return
} }
@ -105,7 +106,7 @@ func (pool *This) doRetry(bl *pkg.Baseline) {
}() }()
} }
func (pool *This) doActive() { func (pool *BasePool) doActive() {
defer pool.wg.Done() defer pool.wg.Done()
for _, u := range pkg.ActivePath { for _, u := range pkg.ActivePath {
pool.addAddition(&Unit{ pool.addAddition(&Unit{
@ -115,7 +116,7 @@ func (pool *This) doActive() {
} }
} }
func (pool *This) doCommonFile() { func (pool *BasePool) doCommonFile() {
defer pool.wg.Done() defer pool.wg.Done()
for _, u := range mask.SpecialWords["common_file"] { for _, u := range mask.SpecialWords["common_file"] {
pool.addAddition(&Unit{ pool.addAddition(&Unit{
@ -125,7 +126,7 @@ func (pool *This) doCommonFile() {
} }
} }
func (pool *This) addAddition(u *Unit) { func (pool *BasePool) addAddition(u *Unit) {
// 强行屏蔽报错, 防止goroutine泄露 // 强行屏蔽报错, 防止goroutine泄露
pool.wg.Add(1) pool.wg.Add(1)
defer func() { defer func() {
@ -135,25 +136,25 @@ func (pool *This) addAddition(u *Unit) {
pool.additionCh <- u pool.additionCh <- u
} }
func (pool *This) Close() { func (pool *BasePool) Close() {
pool.Bar.Close() pool.Bar.Close()
} }
func (pool *This) genReq(s string) (*ihttp.Request, error) { func (pool *BasePool) genReq(s string) (*ihttp.Request, error) {
if pool.Mod == HostSpray { if pool.Mod == HostSpray {
return ihttp.BuildHostRequest(pool.ClientType, pool.BaseURL, s) return ihttp.BuildHostRequest(pool.ClientType, pool.BaseURL, s)
} else if pool.Mod == PathSpray { } else if pool.Mod == PathSpray {
return ihttp.BuildPathRequest(pool.ClientType, pool.BaseURL, s) return ihttp.BuildPathRequest(pool.ClientType, pool.BaseURL, s, pool.Method)
} }
return nil, fmt.Errorf("unknown mod") return nil, fmt.Errorf("unknown mod")
} }
func (pool *This) putToOutput(bl *pkg.Baseline) { func (pool *BasePool) putToOutput(bl *pkg.Baseline) {
pool.OutLocker.Add(1) pool.OutLocker.Add(1)
pool.OutputCh <- bl pool.OutputCh <- bl
} }
func (pool *This) putToFuzzy(bl *pkg.Baseline) { func (pool *BasePool) putToFuzzy(bl *pkg.Baseline) {
pool.OutLocker.Add(1) pool.OutLocker.Add(1)
bl.IsFuzzy = true bl.IsFuzzy = true
pool.FuzzyCh <- bl pool.FuzzyCh <- bl

View File

@ -1,6 +1,9 @@
package pool package pool
import "github.com/chainreactors/parsers" import (
"github.com/chainreactors/parsers"
"github.com/chainreactors/spray/pkg"
)
func newUnit(path string, source parsers.SpraySource) *Unit { func newUnit(path string, source parsers.SpraySource) *Unit {
return &Unit{path: path, source: source} return &Unit{path: path, source: source}
@ -18,3 +21,16 @@ type Unit struct {
frontUrl string frontUrl string
depth int // redirect depth depth int // redirect depth
} }
func NewBaselines() *Baselines {
return &Baselines{
baselines: map[int]*pkg.Baseline{},
}
}
type Baselines struct {
FailedBaselines []*pkg.Baseline
random *pkg.Baseline
index *pkg.Baseline
baselines map[int]*pkg.Baseline
}

View File

@ -2,8 +2,6 @@ package internal
import ( import (
"context" "context"
"fmt"
"github.com/antonmedv/expr/vm"
"github.com/chainreactors/files" "github.com/chainreactors/files"
"github.com/chainreactors/logs" "github.com/chainreactors/logs"
"github.com/chainreactors/spray/internal/ihttp" "github.com/chainreactors/spray/internal/ihttp"
@ -11,8 +9,10 @@ import (
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/pkg"
"github.com/chainreactors/words" "github.com/chainreactors/words"
"github.com/chainreactors/words/rule" "github.com/chainreactors/words/rule"
"github.com/gosuri/uiprogress" "github.com/expr-lang/expr/vm"
"github.com/panjf2000/ants/v2" "github.com/panjf2000/ants/v2"
"github.com/vbauerster/mpb/v8"
"github.com/vbauerster/mpb/v8/decor"
"sync" "sync"
"time" "time"
) )
@ -33,7 +33,7 @@ type Runner struct {
outwg *sync.WaitGroup outwg *sync.WaitGroup
outputCh chan *pkg.Baseline outputCh chan *pkg.Baseline
fuzzyCh chan *pkg.Baseline fuzzyCh chan *pkg.Baseline
bar *uiprogress.Bar bar *mpb.Bar
finished int finished int
Tasks chan *Task Tasks chan *Task
@ -43,6 +43,7 @@ type Runner struct {
AppendRules *rule.Program AppendRules *rule.Program
AppendWords []string AppendWords []string
Headers map[string]string Headers map[string]string
Method string
Fns []func(string) []string Fns []func(string) []string
FilterExpr *vm.Program FilterExpr *vm.Program
MatchExpr *vm.Program MatchExpr *vm.Program
@ -61,7 +62,7 @@ type Runner struct {
FuzzyFile *files.File FuzzyFile *files.File
DumpFile *files.File DumpFile *files.File
StatFile *files.File StatFile *files.File
Progress *uiprogress.Progress Progress *mpb.Progress
Offset int Offset int
Limit int Limit int
RateLimit int RateLimit int
@ -92,6 +93,7 @@ func (r *Runner) PrepareConfig() *pool.Config {
Timeout: r.Timeout, Timeout: r.Timeout,
RateLimit: r.RateLimit, RateLimit: r.RateLimit,
Headers: r.Headers, Headers: r.Headers,
Method: r.Method,
Mod: pool.ModMap[r.Mod], Mod: pool.ModMap[r.Mod],
OutputCh: r.outputCh, OutputCh: r.outputCh,
FuzzyCh: r.fuzzyCh, FuzzyCh: r.fuzzyCh,
@ -157,7 +159,7 @@ func (r *Runner) Prepare(ctx context.Context) error {
}() }()
pool.Worder = words.NewWorderWithChan(ch) pool.Worder = words.NewWorderWithChan(ch)
pool.Worder.Fns = r.Fns pool.Worder.Fns = r.Fns
pool.Bar = pkg.NewBar("check", r.Count-r.Offset, r.Progress) pool.Bar = pkg.NewBar("check", r.Count-r.Offset, pool.Statistor, r.Progress)
pool.Run(ctx, r.Offset, r.Count) pool.Run(ctx, r.Offset, r.Count)
r.poolwg.Done() r.poolwg.Done()
}) })
@ -171,12 +173,7 @@ func (r *Runner) Prepare(ctx context.Context) error {
}() }()
if r.Count > 0 { if r.Count > 0 {
r.bar = r.Progress.AddBar(r.Count) r.addBar(r.Count)
r.bar.PrependCompleted()
r.bar.PrependFunc(func(b *uiprogress.Bar) string {
return fmt.Sprintf("total progressive: %d/%d ", r.finished, r.Count)
})
r.bar.AppendElapsed()
} }
r.Pools, err = ants.NewPoolWithFunc(r.PoolSize, func(i interface{}) { r.Pools, err = ants.NewPoolWithFunc(r.PoolSize, func(i interface{}) {
@ -219,7 +216,7 @@ func (r *Runner) Prepare(ctx context.Context) error {
} else { } else {
limit = pool.Statistor.Total limit = pool.Statistor.Total
} }
pool.Bar = pkg.NewBar(config.BaseURL, limit-pool.Statistor.Offset, r.Progress) pool.Bar = pkg.NewBar(config.BaseURL, limit-pool.Statistor.Offset, pool.Statistor, r.Progress)
logs.Log.Importantf("[pool] task: %s, total %d words, %d threads, proxy: %s", pool.BaseURL, limit-pool.Statistor.Offset, pool.Thread, pool.ProxyAddr) logs.Log.Importantf("[pool] task: %s, total %d words, %d threads, proxy: %s", pool.BaseURL, limit-pool.Statistor.Offset, pool.Thread, pool.ProxyAddr)
err = pool.Init() err = pool.Init()
if err != nil { if err != nil {
@ -330,8 +327,32 @@ Loop:
time.Sleep(100 * time.Millisecond) // 延迟100ms, 等所有数据处理完毕 time.Sleep(100 * time.Millisecond) // 延迟100ms, 等所有数据处理完毕
} }
func (r *Runner) addBar(total int) {
if r.Progress == nil {
return
}
prompt := "total progressive:"
r.bar = r.Progress.AddBar(int64(total),
mpb.BarFillerClearOnComplete(), // 可选:当进度条完成时清除
mpb.PrependDecorators(
// 显示自定义的信息,比如下载速度和进度
decor.Name(prompt, decor.WC{W: len(prompt) + 1, C: decor.DindentRight}), // 这里调整了装饰器的参数
decor.OnComplete( // 当进度完成时显示的文本
decor.Counters(0, "% d/% d"), " done!",
),
),
mpb.AppendDecorators(
// 显示经过的时间
decor.Elapsed(decor.ET_STYLE_GO, decor.WC{W: 4}),
),
)
}
func (r *Runner) Done() { func (r *Runner) Done() {
r.bar.Incr() if r.bar != nil {
r.bar.Increment()
}
r.finished++ r.finished++
r.poolwg.Done() r.poolwg.Done()
} }

View File

@ -2,7 +2,6 @@ package internal
import ( import (
"bytes" "bytes"
"github.com/antonmedv/expr/ast"
"github.com/chainreactors/spray/pkg" "github.com/chainreactors/spray/pkg"
"github.com/chainreactors/words/mask" "github.com/chainreactors/words/mask"
"github.com/chainreactors/words/rule" "github.com/chainreactors/words/rule"
@ -145,20 +144,20 @@ func loadRuleWithFiles(ruleFiles []string, filter string) ([]rule.Expression, er
return rule.Compile(rules.String(), filter).Expressions, nil return rule.Compile(rules.String(), filter).Expressions, nil
} }
type bytesPatcher struct{} //type bytesPatcher struct{}
//
func (p *bytesPatcher) Visit(node *ast.Node) { //func (p *bytesPatcher) Visit(node *ast.Node) {
switch (*node).(type) { // switch (*node).(type) {
case *ast.MemberNode: // case *ast.MemberNode:
ast.Patch(node, &ast.CallNode{ // ast.Patch(node, &ast.CallNode{
Callee: &ast.MemberNode{ // Callee: &ast.MemberNode{
Node: *node, // Node: *node,
Name: "String", // Name: "String",
Property: &ast.StringNode{Value: "String"}, // Property: &ast.StringNode{Value: "String"},
}, // },
}) // })
} // }
} //}
func wrapWordsFunc(f func(string) string) func(string) []string { func wrapWordsFunc(f func(string) string) func(string) []string {
return func(s string) []string { return func(s string) []string {

View File

@ -2,44 +2,60 @@ package pkg
import ( import (
"fmt" "fmt"
"github.com/chainreactors/go-metrics" "github.com/vbauerster/mpb/v8"
"github.com/gosuri/uiprogress" "github.com/vbauerster/mpb/v8/decor"
"time"
) )
func NewBar(u string, total int, progress *uiprogress.Progress) *Bar { func NewBar(u string, total int, stat *Statistor, p *mpb.Progress) *Bar {
bar := &Bar{ if p == nil {
Bar: progress.AddBar(total), return &Bar{
url: u, url: u,
m: metrics.NewMeter(),
} }
}
bar := p.AddBar(int64(total),
mpb.BarFillerClearOnComplete(),
mpb.BarRemoveOnComplete(),
mpb.PrependDecorators(
decor.Name(u, decor.WC{W: len(u) + 1, C: decor.DindentRight}), // 这里调整了装饰器的参数
decor.NewAverageSpeed(0, "% .0f/s ", time.Now()),
decor.Counters(0, "%d/%d"),
decor.Any(func(s decor.Statistics) string {
return fmt.Sprintf(" found: %d", stat.FoundNumber)
}),
),
mpb.AppendDecorators(
decor.Percentage(),
decor.Elapsed(decor.ET_STYLE_GO, decor.WC{W: 4}),
),
)
metrics.Register(bar.url, bar.m) return &Bar{
bar.PrependCompleted() url: u,
bar.PrependFunc(func(b *uiprogress.Bar) string { bar: bar,
return fmt.Sprintf("%f/s %d/%d", bar.m.Rate1(), bar.m.Count(), bar.Bar.Total) //m: m,
}) }
bar.PrependFunc(func(b *uiprogress.Bar) string {
return u
})
bar.AppendElapsed()
return bar
} }
type Bar struct { type Bar struct {
url string url string
total int bar *mpb.Bar
close bool //m metrics.Meter
*uiprogress.Bar
m metrics.Meter
} }
func (bar *Bar) Done() { func (bar *Bar) Done() {
bar.m.Mark(1) //bar.m.Mark(1)
bar.Incr() if bar.bar == nil {
return
}
bar.bar.Increment()
} }
func (bar *Bar) Close() { func (bar *Bar) Close() {
metrics.Unregister(bar.url) //metrics.Unregister(bar.url)
bar.close = true // 标记进度条为完成状态
if bar.bar == nil {
return
}
bar.bar.Abort(true)
} }

View File

@ -35,7 +35,7 @@ func NewBaseline(u, host string, resp *ihttp.Response) *Baseline {
copy(bl.Header, header) copy(bl.Header, header)
bl.HeaderLength = len(bl.Header) bl.HeaderLength = len(bl.Header)
if i := resp.ContentLength(); i != 0 && i <= ihttp.DefaultMaxBodySize { if i := resp.ContentLength(); ihttp.CheckBodySize(i) {
body := resp.Body() body := resp.Body()
bl.Body = make([]byte, len(body)) bl.Body = make([]byte, len(body))
copy(bl.Body, body) copy(bl.Body, body)
@ -44,13 +44,18 @@ func NewBaseline(u, host string, resp *ihttp.Response) *Baseline {
bl.Chunked = true bl.Chunked = true
bl.BodyLength = len(bl.Body) bl.BodyLength = len(bl.Body)
} else { } else {
bl.BodyLength = i bl.BodyLength = int(i)
} }
} }
bl.Raw = append(bl.Header, bl.Body...) bl.Raw = append(bl.Header, bl.Body...)
bl.Response, err = ParseRawResponse(bl.Raw) bl.Response, err = ParseRawResponse(bl.Raw)
if err != nil {
bl.IsValid = false
bl.Reason = ErrResponseError.Error()
bl.ErrString = err.Error()
return bl
}
if r := bl.Response.Header.Get("Location"); r != "" { if r := bl.Response.Header.Get("Location"); r != "" {
bl.RedirectURL = r bl.RedirectURL = r
} else { } else {
@ -86,7 +91,7 @@ func NewInvalidBaseline(u, host string, resp *ihttp.Response, reason string) *Ba
// 无效数据也要读取body, 否则keep-alive不生效 // 无效数据也要读取body, 否则keep-alive不生效
resp.Body() resp.Body()
bl.BodyLength = resp.ContentLength() bl.BodyLength = int(resp.ContentLength())
bl.RedirectURL = string(resp.GetHeader("Location")) bl.RedirectURL = string(resp.GetHeader("Location"))
bl.Dir = bl.IsDir() bl.Dir = bl.IsDir()
@ -188,7 +193,7 @@ func (bl *Baseline) CollectURL() {
} }
bl.URLs = iutils.StringsUnique(bl.URLs) bl.URLs = iutils.StringsUnique(bl.URLs)
if bl.URLs != nil { if len(bl.URLs) != 0 {
bl.Extracteds = append(bl.Extracteds, &parsers.Extracted{ bl.Extracteds = append(bl.Extracteds, &parsers.Extracted{
Name: "crawl", Name: "crawl",
ExtractResult: bl.URLs, ExtractResult: bl.URLs,

View File

@ -16,6 +16,7 @@ const (
ErrFuzzyRedirect ErrFuzzyRedirect
ErrFuzzyNotUnique ErrFuzzyNotUnique
ErrUrlError ErrUrlError
ErrResponseError
) )
var ErrMap = map[ErrorType]string{ var ErrMap = map[ErrorType]string{
@ -32,6 +33,7 @@ var ErrMap = map[ErrorType]string{
ErrFuzzyRedirect: "fuzzy redirect", ErrFuzzyRedirect: "fuzzy redirect",
ErrFuzzyNotUnique: "not unique", ErrFuzzyNotUnique: "not unique",
ErrUrlError: "url parse error", ErrUrlError: "url parse error",
ErrResponseError: "response parse error",
} }
func (e ErrorType) Error() string { func (e ErrorType) Error() string {

View File

@ -8,7 +8,7 @@ import (
// gogo fingers engine // gogo fingers engine
func FingersDetect(content []byte) common.Frameworks { func FingersDetect(content []byte) common.Frameworks {
frames, _ := FingerEngine.FingersEngine.Match(bytes.ToLower(content), "") frames, _ := FingerEngine.FingersEngine.HTTPMatch(bytes.ToLower(content), "")
return frames return frames
} }

View File

@ -28,7 +28,7 @@ func LoadTemplates() error {
if err != nil { if err != nil {
return err return err
} }
for _, f := range FingerEngine.FingersEngine.Fingers { for _, f := range FingerEngine.FingersEngine.HTTPFingers {
for _, rule := range f.Rules { for _, rule := range f.Rules {
if rule.SendDataStr != "" { if rule.SendDataStr != "" {
ActivePath = append(ActivePath, rule.SendDataStr) ActivePath = append(ActivePath, rule.SendDataStr)

View File

@ -1 +0,0 @@
package pkg

View File

@ -3,10 +3,10 @@ package pkg
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/chainreactors/logs" "github.com/chainreactors/logs"
"github.com/chainreactors/utils/iutils" "github.com/chainreactors/utils/iutils"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
"math/rand" "math/rand"
"net/http" "net/http"
"net/url" "net/url"

View File

@ -5,6 +5,7 @@ import (
"github.com/chainreactors/spray/cmd" "github.com/chainreactors/spray/cmd"
"github.com/gookit/config/v2" "github.com/gookit/config/v2"
"github.com/gookit/config/v2/yaml" "github.com/gookit/config/v2/yaml"
//_ "net/http/pprof"
) )
func init() { func init() {

@ -1 +1 @@
Subproject commit 89dd0246cdbb1ce4604e2538a661e826e8b41142 Subproject commit d4db8ffbae6f0d52b18fea2bcabe7b2c29c2153d