给全局的输出添加配色, 可以使用--no-color或-q关闭

This commit is contained in:
M09Ic 2022-12-15 00:19:06 +08:00
parent 34e544deaf
commit 073cf2a095
8 changed files with 136 additions and 17 deletions

View File

@ -284,7 +284,7 @@ spray支持断点续传, 可以通过`--resume-from`参数指定断点文件.
断点续传支持比命令行更自由的字典配置. 每个任务都可以拥有独立的-w/-r/-d配置. 因此某些特殊情况下要进行批量操作, 可以通过脚本去构造对应的stat文件, 实现更加自由的任务配置. 断点续传支持比命令行更自由的字典配置. 每个任务都可以拥有独立的-w/-r/-d配置. 因此某些特殊情况下要进行批量操作, 可以通过脚本去构造对应的stat文件, 实现更加自由的任务配置.
### 递归 ### 递归
spray并不鼓励使用递归, 因为spray的定位是批量从反代/cdn中发现隐形资产. 不管是因为批量, 还是因为反代/cdn, 99.9的情况都用不到递归. spray并不鼓励使用递归, 因为spray的定位是批量从反代/cdn中发现隐形资产. 不管是因为批量, 还是因为反代/cdn, 绝大多数的情况都用不到递归.
但为了兼容某些极为罕见的情况, spray依旧保留了递归的功能. 但为了兼容某些极为罕见的情况, spray依旧保留了递归的功能.

4
go.mod
View File

@ -7,9 +7,9 @@ require (
github.com/chainreactors/go-metrics v0.0.0-20220926021830-24787b7a10f8 github.com/chainreactors/go-metrics v0.0.0-20220926021830-24787b7a10f8
github.com/chainreactors/gogo/v2 v2.10.1 github.com/chainreactors/gogo/v2 v2.10.1
github.com/chainreactors/ipcs v0.0.13 github.com/chainreactors/ipcs v0.0.13
github.com/chainreactors/logs v0.6.2 github.com/chainreactors/logs v0.7.1-0.20221214153111-85f123ff6580
github.com/chainreactors/parsers v0.2.9-0.20221210155102-cc0814762410 github.com/chainreactors/parsers v0.2.9-0.20221210155102-cc0814762410
github.com/chainreactors/words v0.3.2-0.20221214062855-48dff09b01ad github.com/chainreactors/words v0.3.2-0.20221214154622-381fc37abdf9
) )
require ( require (

10
go.sum
View File

@ -19,6 +19,14 @@ github.com/chainreactors/ipcs v0.0.13/go.mod h1:E9M3Ohyq0TYQLlV4i2dbM9ThBZB1Nnd7
github.com/chainreactors/logs v0.6.1/go.mod h1:Y0EtAnoF0kiASIJUnXN0pcOt420iRpHOAnOhEphzRHA= github.com/chainreactors/logs v0.6.1/go.mod h1:Y0EtAnoF0kiASIJUnXN0pcOt420iRpHOAnOhEphzRHA=
github.com/chainreactors/logs v0.6.2 h1:Yz5oayjwxO6KkjfjnmtT5WKbWjTaBdttFcneaFTpBe0= github.com/chainreactors/logs v0.6.2 h1:Yz5oayjwxO6KkjfjnmtT5WKbWjTaBdttFcneaFTpBe0=
github.com/chainreactors/logs v0.6.2/go.mod h1:Y0EtAnoF0kiASIJUnXN0pcOt420iRpHOAnOhEphzRHA= github.com/chainreactors/logs v0.6.2/go.mod h1:Y0EtAnoF0kiASIJUnXN0pcOt420iRpHOAnOhEphzRHA=
github.com/chainreactors/logs v0.7.1-0.20221214130332-9bc5319887fe h1:FRMBKyuuh6EoHefqprP+pSblHrUxTaSp9GPJahYa+Fc=
github.com/chainreactors/logs v0.7.1-0.20221214130332-9bc5319887fe/go.mod h1:Y0EtAnoF0kiASIJUnXN0pcOt420iRpHOAnOhEphzRHA=
github.com/chainreactors/logs v0.7.1-0.20221214130646-2e08f98a1f71 h1:SpyPYjRihGyBiqoMUggXzCc4t9A0tmAvYdjghDG8s+M=
github.com/chainreactors/logs v0.7.1-0.20221214130646-2e08f98a1f71/go.mod h1:Y0EtAnoF0kiASIJUnXN0pcOt420iRpHOAnOhEphzRHA=
github.com/chainreactors/logs v0.7.1-0.20221214152543-60422cf64610 h1:ErODIlY9NmlrwEi6np3bm7HmuRZSaH3+ID2fJ2ViUpM=
github.com/chainreactors/logs v0.7.1-0.20221214152543-60422cf64610/go.mod h1:Y0EtAnoF0kiASIJUnXN0pcOt420iRpHOAnOhEphzRHA=
github.com/chainreactors/logs v0.7.1-0.20221214153111-85f123ff6580 h1:28gbL1t+Mm4AoP1MeKM9oeSHoPcUwIrzrLtmdusHMIo=
github.com/chainreactors/logs v0.7.1-0.20221214153111-85f123ff6580/go.mod h1:Y0EtAnoF0kiASIJUnXN0pcOt420iRpHOAnOhEphzRHA=
github.com/chainreactors/parsers v0.2.9-0.20221210155102-cc0814762410 h1:K7EV0wtUuN6Rvh/MgqaBXyElD3guPsgNR5kF8nrV7iw= github.com/chainreactors/parsers v0.2.9-0.20221210155102-cc0814762410 h1:K7EV0wtUuN6Rvh/MgqaBXyElD3guPsgNR5kF8nrV7iw=
github.com/chainreactors/parsers v0.2.9-0.20221210155102-cc0814762410/go.mod h1:Z9weht+lnFCk7UcwqFu6lXpS7u5vttiy0AJYOAyCCLA= github.com/chainreactors/parsers v0.2.9-0.20221210155102-cc0814762410/go.mod h1:Z9weht+lnFCk7UcwqFu6lXpS7u5vttiy0AJYOAyCCLA=
github.com/chainreactors/words v0.3.2-0.20221212161820-bae5f18558db h1:Rv6mcLAKXRXoZuifCwGTlXnuDbDpbDKC0JsTI1op/OA= github.com/chainreactors/words v0.3.2-0.20221212161820-bae5f18558db h1:Rv6mcLAKXRXoZuifCwGTlXnuDbDpbDKC0JsTI1op/OA=
@ -27,6 +35,8 @@ github.com/chainreactors/words v0.3.2-0.20221214061028-a7cf9f9f8ddb h1:9AV8SH+Sv
github.com/chainreactors/words v0.3.2-0.20221214061028-a7cf9f9f8ddb/go.mod h1:QIWX1vMT5j/Mp9zx3/wgZh3FqskhjCbo/3Ffy/Hxj9w= github.com/chainreactors/words v0.3.2-0.20221214061028-a7cf9f9f8ddb/go.mod h1:QIWX1vMT5j/Mp9zx3/wgZh3FqskhjCbo/3Ffy/Hxj9w=
github.com/chainreactors/words v0.3.2-0.20221214062855-48dff09b01ad h1:uL3TIQgvFY7dLoX0tAzIIXilCPIcNeLz/124gs+SA/Q= github.com/chainreactors/words v0.3.2-0.20221214062855-48dff09b01ad h1:uL3TIQgvFY7dLoX0tAzIIXilCPIcNeLz/124gs+SA/Q=
github.com/chainreactors/words v0.3.2-0.20221214062855-48dff09b01ad/go.mod h1:QIWX1vMT5j/Mp9zx3/wgZh3FqskhjCbo/3Ffy/Hxj9w= github.com/chainreactors/words v0.3.2-0.20221214062855-48dff09b01ad/go.mod h1:QIWX1vMT5j/Mp9zx3/wgZh3FqskhjCbo/3Ffy/Hxj9w=
github.com/chainreactors/words v0.3.2-0.20221214154622-381fc37abdf9 h1:IUNopSuorfINmn4pOuSwZtxJbg8zsRIZ67a33SiYoQ0=
github.com/chainreactors/words v0.3.2-0.20221214154622-381fc37abdf9/go.mod h1:QIWX1vMT5j/Mp9zx3/wgZh3FqskhjCbo/3Ffy/Hxj9w=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/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.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=

View File

@ -92,6 +92,7 @@ type MiscOptions struct {
PoolSize int `short:"p" long:"pool" default:"5" description:"Int, Pool size"` PoolSize int `short:"p" long:"pool" default:"5" description:"Int, Pool size"`
Threads int `short:"t" long:"thread" default:"20" description:"Int, number of threads per pool (seconds)"` Threads int `short:"t" long:"thread" default:"20" description:"Int, number of threads per pool (seconds)"`
Debug bool `long:"debug" description:"Bool, output debug info"` Debug bool `long:"debug" description:"Bool, output debug info"`
NoColor bool `long:"no-color" description:"Bool, no color"`
Quiet bool `short:"q" long:"quiet" description:"Bool, Quiet"` Quiet bool `short:"q" long:"quiet" description:"Bool, Quiet"`
NoBar bool `long:"no-bar" description:"Bool, No progress bar"` NoBar bool `long:"no-bar" description:"Bool, No progress bar"`
Mod string `short:"m" long:"mod" default:"path" choice:"path" choice:"host" description:"String, path/host spray"` Mod string `short:"m" long:"mod" default:"path" choice:"path" choice:"host" description:"String, path/host spray"`
@ -139,11 +140,19 @@ func (opt *Option) PrepareRunner() (*Runner, error) {
} }
} }
// 一些全局变量初始化 // 一些全局变量初始化
if !opt.NoColor {
logs.Log.Color = true
logs.DefaultColorMap[logs.Info] = logs.PurpleBold
logs.DefaultColorMap[logs.Important] = logs.Green
r.Color = true
}
if opt.Debug { if opt.Debug {
logs.Log.Level = logs.Debug logs.Log.Level = logs.Debug
} }
if opt.Quiet { if opt.Quiet {
logs.Log.Quiet = true logs.Log.Quiet = true
logs.Log.Color = false
r.Color = false
} }
if !(opt.Quiet || opt.NoBar) { if !(opt.Quiet || opt.NoBar) {
r.Progress.Start() r.Progress.Start()

View File

@ -230,7 +230,7 @@ func (pool *Pool) Init() error {
return fmt.Errorf(pool.index.String()) return fmt.Errorf(pool.index.String())
} }
pool.index.Collect() pool.index.Collect()
logs.Log.Important("[baseline.index] " + pool.index.String()) logs.Log.Info("[baseline.index] " + pool.index.String())
pool.initwg.Add(1) pool.initwg.Add(1)
pool.reqPool.Invoke(newUnit(pkg.RandPath(), InitRandomSource)) pool.reqPool.Invoke(newUnit(pkg.RandPath(), InitRandomSource))
@ -240,14 +240,14 @@ func (pool *Pool) Init() error {
return fmt.Errorf(pool.random.String()) return fmt.Errorf(pool.random.String())
} }
pool.random.Collect() pool.random.Collect()
logs.Log.Important("[baseline.random] " + pool.random.String()) logs.Log.Info("[baseline.random] " + pool.random.String())
if pool.random.RedirectURL != "" { if pool.random.RedirectURL != "" {
// 自定协议升级 // 自定协议升级
// 某些网站http会重定向到https, 如果发现随机目录出现这种情况, 则自定将baseurl升级为https // 某些网站http会重定向到https, 如果发现随机目录出现这种情况, 则自定将baseurl升级为https
rurl, err := url.Parse(pool.random.RedirectURL) rurl, err := url.Parse(pool.random.RedirectURL)
if err == nil && rurl.Hostname() == pool.random.Url.Hostname() && pool.random.Url.Scheme == "http" && rurl.Scheme == "https" { if err == nil && rurl.Hostname() == pool.random.Url.Hostname() && pool.random.Url.Scheme == "http" && rurl.Scheme == "https" {
logs.Log.Importantf("baseurl %s upgrade http to https", pool.BaseURL) logs.Log.Infof("baseurl %s upgrade http to https", pool.BaseURL)
pool.BaseURL = strings.Replace(pool.BaseURL, "http", "https", 1) pool.BaseURL = strings.Replace(pool.BaseURL, "http", "https", 1)
} }
} }
@ -435,7 +435,7 @@ func (pool *Pool) addFuzzyBaseline(bl *pkg.Baseline) {
pool.locker.Lock() pool.locker.Lock()
pool.baselines[bl.Status] = bl pool.baselines[bl.Status] = bl
pool.locker.Unlock() pool.locker.Unlock()
logs.Log.Importantf("[baseline.%dinit] %s", bl.Status, bl.String()) logs.Log.Infof("[baseline.%dinit] %s", bl.Status, bl.String())
} }
} }

View File

@ -68,6 +68,7 @@ type Runner struct {
CheckPeriod int CheckPeriod int
ErrPeriod int ErrPeriod int
BreakThreshold int BreakThreshold int
Color bool
CheckOnly bool CheckOnly bool
Force bool Force bool
IgnoreWaf bool IgnoreWaf bool
@ -198,8 +199,14 @@ func (r *Runner) Prepare(ctx context.Context) error {
} }
pool.Run(ctx, pool.Statistor.Offset, limit) pool.Run(ctx, pool.Statistor.Offset, limit)
logs.Log.Important(pool.Statistor.String()) if r.Color {
logs.Log.Important(pool.Statistor.Detail()) logs.Log.Important(pool.Statistor.ColorString())
logs.Log.Important(pool.Statistor.ColorDetail())
} else {
logs.Log.Important(pool.Statistor.String())
logs.Log.Important(pool.Statistor.Detail())
}
if r.StatFile != nil { if r.StatFile != nil {
r.StatFile.SafeWrite(pool.Statistor.Json()) r.StatFile.SafeWrite(pool.Statistor.Json())
r.StatFile.SafeSync() r.StatFile.SafeSync()
@ -314,13 +321,26 @@ func (r *Runner) Outputting() {
} else { } else {
if len(r.Probes) > 0 { if len(r.Probes) > 0 {
saveFunc = func(bl *pkg.Baseline) { if r.Color {
logs.Log.Console("[+] " + bl.Format(r.Probes) + "\n") saveFunc = func(bl *pkg.Baseline) {
logs.Log.Console(logs.GreenBold("[+] " + bl.Format(r.Probes) + "\n"))
}
} else {
saveFunc = func(bl *pkg.Baseline) {
logs.Log.Console("[+] " + bl.Format(r.Probes) + "\n")
}
} }
} else { } else {
saveFunc = func(bl *pkg.Baseline) { if r.Color {
logs.Log.Console("[+] " + bl.String() + "\n") saveFunc = func(bl *pkg.Baseline) {
logs.Log.Console(logs.GreenBold("[+] " + bl.ColorString() + "\n"))
}
} else {
saveFunc = func(bl *pkg.Baseline) {
logs.Log.Console("[+] " + bl.String() + "\n")
}
} }
} }
} }
@ -340,7 +360,12 @@ func (r *Runner) Outputting() {
r.AddPool(&Task{baseUrl: bl.UrlString, depth: bl.RecuDepth + 1}) r.AddPool(&Task{baseUrl: bl.UrlString, depth: bl.RecuDepth + 1})
} }
} else { } else {
logs.Log.Debug(bl.String()) if r.Color {
logs.Log.Debug(bl.ColorString())
} else {
logs.Log.Debug(bl.String())
}
} }
} }
} }
@ -353,9 +378,13 @@ func (r *Runner) Outputting() {
r.FuzzyFile.SafeWrite(bl.Jsonify() + "\n") r.FuzzyFile.SafeWrite(bl.Jsonify() + "\n")
} }
} else { } else {
fuzzySaveFunc = func(bl *pkg.Baseline) { if r.Color {
if r.Fuzzy { fuzzySaveFunc = func(bl *pkg.Baseline) {
logs.Log.Console("[baseline.fuzzy] " + bl.String() + "\n") logs.Log.Console(logs.GreenBold("[fuzzy] " + bl.ColorString() + "\n"))
}
} else {
fuzzySaveFunc = func(bl *pkg.Baseline) {
logs.Log.Console("[fuzzy] " + bl.String() + "\n")
} }
} }
} }

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"github.com/chainreactors/gogo/v2/pkg/utils" "github.com/chainreactors/gogo/v2/pkg/utils"
"github.com/chainreactors/logs"
"github.com/chainreactors/parsers" "github.com/chainreactors/parsers"
"github.com/chainreactors/spray/pkg/ihttp" "github.com/chainreactors/spray/pkg/ihttp"
"net/url" "net/url"
@ -227,6 +228,47 @@ func (bl *Baseline) Format(probes []string) string {
return line.String() return line.String()
} }
func (bl *Baseline) ColorString() string {
var line strings.Builder
if bl.FrontURL != "" {
line.WriteString("\t")
line.WriteString(logs.CyanLine(bl.FrontURL))
line.WriteString(" --> ")
}
line.WriteString(logs.GreenLine(bl.UrlString))
if bl.Host != "" {
line.WriteString(" (" + bl.Host + ")")
}
if bl.Reason != "" {
line.WriteString(" [reason: ")
line.WriteString(logs.YellowBold(bl.Reason))
line.WriteString("]")
}
if bl.ErrString != "" {
line.WriteString(" [err: ")
line.WriteString(logs.RedBold(bl.ErrString))
line.WriteString("]")
return line.String()
}
line.WriteString(" - ")
line.WriteString(logs.GreenBold(strconv.Itoa(bl.Status)))
line.WriteString(" - ")
line.WriteString(logs.Blue(strconv.Itoa(bl.BodyLength)))
line.WriteString(" - ")
line.WriteString(logs.Blue(strconv.Itoa(int(bl.Spended)) + "ms"))
line.WriteString(logs.GreenLine(bl.Additional("title")))
line.WriteString(logs.Blue(bl.Frameworks.String()))
line.WriteString(logs.Blue(bl.Extracteds.String()))
if bl.RedirectURL != "" {
line.WriteString(" --> ")
line.WriteString(logs.CyanLine(bl.RedirectURL))
line.WriteString(" ")
}
return line.String()
}
func (bl *Baseline) String() string { func (bl *Baseline) String() string {
var line strings.Builder var line strings.Builder
if bl.FrontURL != "" { if bl.FrontURL != "" {

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/chainreactors/logs"
"io/ioutil" "io/ioutil"
"strconv" "strconv"
"strings" "strings"
@ -56,6 +57,21 @@ type Statistor struct {
RuleFilter string `json:"rule_filter"` RuleFilter string `json:"rule_filter"`
} }
func (stat *Statistor) ColorString() string {
var s strings.Builder
s.WriteString(fmt.Sprintf("[stat] %s took %d s, request total: %s, finish: %s/%s, found: %s, check: %s, failed: %s", logs.GreenLine(stat.BaseUrl), stat.EndTime-stat.StartTime, logs.YellowBold(strconv.Itoa(int(stat.ReqTotal))), logs.YellowBold(strconv.Itoa(stat.End)), logs.YellowBold(strconv.Itoa(stat.Total)), logs.YellowBold(strconv.Itoa(stat.FoundNumber)), logs.YellowBold(strconv.Itoa(stat.CheckNumber)), logs.YellowBold(strconv.Itoa(int(stat.FailedNumber)))))
if stat.FuzzyNumber != 0 {
s.WriteString(", fuzzy: " + logs.Yellow(strconv.Itoa(stat.FuzzyNumber)))
}
if stat.FilteredNumber != 0 {
s.WriteString(", filtered: " + logs.Yellow(strconv.Itoa(stat.FilteredNumber)))
}
if stat.WafedNumber != 0 {
s.WriteString(", wafed: " + logs.Yellow(strconv.Itoa(stat.WafedNumber)))
}
return s.String()
}
func (stat *Statistor) String() string { func (stat *Statistor) String() string {
var s strings.Builder var s strings.Builder
s.WriteString(fmt.Sprintf("[stat] %s took %d s, request total: %d, finish: %d/%d, found: %d, check: %d, failed: %d", stat.BaseUrl, stat.EndTime-stat.StartTime, stat.ReqTotal, stat.End, stat.Total, stat.FoundNumber, stat.CheckNumber, stat.FailedNumber)) s.WriteString(fmt.Sprintf("[stat] %s took %d s, request total: %d, finish: %d/%d, found: %d, check: %d, failed: %d", stat.BaseUrl, stat.EndTime-stat.StartTime, stat.ReqTotal, stat.End, stat.Total, stat.FoundNumber, stat.CheckNumber, stat.FailedNumber))
@ -85,6 +101,19 @@ func (stat *Statistor) Detail() string {
return s.String() return s.String()
} }
func (stat *Statistor) ColorDetail() string {
var s strings.Builder
s.WriteString("[stat] ")
s.WriteString(stat.BaseUrl)
for k, v := range stat.Counts {
if k == 0 {
continue
}
s.WriteString(fmt.Sprintf(" %s: %s,", logs.YellowBold(strconv.Itoa(k)), logs.YellowBold(strconv.Itoa(v))))
}
return s.String()
}
func (stat *Statistor) Json() string { func (stat *Statistor) Json() string {
content, err := json.Marshal(stat) content, err := json.Marshal(stat)
if err != nil { if err != nil {