mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2026-11-11 04:01:35 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
930f51fcf0 | ||
|
|
8a9dc11f36 | ||
|
|
59a624f2bd | ||
|
|
af2284b741 | ||
|
|
3ee1bfa4ba | ||
|
|
ac240cd14c | ||
|
|
0b70d58665 | ||
|
|
7d06c97a0d | ||
|
|
a8ec9819fc | ||
|
|
bf2699462f | ||
|
|
f3d2f394ce | ||
|
|
e1fea06f2c | ||
|
|
2dd8c8c5ae | ||
|
|
94817ca300 | ||
|
|
fcd5c6b111 | ||
|
|
d93b4a01df | ||
|
|
aaf3cf9cb3 | ||
|
|
df67578d98 | ||
|
|
d988de45f6 | ||
|
|
e523d3872c | ||
|
|
b7f76cfd4b | ||
|
|
1700cdf4e2 | ||
|
|
3a41f752e4 | ||
|
|
a751993808 | ||
|
|
e9f6febe01 | ||
|
|
3685379960 | ||
|
|
117d4a30e2 | ||
|
|
397ad04540 | ||
|
|
fd14472181 | ||
|
|
cd289a81c9 | ||
|
|
cbda987288 | ||
|
|
4aff6d7189 | ||
|
|
4b43bd0c65 | ||
|
|
a335e4962e | ||
|
|
946d81dc56 | ||
|
|
a69a082247 | ||
|
|
582bf865d2 | ||
|
|
d93454eee3 | ||
|
|
de9f9620d5 | ||
|
|
29f4ac9160 | ||
|
|
e2cf5dda35 | ||
|
|
da38d4db20 | ||
|
|
3f295226ad | ||
|
|
6f6c1d3679 | ||
|
|
89858a2ec8 | ||
|
|
75183c7660 | ||
|
|
b445f7fc87 | ||
|
|
e98d1d6a97 | ||
|
|
46830d4402 | ||
|
|
52beea3bcd | ||
|
|
ea3ecbf2d2 | ||
|
|
4d29bff759 |
No files matched your search
+1
-1
@@ -8,7 +8,7 @@ RUN go mod download
|
||||
RUN go build ./cmd/nuclei
|
||||
|
||||
# Release
|
||||
FROM alpine:3.18.2
|
||||
FROM alpine:3.18.6
|
||||
RUN apk -U upgrade --no-cache \
|
||||
&& apk add --no-cache bind-tools chromium ca-certificates
|
||||
COPY --from=build-env /app/nuclei /usr/local/bin/
|
||||
|
||||
@@ -20,14 +20,15 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/core"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/input/provider"
|
||||
parsers "github.com/projectdiscovery/nuclei/v3/pkg/loader/workflow"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/parsers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/hosterrorscache"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolinit"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/ratelimit"
|
||||
@@ -98,6 +99,7 @@ func executeNucleiAsLibrary(templatePath, templateURL string) ([]string, error)
|
||||
catalog := disk.NewCatalog(path.Join(home, "nuclei-templates"))
|
||||
ratelimiter := ratelimit.New(context.Background(), 150, time.Second)
|
||||
defer ratelimiter.Stop()
|
||||
|
||||
executerOpts := protocols.ExecutorOptions{
|
||||
Output: outputWriter,
|
||||
Options: defaultOpts,
|
||||
@@ -109,6 +111,7 @@ func executeNucleiAsLibrary(templatePath, templateURL string) ([]string, error)
|
||||
HostErrorsCache: cache,
|
||||
Colorizer: aurora.NewAurora(true),
|
||||
ResumeCfg: types.NewResumeCfg(),
|
||||
Parser: templates.NewParser(),
|
||||
}
|
||||
engine := core.New(defaultOpts)
|
||||
engine.SetExecuterOptions(executerOpts)
|
||||
|
||||
@@ -30,7 +30,7 @@ require (
|
||||
github.com/remeh/sizedwaitgroup v1.0.0
|
||||
github.com/rs/xid v1.5.0
|
||||
github.com/segmentio/ksuid v1.0.4
|
||||
github.com/shirou/gopsutil/v3 v3.23.7 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.24.2 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/cast v1.5.1
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
@@ -63,7 +63,7 @@ require (
|
||||
github.com/denisenkom/go-mssqldb v0.12.3
|
||||
github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e
|
||||
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/getkin/kin-openapi v0.123.0
|
||||
github.com/go-git/go-git/v5 v5.11.0
|
||||
@@ -94,7 +94,7 @@ require (
|
||||
github.com/projectdiscovery/tlsx v1.1.6
|
||||
github.com/projectdiscovery/uncover v1.0.7
|
||||
github.com/projectdiscovery/useragent v0.0.40
|
||||
github.com/projectdiscovery/utils v0.0.84-0.20240312214300-d3ba70dbb9ca
|
||||
github.com/projectdiscovery/utils v0.0.84-0.20240313184656-e3ec80f4dd42
|
||||
github.com/projectdiscovery/wappalyzergo v0.0.112
|
||||
github.com/redis/go-redis/v9 v9.1.0
|
||||
github.com/sashabaranov/go-openai v1.15.3
|
||||
@@ -102,7 +102,6 @@ require (
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/zmap/zgrab2 v0.1.8-0.20230806160807-97ba87c0e706
|
||||
golang.org/x/term v0.17.0
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
moul.io/http2curl v1.0.0
|
||||
)
|
||||
@@ -138,7 +137,7 @@ require (
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dlclark/regexp2 v1.10.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/docker/cli v24.0.5+incompatible // indirect
|
||||
github.com/docker/docker v24.0.7+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
@@ -152,14 +151,14 @@ require (
|
||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.9 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/google/certificate-transparency-go v1.1.4 // indirect
|
||||
github.com/google/go-github/v30 v30.1.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
|
||||
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||
@@ -204,6 +203,7 @@ require (
|
||||
github.com/projectdiscovery/stringsutil v0.0.2 // indirect
|
||||
github.com/quic-go/quic-go v0.40.1 // indirect
|
||||
github.com/refraction-networking/utls v1.6.1 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.2.1 // indirect
|
||||
@@ -227,6 +227,7 @@ require (
|
||||
github.com/yuin/goldmark-emoji v1.0.1 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
gopkg.in/djherbis/times.v1 v1.3.0 // indirect
|
||||
mellium.im/sasl v0.3.1 // indirect
|
||||
)
|
||||
@@ -281,8 +282,8 @@ require (
|
||||
github.com/projectdiscovery/networkpolicy v0.0.8
|
||||
github.com/rivo/uniseg v0.4.6 // indirect
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/trivago/tgo v1.0.7
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
@@ -290,7 +291,7 @@ require (
|
||||
github.com/ysmood/goob v0.4.0 // indirect
|
||||
github.com/ysmood/gson v0.7.3 // indirect
|
||||
github.com/ysmood/leakless v0.8.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 // indirect
|
||||
github.com/zmap/zcrypto v0.0.0-20231219022726-a1f61fb1661c // indirect
|
||||
go.etcd.io/bbolt v1.3.8 // indirect
|
||||
@@ -335,8 +336,10 @@ require (
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
|
||||
github.com/sergi/go-diff v1.2.0 // indirect
|
||||
github.com/src-d/gcfg v1.4.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
// https://go.dev/ref/mod#go-mod-file-retract
|
||||
retract v3.2.0 // retract due to broken js protocol issue
|
||||
@@ -92,8 +92,6 @@ github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/akrylysov/pogreb v0.10.2 h1:e6PxmeyEhWyi2AKOBIJzAEi4HkiC+lKyCocRGlnDi78=
|
||||
github.com/akrylysov/pogreb v0.10.2/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
|
||||
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
|
||||
github.com/alecthomas/jsonschema v0.0.0-20211022214203-8b29eab41725 h1:NjwIgLQlD46o79bheVG4SCdRnnOz4XtgUN1WABX5DLA=
|
||||
@@ -117,7 +115,6 @@ github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsVi
|
||||
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
|
||||
github.com/andygrunwald/go-jira v1.16.0 h1:PU7C7Fkk5L96JvPc6vDVIrd99vdPnYudHu4ju2c2ikQ=
|
||||
github.com/andygrunwald/go-jira v1.16.0/go.mod h1:UQH4IBVxIYWbgagc0LF/k9FRs9xjIiQ8hIcC6HfLwFU=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/antchfx/htmlquery v1.3.0 h1:5I5yNFOVI+egyia5F2s/5Do2nFWxJz41Tr3DyfKD25E=
|
||||
@@ -272,8 +269,8 @@ github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c/go.mod h1:
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
|
||||
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc=
|
||||
@@ -286,8 +283,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
||||
github.com/dop251/goja v0.0.0-20230531210528-d7324b2d74f7/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
||||
github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e h1:UvQD6hTSfeM6hhTQ24Dlw2RppP05W7SWbWb6kubJAog=
|
||||
github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
||||
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204 h1:O7I1iuzEA7SG+dK8ocOBSlYAA9jBUmCYl/Qa7ey7JAM=
|
||||
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20230821135201-94e508132562 h1:ObbB2tzHWWAxzsG5futqeq2Ual2zYlo/+eMkSc5sn8w=
|
||||
@@ -303,7 +300,6 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
@@ -316,7 +312,6 @@ github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||
@@ -337,7 +332,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
|
||||
@@ -387,8 +381,9 @@ github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+j
|
||||
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-rod/rod v0.114.0 h1:P+zLOqsj+vKf4C86SfjP6ymyPl9VXoYKm+ceCeQms6Y=
|
||||
github.com/go-rod/rod v0.114.0/go.mod h1:aiedSEFg5DwG/fnNbUOTPMTTWX3MRj6vIs/a684Mthw=
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q=
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
@@ -497,8 +492,8 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
|
||||
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ=
|
||||
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q=
|
||||
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
@@ -594,7 +589,6 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jlaffaye/ftp v0.0.0-20190624084859-c1312a7102bf/go.mod h1:lli8NYPQOFy3O++YmYbqVgOcQ1JPCwdOy+5zSjKJ9qY=
|
||||
@@ -623,7 +617,6 @@ github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4d
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kataras/jwt v0.1.10 h1:GBXOF9RVInDPhCFBiDumRG9Tt27l7ugLeLo8HL5SeKQ=
|
||||
github.com/kataras/jwt v0.1.10/go.mod h1:xkimAtDhU/aGlQqjwvgtg+VyuPwMiyZHaY8LJRh0mYo=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
@@ -650,7 +643,6 @@ github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NB
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
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/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
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=
|
||||
@@ -798,7 +790,6 @@ github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnz
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||
@@ -890,8 +881,8 @@ github.com/projectdiscovery/uncover v1.0.7 h1:ut+2lTuvmftmveqF5RTjMWAgyLj8ltPQC7
|
||||
github.com/projectdiscovery/uncover v1.0.7/go.mod h1:HFXgm1sRPuoN0D4oATljPIdmbo/EEh1wVuxQqo/dwFE=
|
||||
github.com/projectdiscovery/useragent v0.0.40 h1:1LUhReSGPkhqsM5n40OOC9dIoNqMGs1dyGFJcOmg2Fo=
|
||||
github.com/projectdiscovery/useragent v0.0.40/go.mod h1:EvK1x3s948Gtqb/XOahXcauyejCL/rSgy5d1IAvsKT4=
|
||||
github.com/projectdiscovery/utils v0.0.84-0.20240312214300-d3ba70dbb9ca h1:GY9lUYDlENXPSFPJH01Bm1BfhrUF2jpnUBR+K4VPJIs=
|
||||
github.com/projectdiscovery/utils v0.0.84-0.20240312214300-d3ba70dbb9ca/go.mod h1:wzMfHBq2I9oy+DEiMfUYV86g1D7eXKaQsgWnqFpmMtI=
|
||||
github.com/projectdiscovery/utils v0.0.84-0.20240313184656-e3ec80f4dd42 h1:l22rSOP8i6HXu1QfAtIot8NvmJgUmBHEn6Mih7s8Gak=
|
||||
github.com/projectdiscovery/utils v0.0.84-0.20240313184656-e3ec80f4dd42/go.mod h1:VsoXXTuNAAziuodKWakLyurVXaV4tNTJU4Eo8umyr3Q=
|
||||
github.com/projectdiscovery/wappalyzergo v0.0.112 h1:QPpp5jmj1lqLd5mFdFKQ9VvcYhQNqyU9Mr+IB0US2zA=
|
||||
github.com/projectdiscovery/wappalyzergo v0.0.112/go.mod h1:hc/o+fgM8KtdpFesjfBTmHTwsR+yBd+4kYZW/DGy/x8=
|
||||
github.com/projectdiscovery/yamldoc-go v1.0.4 h1:eZoESapnMw6WAHiVgRwNqvbJEfNHEH148uthhFbG5jE=
|
||||
@@ -961,11 +952,12 @@ github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c
|
||||
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
|
||||
github.com/seh-msft/burpxml v1.0.1 h1:5G3QPSzvfA1WcX7LkxmKBmK2RnNyGviGWnJPumE0nwg=
|
||||
github.com/seh-msft/burpxml v1.0.1/go.mod h1:lTViCHPtGGS0scK0B4krm6Ld1kVZLWzQccwUomRc58I=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
|
||||
github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil/v3 v3.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y=
|
||||
github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||
@@ -995,14 +987,11 @@ github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
@@ -1044,10 +1033,10 @@ github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaym
|
||||
github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw=
|
||||
github.com/tim-ywliu/nested-logrus-formatter v1.3.2 h1:jugNJ2/CNCI79SxOJCOhwUHeN3O7/7/bj+ZRGOFlCSw=
|
||||
github.com/tim-ywliu/nested-logrus-formatter v1.3.2/go.mod h1:oGPmcxZB65j9Wo7mCnQKSrKEJtVDqyjD666SGmyStXI=
|
||||
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
||||
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
||||
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
||||
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/trivago/tgo v1.0.7 h1:uaWH/XIy9aWYWpjm2CU3RpcqZXmX2ysQ9/Go+d9gyrM=
|
||||
github.com/trivago/tgo v1.0.7/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc=
|
||||
@@ -1076,7 +1065,6 @@ github.com/weppos/publicsuffix-go v0.30.2-0.20230730094716-a20f9abcc222/go.mod h
|
||||
github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220927085643-dc0d00c92642/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE=
|
||||
github.com/xanzy/go-gitlab v0.84.0 h1:PdpCaskQSgcVDsx21c6ikf8Rfyo7SNtFAJwP9PrbCFE=
|
||||
github.com/xanzy/go-gitlab v0.84.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -1116,8 +1104,8 @@ github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
|
||||
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
|
||||
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
|
||||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
|
||||
@@ -1169,7 +1157,6 @@ golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -1322,7 +1309,6 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/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-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1379,7 +1365,6 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
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.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1387,6 +1372,7 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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=
|
||||
@@ -1440,7 +1426,6 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -1591,12 +1576,6 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
|
||||
+26
-20
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/authprovider"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/input/provider"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/installer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/loader/parser"
|
||||
uncoverlib "github.com/projectdiscovery/uncover"
|
||||
pdcpauth "github.com/projectdiscovery/utils/auth/pdcp"
|
||||
"github.com/projectdiscovery/utils/env"
|
||||
@@ -37,8 +38,8 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/core"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/external/customtemplates"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/input"
|
||||
parsers "github.com/projectdiscovery/nuclei/v3/pkg/loader/workflow"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/parsers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/projectfile"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
@@ -87,6 +88,7 @@ type Runner struct {
|
||||
inputProvider provider.InputProvider
|
||||
//general purpose temporary directory
|
||||
tmpDir string
|
||||
parser parser.Parser
|
||||
}
|
||||
|
||||
const pprofServerAddress = "127.0.0.1:8086"
|
||||
@@ -148,12 +150,15 @@ func New(options *types.Options) (*Runner, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if options.Validate {
|
||||
parsers.ShouldValidate = true
|
||||
}
|
||||
parser := templates.NewParser()
|
||||
|
||||
if options.Validate {
|
||||
parser.ShouldValidate = true
|
||||
}
|
||||
// TODO: refactor to pass options reference globally without cycles
|
||||
parsers.NoStrictSyntax = options.NoStrictSyntax
|
||||
parser.NoStrictSyntax = options.NoStrictSyntax
|
||||
runner.parser = parser
|
||||
|
||||
yaml.StrictSyntax = !options.NoStrictSyntax
|
||||
|
||||
if options.Headless {
|
||||
@@ -431,6 +436,7 @@ func (r *Runner) RunEnumeration() error {
|
||||
ExcludeMatchers: excludematchers.New(r.options.ExcludeMatchers),
|
||||
InputHelper: input.NewHelper(),
|
||||
TemporaryDirectory: r.tmpDir,
|
||||
Parser: r.parser,
|
||||
}
|
||||
|
||||
if len(r.options.SecretsFile) > 0 && !r.options.Validate {
|
||||
@@ -483,7 +489,7 @@ func (r *Runner) RunEnumeration() error {
|
||||
if err := store.ValidateTemplates(); err != nil {
|
||||
return err
|
||||
}
|
||||
if stats.GetValue(parsers.SyntaxErrorStats) == 0 && stats.GetValue(parsers.SyntaxWarningStats) == 0 && stats.GetValue(parsers.RuntimeWarningsStats) == 0 {
|
||||
if stats.GetValue(templates.SyntaxErrorStats) == 0 && stats.GetValue(templates.SyntaxWarningStats) == 0 && stats.GetValue(templates.RuntimeWarningsStats) == 0 {
|
||||
gologger.Info().Msgf("All templates validated successfully\n")
|
||||
} else {
|
||||
return errors.New("encountered errors while performing template validation")
|
||||
@@ -495,9 +501,6 @@ func (r *Runner) RunEnumeration() error {
|
||||
disk.PrintDeprecatedPathsMsgIfApplicable(r.options.Silent)
|
||||
templates.PrintDeprecatedProtocolNameMsgIfApplicable(r.options.Silent, r.options.Verbose)
|
||||
|
||||
// purge global caches primarily used for loading templates
|
||||
config.DefaultConfig.PurgeGlobalCache()
|
||||
|
||||
// add the hosts from the metadata queries of loaded templates into input provider
|
||||
if r.options.Uncover && len(r.options.UncoverQuery) == 0 {
|
||||
uncoverOpts := &uncoverlib.Options{
|
||||
@@ -634,19 +637,22 @@ func (r *Runner) executeTemplatesInput(store *loader.Store, engine *core.Engine)
|
||||
// displayExecutionInfo displays misc info about the nuclei engine execution
|
||||
func (r *Runner) displayExecutionInfo(store *loader.Store) {
|
||||
// Display stats for any loaded templates' syntax warnings or errors
|
||||
stats.Display(parsers.SyntaxWarningStats)
|
||||
stats.Display(parsers.SyntaxErrorStats)
|
||||
stats.Display(parsers.RuntimeWarningsStats)
|
||||
stats.Display(templates.SyntaxWarningStats)
|
||||
stats.Display(templates.SyntaxErrorStats)
|
||||
stats.Display(templates.RuntimeWarningsStats)
|
||||
if r.options.Verbose {
|
||||
// only print these stats in verbose mode
|
||||
stats.DisplayAsWarning(parsers.HeadlessFlagWarningStats)
|
||||
stats.DisplayAsWarning(parsers.CodeFlagWarningStats)
|
||||
stats.DisplayAsWarning(parsers.FuzzFlagWarningStats)
|
||||
stats.DisplayAsWarning(parsers.TemplatesExecutedStats)
|
||||
stats.DisplayAsWarning(templates.HeadlessFlagWarningStats)
|
||||
stats.DisplayAsWarning(templates.CodeFlagWarningStats)
|
||||
stats.DisplayAsWarning(templates.TemplatesExecutedStats)
|
||||
stats.DisplayAsWarning(templates.HeadlessFlagWarningStats)
|
||||
stats.DisplayAsWarning(templates.CodeFlagWarningStats)
|
||||
stats.DisplayAsWarning(templates.FuzzFlagWarningStats)
|
||||
stats.DisplayAsWarning(templates.TemplatesExecutedStats)
|
||||
}
|
||||
|
||||
stats.DisplayAsWarning(parsers.UnsignedCodeWarning)
|
||||
stats.ForceDisplayWarning(parsers.SkippedUnsignedStats)
|
||||
stats.DisplayAsWarning(templates.UnsignedCodeWarning)
|
||||
stats.ForceDisplayWarning(templates.SkippedUnsignedStats)
|
||||
|
||||
cfg := config.DefaultConfig
|
||||
|
||||
@@ -671,8 +677,8 @@ func (r *Runner) displayExecutionInfo(store *loader.Store) {
|
||||
value := v.Load()
|
||||
if k == templates.Unsigned && value > 0 {
|
||||
// adjust skipped unsigned templates via code or -dut flag
|
||||
value = value - uint64(stats.GetValue(parsers.SkippedUnsignedStats))
|
||||
value = value - uint64(stats.GetValue(parsers.CodeFlagWarningStats))
|
||||
value = value - uint64(stats.GetValue(templates.SkippedUnsignedStats))
|
||||
value = value - uint64(stats.GetValue(templates.CodeFlagWarningStats))
|
||||
}
|
||||
if value > 0 {
|
||||
if k != templates.Unsigned {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
@@ -15,15 +15,15 @@ func TestCreateReportingOptions(t *testing.T) {
|
||||
options.ReportingConfig = "../../integration_tests/test-issue-tracker-config1.yaml"
|
||||
resultOptions, err := createReportingOptions(&options)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, resultOptions.AllowList.Severities, severity.Severities{severity.High, severity.Critical})
|
||||
assert.Equal(t, resultOptions.DenyList.Severities, severity.Severities{severity.Low})
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, resultOptions.AllowList.Severities, severity.Severities{severity.High, severity.Critical})
|
||||
require.Equal(t, resultOptions.DenyList.Severities, severity.Severities{severity.Low})
|
||||
|
||||
options.ReportingConfig = "../../integration_tests/test-issue-tracker-config2.yaml"
|
||||
resultOptions2, err := createReportingOptions(&options)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, resultOptions2.AllowList.Severities, resultOptions.AllowList.Severities)
|
||||
assert.Equal(t, resultOptions2.DenyList.Severities, resultOptions.DenyList.Severities)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, resultOptions2.AllowList.Severities, resultOptions.AllowList.Severities)
|
||||
require.Equal(t, resultOptions2.DenyList.Severities, resultOptions.DenyList.Severities)
|
||||
}
|
||||
|
||||
type TestStruct1 struct {
|
||||
@@ -69,8 +69,8 @@ func TestWalkReflectStructAssignsEnvVars(t *testing.T) {
|
||||
|
||||
Walk(testStruct, expandEndVars)
|
||||
|
||||
assert.Equal(t, "value", testStruct.A)
|
||||
assert.Equal(t, "value2", testStruct.Struct.B)
|
||||
require.Equal(t, "value", testStruct.A)
|
||||
require.Equal(t, "value2", testStruct.Struct.B)
|
||||
}
|
||||
|
||||
func TestWalkReflectStructHandlesDifferentTypes(t *testing.T) {
|
||||
@@ -85,9 +85,9 @@ func TestWalkReflectStructHandlesDifferentTypes(t *testing.T) {
|
||||
|
||||
Walk(testStruct, expandEndVars)
|
||||
|
||||
assert.Equal(t, "value", testStruct.A)
|
||||
assert.Equal(t, "2", testStruct.B)
|
||||
assert.Equal(t, "true", testStruct.C)
|
||||
require.Equal(t, "value", testStruct.A)
|
||||
require.Equal(t, "2", testStruct.B)
|
||||
require.Equal(t, "true", testStruct.C)
|
||||
}
|
||||
|
||||
func TestWalkReflectStructEmpty(t *testing.T) {
|
||||
@@ -102,9 +102,9 @@ func TestWalkReflectStructEmpty(t *testing.T) {
|
||||
|
||||
Walk(testStruct, expandEndVars)
|
||||
|
||||
assert.Equal(t, "value", testStruct.A)
|
||||
assert.Equal(t, "", testStruct.B)
|
||||
assert.Equal(t, "true", testStruct.C)
|
||||
require.Equal(t, "value", testStruct.A)
|
||||
require.Equal(t, "", testStruct.B)
|
||||
require.Equal(t, "true", testStruct.C)
|
||||
}
|
||||
|
||||
func TestWalkReflectStructWithNoYamlTag(t *testing.T) {
|
||||
@@ -119,9 +119,9 @@ func TestWalkReflectStructWithNoYamlTag(t *testing.T) {
|
||||
os.Setenv("GITHUB_USER", "testuser")
|
||||
|
||||
Walk(test, expandEndVars)
|
||||
assert.Equal(t, "testuser", test.A)
|
||||
assert.Equal(t, "testuser", test.B.B, test.B)
|
||||
assert.Equal(t, "$GITHUB_USER", test.C)
|
||||
require.Equal(t, "testuser", test.A)
|
||||
require.Equal(t, "testuser", test.B.B, test.B)
|
||||
require.Equal(t, "$GITHUB_USER", test.C)
|
||||
}
|
||||
|
||||
func TestWalkReflectStructHandlesNestedStructs(t *testing.T) {
|
||||
@@ -138,7 +138,7 @@ func TestWalkReflectStructHandlesNestedStructs(t *testing.T) {
|
||||
|
||||
Walk(testStruct, expandEndVars)
|
||||
|
||||
assert.Equal(t, "value", testStruct.A)
|
||||
assert.Equal(t, "2", testStruct.Struct.B)
|
||||
assert.Equal(t, "true", testStruct.Struct.C)
|
||||
require.Equal(t, "value", testStruct.A)
|
||||
require.Equal(t, "2", testStruct.Struct.B)
|
||||
require.Equal(t, "true", testStruct.Struct.C)
|
||||
}
|
||||
@@ -11,18 +11,21 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/parsers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
)
|
||||
|
||||
// log available templates for verbose (-vv)
|
||||
func (r *Runner) logAvailableTemplate(tplPath string) {
|
||||
t, err := parsers.ParseTemplate(tplPath, r.catalog)
|
||||
t, err := r.parser.ParseTemplate(tplPath, r.catalog)
|
||||
tpl, ok := t.(*templates.Template)
|
||||
if !ok {
|
||||
panic("not a template")
|
||||
}
|
||||
if err != nil {
|
||||
gologger.Error().Msgf("Could not parse file '%s': %s\n", tplPath, err)
|
||||
} else {
|
||||
r.verboseTemplate(t)
|
||||
r.verboseTemplate(tpl)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-4
@@ -5,12 +5,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/core"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/input/provider"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/loader/workflow"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/parsers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/ratelimit"
|
||||
@@ -40,6 +39,7 @@ func createEphemeralObjects(base *NucleiEngine, opts *types.Options) (*unsafeOpt
|
||||
HostErrorsCache: base.hostErrCache,
|
||||
Colorizer: aurora.NewAurora(true),
|
||||
ResumeCfg: types.NewResumeCfg(),
|
||||
Parser: base.parser,
|
||||
}
|
||||
if opts.RateLimitMinute > 0 {
|
||||
u.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(opts.RateLimitMinute), time.Minute)
|
||||
@@ -88,7 +88,6 @@ func (e *ThreadSafeNucleiEngine) GlobalLoadAllTemplates() error {
|
||||
// GlobalResultCallback sets a callback function which will be called for each result
|
||||
func (e *ThreadSafeNucleiEngine) GlobalResultCallback(callback func(event *output.ResultEvent)) {
|
||||
e.eng.resultCallbacks = []func(*output.ResultEvent){callback}
|
||||
config.DefaultConfig.PurgeGlobalCache()
|
||||
}
|
||||
|
||||
// ExecuteWithCallback executes templates on targets and calls callback on each result(only if results are found)
|
||||
@@ -110,7 +109,7 @@ func (e *ThreadSafeNucleiEngine) ExecuteNucleiWithOpts(targets []string, opts ..
|
||||
}
|
||||
|
||||
// load templates
|
||||
workflowLoader, err := parsers.NewLoader(&unsafeOpts.executerOpts)
|
||||
workflowLoader, err := workflow.NewLoader(&unsafeOpts.executerOpts)
|
||||
if err != nil {
|
||||
return errorutil.New("Could not create workflow loader: %s\n", err)
|
||||
}
|
||||
|
||||
+3
-2
@@ -11,8 +11,8 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/core"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/input/provider"
|
||||
providerTypes "github.com/projectdiscovery/nuclei/v3/pkg/input/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/loader/workflow"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/parsers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/hosterrorscache"
|
||||
@@ -72,6 +72,7 @@ type NucleiEngine struct {
|
||||
mode engineMode
|
||||
browserInstance *engine.Browser
|
||||
httpClient *retryablehttp.Client
|
||||
parser *templates.Parser
|
||||
authprovider authprovider.AuthProvider
|
||||
|
||||
// unexported meta options
|
||||
@@ -86,7 +87,7 @@ type NucleiEngine struct {
|
||||
|
||||
// LoadAllTemplates loads all nuclei template based on given options
|
||||
func (e *NucleiEngine) LoadAllTemplates() error {
|
||||
workflowLoader, err := parsers.NewLoader(&e.executerOpts)
|
||||
workflowLoader, err := workflow.NewLoader(&e.executerOpts)
|
||||
if err != nil {
|
||||
return errorutil.New("Could not create workflow loader: %s\n", err)
|
||||
}
|
||||
|
||||
+12
-6
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httpclientpool"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
nucleiUtils "github.com/projectdiscovery/nuclei/v3/pkg/utils"
|
||||
@@ -113,6 +114,8 @@ func (e *NucleiEngine) init() error {
|
||||
e.httpClient = httpclient
|
||||
}
|
||||
|
||||
e.parser = templates.NewParser()
|
||||
|
||||
_ = protocolstate.Init(e.opts)
|
||||
_ = protocolinit.Init(e.opts)
|
||||
e.applyRequiredDefaults()
|
||||
@@ -157,6 +160,7 @@ func (e *NucleiEngine) init() error {
|
||||
Colorizer: aurora.NewAurora(true),
|
||||
ResumeCfg: types.NewResumeCfg(),
|
||||
Browser: e.browserInstance,
|
||||
Parser: e.parser,
|
||||
}
|
||||
if len(e.opts.SecretsFile) > 0 {
|
||||
authTmplStore, err := runner.GetAuthTmplStore(*e.opts, e.catalog, e.executerOpts)
|
||||
@@ -186,12 +190,14 @@ func (e *NucleiEngine) init() error {
|
||||
}
|
||||
}
|
||||
|
||||
if e.opts.RateLimitMinute > 0 {
|
||||
e.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(e.opts.RateLimitMinute), time.Minute)
|
||||
} else if e.opts.RateLimit > 0 {
|
||||
e.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(e.opts.RateLimit), time.Second)
|
||||
} else {
|
||||
e.executerOpts.RateLimiter = ratelimit.NewUnlimited(context.Background())
|
||||
if e.executerOpts.RateLimiter == nil {
|
||||
if e.opts.RateLimitMinute > 0 {
|
||||
e.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(e.opts.RateLimitMinute), time.Minute)
|
||||
} else if e.opts.RateLimit > 0 {
|
||||
e.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(e.opts.RateLimit), time.Second)
|
||||
} else {
|
||||
e.executerOpts.RateLimiter = ratelimit.NewUnlimited(context.Background())
|
||||
}
|
||||
}
|
||||
|
||||
e.engine = core.New(e.opts)
|
||||
|
||||
File renamed without changes.
@@ -31,7 +31,7 @@ const (
|
||||
CLIConfigFileName = "config.yaml"
|
||||
ReportingConfigFilename = "reporting-config.yaml"
|
||||
// Version is the current version of nuclei
|
||||
Version = `v3.2.0`
|
||||
Version = `v3.2.2`
|
||||
// Directory Names of custom templates
|
||||
CustomS3TemplatesDirName = "s3"
|
||||
CustomGitHubTemplatesDirName = "github"
|
||||
|
||||
@@ -45,9 +45,6 @@ type Config struct {
|
||||
LatestNucleiTemplatesVersion string `json:"nuclei-templates-latest-version"`
|
||||
LatestNucleiIgnoreHash string `json:"nuclei-latest-ignore-hash,omitempty"`
|
||||
|
||||
// Other AppLevel/Global Settings
|
||||
registerdCaches []GlobalCache `json:"-"` // registered global caches
|
||||
|
||||
// internal / unexported fields
|
||||
disableUpdates bool `json:"-"` // disable updates both version check and template updates
|
||||
homeDir string `json:"-"` // User Home Directory
|
||||
@@ -301,19 +298,6 @@ func (c *Config) WriteTemplatesIndex(index map[string]string) error {
|
||||
return os.WriteFile(indexFile, buff.Bytes(), 0600)
|
||||
}
|
||||
|
||||
// RegisterGlobalCache registers a global cache at app level
|
||||
// and is available to be purged on demand
|
||||
func (c *Config) RegisterGlobalCache(cache GlobalCache) {
|
||||
c.registerdCaches = append(c.registerdCaches, cache)
|
||||
}
|
||||
|
||||
// PurgeGlobalCache purges all registered global caches
|
||||
func (c *Config) PurgeGlobalCache() {
|
||||
for _, cache := range c.registerdCaches {
|
||||
cache.Purge()
|
||||
}
|
||||
}
|
||||
|
||||
// getTemplatesConfigFilePath returns configDir/.templates-config.json file path
|
||||
func (c *Config) getTemplatesConfigFilePath() string {
|
||||
return filepath.Join(c.configDir, TemplateConfigFileName)
|
||||
|
||||
@@ -13,13 +13,6 @@ import (
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
)
|
||||
|
||||
// GlobalCache are global cache that have global
|
||||
// scope and are not purged but can be purged
|
||||
// via config.DefaultConfig
|
||||
type GlobalCache interface {
|
||||
Purge()
|
||||
}
|
||||
|
||||
var knownConfigFiles = []string{"cves.json", "contributors.json", "TEMPLATES-STATS.json"}
|
||||
|
||||
// TemplateFormat
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
cfg "github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader/filter"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/parsers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
@@ -67,7 +67,7 @@ type Config struct {
|
||||
|
||||
// Store is a storage for loaded nuclei templates
|
||||
type Store struct {
|
||||
tagFilter *filter.TagFilter
|
||||
tagFilter *templates.TagFilter
|
||||
pathFilter *filter.PathFilter
|
||||
config *Config
|
||||
finalTemplates []string
|
||||
@@ -113,7 +113,7 @@ func NewConfig(options *types.Options, catalog catalog.Catalog, executerOpts pro
|
||||
|
||||
// New creates a new template store based on provided configuration
|
||||
func New(cfg *Config) (*Store, error) {
|
||||
tagFilter, err := filter.New(&filter.Config{
|
||||
tagFilter, err := templates.NewTagFilter(&templates.TagFilterConfig{
|
||||
Tags: cfg.Tags,
|
||||
ExcludeTags: cfg.ExcludeTags,
|
||||
Authors: cfg.Authors,
|
||||
@@ -269,25 +269,27 @@ func (store *Store) ValidateTemplates() error {
|
||||
filteredTemplatePaths := store.pathFilter.Match(templatePaths)
|
||||
filteredWorkflowPaths := store.pathFilter.Match(workflowPaths)
|
||||
|
||||
if areTemplatesValid(store, filteredTemplatePaths) && areWorkflowsValid(store, filteredWorkflowPaths) {
|
||||
if store.areTemplatesValid(filteredTemplatePaths) && store.areWorkflowsValid(filteredWorkflowPaths) {
|
||||
return nil
|
||||
}
|
||||
return errors.New("errors occurred during template validation")
|
||||
}
|
||||
|
||||
func areWorkflowsValid(store *Store, filteredWorkflowPaths map[string]struct{}) bool {
|
||||
return areWorkflowOrTemplatesValid(store, filteredWorkflowPaths, true, func(templatePath string, tagFilter *filter.TagFilter) (bool, error) {
|
||||
return parsers.LoadWorkflow(templatePath, store.config.Catalog)
|
||||
func (store *Store) areWorkflowsValid(filteredWorkflowPaths map[string]struct{}) bool {
|
||||
return store.areWorkflowOrTemplatesValid(filteredWorkflowPaths, true, func(templatePath string, tagFilter *templates.TagFilter) (bool, error) {
|
||||
return false, nil
|
||||
// return store.config.ExecutorOptions.Parser.LoadWorkflow(templatePath, store.config.Catalog)
|
||||
})
|
||||
}
|
||||
|
||||
func areTemplatesValid(store *Store, filteredTemplatePaths map[string]struct{}) bool {
|
||||
return areWorkflowOrTemplatesValid(store, filteredTemplatePaths, false, func(templatePath string, tagFilter *filter.TagFilter) (bool, error) {
|
||||
return parsers.LoadTemplate(templatePath, store.tagFilter, nil, store.config.Catalog)
|
||||
func (store *Store) areTemplatesValid(filteredTemplatePaths map[string]struct{}) bool {
|
||||
return store.areWorkflowOrTemplatesValid(filteredTemplatePaths, false, func(templatePath string, tagFilter *templates.TagFilter) (bool, error) {
|
||||
return false, nil
|
||||
// return store.config.ExecutorOptions.Parser.LoadTemplate(templatePath, store.tagFilter, nil, store.config.Catalog)
|
||||
})
|
||||
}
|
||||
|
||||
func areWorkflowOrTemplatesValid(store *Store, filteredTemplatePaths map[string]struct{}, isWorkflow bool, load func(templatePath string, tagFilter *filter.TagFilter) (bool, error)) bool {
|
||||
func (store *Store) areWorkflowOrTemplatesValid(filteredTemplatePaths map[string]struct{}, isWorkflow bool, load func(templatePath string, tagFilter *templates.TagFilter) (bool, error)) bool {
|
||||
areTemplatesValid := true
|
||||
|
||||
for templatePath := range filteredTemplatePaths {
|
||||
@@ -340,7 +342,7 @@ func areWorkflowTemplatesValid(store *Store, workflows []*workflows.WorkflowTemp
|
||||
}
|
||||
|
||||
func isParsingError(message string, template string, err error) bool {
|
||||
if errors.Is(err, filter.ErrExcluded) {
|
||||
if errors.Is(err, templates.ErrExcluded) {
|
||||
return false
|
||||
}
|
||||
if errors.Is(err, templates.ErrCreateTemplateExecutor) {
|
||||
@@ -363,7 +365,7 @@ func (store *Store) LoadWorkflows(workflowsList []string) []*templates.Template
|
||||
|
||||
loadedWorkflows := make([]*templates.Template, 0, len(workflowPathMap))
|
||||
for workflowPath := range workflowPathMap {
|
||||
loaded, err := parsers.LoadWorkflow(workflowPath, store.config.Catalog)
|
||||
loaded, err := store.config.ExecutorOptions.Parser.LoadWorkflow(workflowPath, store.config.Catalog)
|
||||
if err != nil {
|
||||
gologger.Warning().Msgf("Could not load workflow %s: %s\n", workflowPath, err)
|
||||
}
|
||||
@@ -388,43 +390,43 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
|
||||
|
||||
loadedTemplates := make([]*templates.Template, 0, len(templatePathMap))
|
||||
for templatePath := range templatePathMap {
|
||||
loaded, err := parsers.LoadTemplate(templatePath, store.tagFilter, tags, store.config.Catalog)
|
||||
loaded, err := store.config.ExecutorOptions.Parser.LoadTemplate(templatePath, store.tagFilter, tags, store.config.Catalog)
|
||||
if loaded || store.pathFilter.MatchIncluded(templatePath) {
|
||||
parsed, err := templates.Parse(templatePath, store.preprocessor, store.config.ExecutorOptions)
|
||||
if err != nil {
|
||||
// exclude templates not compatible with offline matching from total runtime warning stats
|
||||
if !errors.Is(err, templates.ErrIncompatibleWithOfflineMatching) {
|
||||
stats.Increment(parsers.RuntimeWarningsStats)
|
||||
stats.Increment(templates.RuntimeWarningsStats)
|
||||
}
|
||||
gologger.Warning().Msgf("Could not parse template %s: %s\n", templatePath, err)
|
||||
} else if parsed != nil {
|
||||
if !parsed.Verified && store.config.ExecutorOptions.Options.DisableUnsignedTemplates {
|
||||
// skip unverified templates when prompted to
|
||||
stats.Increment(parsers.SkippedUnsignedStats)
|
||||
stats.Increment(templates.SkippedUnsignedStats)
|
||||
continue
|
||||
}
|
||||
if len(parsed.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless {
|
||||
// donot include headless template in final list if headless flag is not set
|
||||
stats.Increment(parsers.HeadlessFlagWarningStats)
|
||||
stats.Increment(templates.HeadlessFlagWarningStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] Headless flag is required for headless template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||
}
|
||||
} else if len(parsed.RequestsCode) > 0 && !store.config.ExecutorOptions.Options.EnableCodeTemplates {
|
||||
// donot include 'Code' protocol custom template in final list if code flag is not set
|
||||
stats.Increment(parsers.CodeFlagWarningStats)
|
||||
stats.Increment(templates.CodeFlagWarningStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] Code flag is required for code protocol template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||
}
|
||||
} else if len(parsed.RequestsCode) > 0 && !parsed.Verified && len(parsed.Workflows) == 0 {
|
||||
// donot include unverified 'Code' protocol custom template in final list
|
||||
stats.Increment(parsers.UnsignedCodeWarning)
|
||||
stats.Increment(templates.UnsignedCodeWarning)
|
||||
// these will be skipped so increment skip counter
|
||||
stats.Increment(parsers.SkippedUnsignedStats)
|
||||
stats.Increment(templates.SkippedUnsignedStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] Tampered/Unsigned template at %v.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||
}
|
||||
} else if parsed.IsFuzzing() && !store.config.ExecutorOptions.Options.FuzzTemplates {
|
||||
stats.Increment(parsers.FuzzFlagWarningStats)
|
||||
stats.Increment(templates.FuzzFlagWarningStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] Fuzz flag is required for fuzzing template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||
}
|
||||
@@ -436,9 +438,9 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), filter.ErrExcluded.Error()) {
|
||||
stats.Increment(parsers.TemplatesExecutedStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
if strings.Contains(err.Error(), templates.ErrExcluded.Error()) {
|
||||
stats.Increment(templates.TemplatesExecutedStats)
|
||||
if cfg.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] %v\n", aurora.Yellow("WRN").String(), err.Error())
|
||||
}
|
||||
continue
|
||||
|
||||
+1
-1
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
"github.com/google/go-github/github"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
@@ -15,7 +16,6 @@ import (
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
folderutil "github.com/projectdiscovery/utils/folder"
|
||||
"golang.org/x/oauth2"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
|
||||
)
|
||||
|
||||
var _ Provider = &customTemplateGitHubRepo{}
|
||||
|
||||
+18
-6
@@ -138,8 +138,7 @@ func executeWithPoolingProgram(p *goja.Program, args *ExecuteArgs, opts *Execute
|
||||
return goja.Null()
|
||||
}
|
||||
for _, arg := range call.Arguments {
|
||||
value := arg.Export()
|
||||
if out := stringify(value); out != "" {
|
||||
if out := stringify(arg, runtime); out != "" {
|
||||
buff.WriteString(out)
|
||||
}
|
||||
}
|
||||
@@ -158,8 +157,8 @@ func executeWithPoolingProgram(p *goja.Program, args *ExecuteArgs, opts *Execute
|
||||
panic(runtime.ToValue("ExportAs expects 2 arguments"))
|
||||
}
|
||||
key := call.Argument(0).String()
|
||||
value := call.Argument(1).Export()
|
||||
opts.exports[key] = stringify(value)
|
||||
value := call.Argument(1)
|
||||
opts.exports[key] = stringify(value, runtime)
|
||||
return goja.Null()
|
||||
},
|
||||
})
|
||||
@@ -169,7 +168,7 @@ func executeWithPoolingProgram(p *goja.Program, args *ExecuteArgs, opts *Execute
|
||||
}
|
||||
if val.Export() != nil {
|
||||
// append last value to output
|
||||
buff.WriteString(stringify(val.Export()))
|
||||
buff.WriteString(stringify(val, runtime))
|
||||
}
|
||||
// and return it as result
|
||||
return runtime.ToValue(buff.String()), nil
|
||||
@@ -201,13 +200,26 @@ func createNewRuntime() *goja.Runtime {
|
||||
|
||||
// stringify converts a given value to string
|
||||
// if its a struct it will be marshalled to json
|
||||
func stringify(value interface{}) string {
|
||||
func stringify(gojaValue goja.Value, runtime *goja.Runtime) string {
|
||||
value := gojaValue.Export()
|
||||
if value == nil {
|
||||
return ""
|
||||
}
|
||||
kind := reflect.TypeOf(value).Kind()
|
||||
if kind == reflect.Struct || kind == reflect.Ptr && reflect.ValueOf(value).Elem().Kind() == reflect.Struct {
|
||||
// in this case we must use JSON.stringify to convert to string
|
||||
// because json.Marshal() utilizes json tags when marshalling
|
||||
// but goja has custom implementation of json.Marshal() which does not
|
||||
// since we have been using `to_json` in all our examples we must stick to it
|
||||
// marshal structs or struct pointers to json automatically
|
||||
jsonStringify, ok := goja.AssertFunction(runtime.Get("to_json"))
|
||||
if ok {
|
||||
result, err := jsonStringify(goja.Undefined(), gojaValue)
|
||||
if err == nil {
|
||||
return result.String()
|
||||
}
|
||||
}
|
||||
// unlikely but if to_json throwed some error use native json.Marshal
|
||||
val := value
|
||||
if kind == reflect.Ptr {
|
||||
val = reflect.ValueOf(value).Elem().Interface()
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
|
||||
)
|
||||
|
||||
type Parser interface {
|
||||
LoadTemplate(templatePath string, tagFilter any, extraTags []string, catalog catalog.Catalog) (bool, error)
|
||||
ParseTemplate(templatePath string, catalog catalog.Catalog) (any, error)
|
||||
LoadWorkflow(templatePath string, catalog catalog.Catalog) (bool, error)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package parsers
|
||||
package workflow
|
||||
|
||||
import (
|
||||
"github.com/projectdiscovery/gologger"
|
||||
@@ -6,17 +6,18 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader/filter"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
)
|
||||
|
||||
type workflowLoader struct {
|
||||
pathFilter *filter.PathFilter
|
||||
tagFilter *filter.TagFilter
|
||||
tagFilter *templates.TagFilter
|
||||
options *protocols.ExecutorOptions
|
||||
}
|
||||
|
||||
// NewLoader returns a new workflow loader structure
|
||||
func NewLoader(options *protocols.ExecutorOptions) (model.WorkflowLoader, error) {
|
||||
tagFilter, err := filter.New(&filter.Config{
|
||||
tagFilter, err := templates.NewTagFilter(&templates.TagFilterConfig{
|
||||
Authors: options.Options.Authors,
|
||||
Tags: options.Options.Tags,
|
||||
ExcludeTags: options.Options.ExcludeTags,
|
||||
@@ -50,7 +51,7 @@ func (w *workflowLoader) GetTemplatePathsByTags(templateTags []string) []string
|
||||
|
||||
loadedTemplates := make([]string, 0, len(templatePathMap))
|
||||
for templatePath := range templatePathMap {
|
||||
loaded, _ := LoadTemplate(templatePath, w.tagFilter, templateTags, w.options.Catalog)
|
||||
loaded, _ := w.options.Parser.LoadTemplate(templatePath, w.tagFilter, templateTags, w.options.Catalog)
|
||||
if loaded {
|
||||
loadedTemplates = append(loadedTemplates, templatePath)
|
||||
}
|
||||
@@ -67,7 +68,7 @@ func (w *workflowLoader) GetTemplatePaths(templatesList []string, noValidate boo
|
||||
|
||||
loadedTemplates := make([]string, 0, len(templatesPathMap))
|
||||
for templatePath := range templatesPathMap {
|
||||
matched, err := LoadTemplate(templatePath, w.tagFilter, nil, w.options.Catalog)
|
||||
matched, err := w.options.Parser.LoadTemplate(templatePath, w.tagFilter, nil, w.options.Catalog)
|
||||
if err != nil && !matched {
|
||||
gologger.Warning().Msg(err.Error())
|
||||
} else if matched || noValidate {
|
||||
+13
-13
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/stringslice"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
@@ -29,10 +29,10 @@ func TestInfoJsonMarshal(t *testing.T) {
|
||||
}
|
||||
|
||||
result, err := json.Marshal(&info)
|
||||
assert.Nil(t, err)
|
||||
require.Nil(t, err)
|
||||
|
||||
expected := `{"name":"Test Template Name","author":["forgedhallpass","ice3man"],"tags":["cve","misc"],"description":"Test description","reference":"Reference1","severity":"high","metadata":{"array_key":["array_value1","array_value2"],"map_key":{"key1":"val1"},"string_key":"string_value"}}`
|
||||
assert.Equal(t, expected, string(result))
|
||||
require.Equal(t, expected, string(result))
|
||||
}
|
||||
|
||||
func TestInfoYamlMarshal(t *testing.T) {
|
||||
@@ -53,7 +53,7 @@ func TestInfoYamlMarshal(t *testing.T) {
|
||||
}
|
||||
|
||||
result, err := yaml.Marshal(&info)
|
||||
assert.Nil(t, err)
|
||||
require.Nil(t, err)
|
||||
|
||||
expected := `name: Test Template Name
|
||||
author:
|
||||
@@ -73,7 +73,7 @@ metadata:
|
||||
key1: val1
|
||||
string_key: string_value
|
||||
`
|
||||
assert.Equal(t, expected, string(result))
|
||||
require.Equal(t, expected, string(result))
|
||||
}
|
||||
|
||||
func TestUnmarshal(t *testing.T) {
|
||||
@@ -94,13 +94,13 @@ func TestUnmarshal(t *testing.T) {
|
||||
t.Helper()
|
||||
info := Info{}
|
||||
err := yaml.Unmarshal([]byte(yamlPayload), &info)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, info.Name, templateName)
|
||||
assert.Equal(t, info.Authors.ToSlice(), authors)
|
||||
assert.Equal(t, info.Tags.ToSlice(), tags)
|
||||
assert.Equal(t, info.SeverityHolder.Severity, severity.Critical)
|
||||
assert.Equal(t, info.Reference.ToSlice(), references)
|
||||
assert.Equal(t, info.Metadata, dynamicKeysMap)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, info.Name, templateName)
|
||||
require.Equal(t, info.Authors.ToSlice(), authors)
|
||||
require.Equal(t, info.Tags.ToSlice(), tags)
|
||||
require.Equal(t, info.SeverityHolder.Severity, severity.Critical)
|
||||
require.Equal(t, info.Reference.ToSlice(), references)
|
||||
require.Equal(t, info.Metadata, dynamicKeysMap)
|
||||
return info
|
||||
}
|
||||
|
||||
@@ -133,5 +133,5 @@ func TestUnmarshal(t *testing.T) {
|
||||
|
||||
info1 := assertUnmarshalledTemplateInfo(t, yamlPayload1)
|
||||
info2 := assertUnmarshalledTemplateInfo(t, yamlPayload2)
|
||||
assert.Equal(t, info1, info2)
|
||||
require.Equal(t, info1, info2)
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -17,8 +16,8 @@ func TestYamlMarshal(t *testing.T) {
|
||||
severity := Holder{Severity: High}
|
||||
|
||||
marshalled, err := severity.MarshalYAML()
|
||||
assert.Nil(t, err, "could not marshal yaml")
|
||||
assert.Equal(t, "high", marshalled, "could not marshal severity correctly")
|
||||
require.Nil(t, err, "could not marshal yaml")
|
||||
require.Equal(t, "high", marshalled, "could not marshal severity correctly")
|
||||
}
|
||||
|
||||
func TestYamlUnmarshalFail(t *testing.T) {
|
||||
@@ -27,7 +26,7 @@ func TestYamlUnmarshalFail(t *testing.T) {
|
||||
|
||||
func TestGetSupportedSeverities(t *testing.T) {
|
||||
severities := GetSupportedSeverities()
|
||||
assert.Equal(t, severities, Severities{Info, Low, Medium, High, Critical, Unknown})
|
||||
require.Equal(t, severities, Severities{Info, Low, Medium, High, Critical, Unknown})
|
||||
}
|
||||
|
||||
func testUnmarshal(t *testing.T, unmarshaller func(data []byte, v interface{}) error, payloadCreator func(value string) string) {
|
||||
@@ -43,15 +42,15 @@ func testUnmarshal(t *testing.T, unmarshaller func(data []byte, v interface{}) e
|
||||
for _, payload := range payloads { // nolint:scopelint // false-positive
|
||||
t.Run(payload, func(t *testing.T) {
|
||||
result := unmarshal(payload, unmarshaller)
|
||||
assert.Equal(t, result.Severity, Info)
|
||||
assert.Equal(t, result.Severity.String(), "info")
|
||||
require.Equal(t, result.Severity, Info)
|
||||
require.Equal(t, result.Severity.String(), "info")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testUnmarshalFail(t *testing.T, unmarshaller func(data []byte, v interface{}) error, payloadCreator func(value string) string) {
|
||||
t.Helper()
|
||||
assert.Panics(t, func() { unmarshal(payloadCreator("invalid"), unmarshaller) })
|
||||
require.Panics(t, func() { unmarshal(payloadCreator("invalid"), unmarshaller) })
|
||||
}
|
||||
|
||||
func unmarshal(value string, unmarshaller func(data []byte, v interface{}) error) Holder {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/Knetic/govaluate"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -41,7 +40,7 @@ func testDslExpressionScenarios(t *testing.T, dslExpressions map[string]interfac
|
||||
actualResult := evaluateExpression(t, dslExpression)
|
||||
|
||||
if expectedResult != nil {
|
||||
assert.Equal(t, expectedResult, actualResult)
|
||||
require.Equal(t, expectedResult, actualResult)
|
||||
}
|
||||
|
||||
fmt.Printf("%s: \t %v\n", dslExpression, actualResult)
|
||||
|
||||
@@ -64,6 +64,22 @@ func (operators *Operators) Compile() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (operators *Operators) HasDSL() bool {
|
||||
for _, matcher := range operators.Matchers {
|
||||
if len(matcher.DSL) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, extractor := range operators.Extractors {
|
||||
if len(extractor.DSL) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// GetMatchersCondition returns the condition for the matchers
|
||||
func (operators *Operators) GetMatchersCondition() matchers.ConditionType {
|
||||
return operators.matchersCondition
|
||||
|
||||
@@ -73,6 +73,10 @@ var decolorizerRegex = regexp.MustCompile(`\x1B\[[0-9;]*[a-zA-Z]`)
|
||||
// InternalEvent is an internal output generation structure for nuclei.
|
||||
type InternalEvent map[string]interface{}
|
||||
|
||||
func (ie InternalEvent) Set(k string, v interface{}) {
|
||||
ie[k] = v
|
||||
}
|
||||
|
||||
// InternalWrappedEvent is a wrapped event with operators result added to it.
|
||||
type InternalWrappedEvent struct {
|
||||
// Mutex is internal field which is implicitly used
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
package parsers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader/filter"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/cache"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/stats"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
errMandatoryFieldMissingFmt = "mandatory '%s' field is missing"
|
||||
errInvalidFieldFmt = "invalid field format for '%s' (allowed format is %s)"
|
||||
warningFieldMissingFmt = "field '%s' is missing"
|
||||
CouldNotLoadTemplate = "Could not load template %s: %s"
|
||||
LoadedWithWarnings = "Loaded template %s: with syntax warning : %s"
|
||||
)
|
||||
|
||||
// LoadTemplate returns true if the template is valid and matches the filtering criteria.
|
||||
func LoadTemplate(templatePath string, tagFilter *filter.TagFilter, extraTags []string, catalog catalog.Catalog) (bool, error) {
|
||||
template, templateParseError := ParseTemplate(templatePath, catalog)
|
||||
if templateParseError != nil {
|
||||
return false, fmt.Errorf(CouldNotLoadTemplate, templatePath, templateParseError)
|
||||
}
|
||||
|
||||
if len(template.Workflows) > 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
validationError := validateTemplateMandatoryFields(template)
|
||||
if validationError != nil {
|
||||
stats.Increment(SyntaxErrorStats)
|
||||
return false, fmt.Errorf(CouldNotLoadTemplate, templatePath, validationError)
|
||||
}
|
||||
|
||||
ret, err := isTemplateInfoMetadataMatch(tagFilter, template, extraTags)
|
||||
if err != nil {
|
||||
return ret, fmt.Errorf(CouldNotLoadTemplate, templatePath, err)
|
||||
}
|
||||
// if template loaded then check the template for optional fields to add warnings
|
||||
if ret {
|
||||
validationWarning := validateTemplateOptionalFields(template)
|
||||
if validationWarning != nil {
|
||||
stats.Increment(SyntaxWarningStats)
|
||||
return ret, fmt.Errorf(LoadedWithWarnings, templatePath, validationWarning)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// LoadWorkflow returns true if the workflow is valid and matches the filtering criteria.
|
||||
func LoadWorkflow(templatePath string, catalog catalog.Catalog) (bool, error) {
|
||||
template, templateParseError := ParseTemplate(templatePath, catalog)
|
||||
if templateParseError != nil {
|
||||
return false, templateParseError
|
||||
}
|
||||
|
||||
if len(template.Workflows) > 0 {
|
||||
if validationError := validateTemplateMandatoryFields(template); validationError != nil {
|
||||
stats.Increment(SyntaxErrorStats)
|
||||
return false, validationError
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func isTemplateInfoMetadataMatch(tagFilter *filter.TagFilter, template *templates.Template, extraTags []string) (bool, error) {
|
||||
match, err := tagFilter.Match(template, extraTags)
|
||||
|
||||
if err == filter.ErrExcluded {
|
||||
return false, filter.ErrExcluded
|
||||
}
|
||||
|
||||
return match, err
|
||||
}
|
||||
|
||||
// validateTemplateMandatoryFields validates the mandatory fields of a template
|
||||
// return error from this function will cause hard fail and not proceed further
|
||||
func validateTemplateMandatoryFields(template *templates.Template) error {
|
||||
info := template.Info
|
||||
|
||||
var errors []string
|
||||
|
||||
if utils.IsBlank(info.Name) {
|
||||
errors = append(errors, fmt.Sprintf(errMandatoryFieldMissingFmt, "name"))
|
||||
}
|
||||
|
||||
if info.Authors.IsEmpty() {
|
||||
errors = append(errors, fmt.Sprintf(errMandatoryFieldMissingFmt, "author"))
|
||||
}
|
||||
|
||||
if template.ID == "" {
|
||||
errors = append(errors, fmt.Sprintf(errMandatoryFieldMissingFmt, "id"))
|
||||
} else if !templateIDRegexp.MatchString(template.ID) {
|
||||
errors = append(errors, fmt.Sprintf(errInvalidFieldFmt, "id", templateIDRegexp.String()))
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return fmt.Errorf(strings.Join(errors, ", "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateTemplateOptionalFields validates the optional fields of a template
|
||||
// return error from this function will throw a warning and proceed further
|
||||
func validateTemplateOptionalFields(template *templates.Template) error {
|
||||
info := template.Info
|
||||
|
||||
var warnings []string
|
||||
|
||||
if template.Type() != types.WorkflowProtocol && utils.IsBlank(info.SeverityHolder.Severity.String()) {
|
||||
warnings = append(warnings, fmt.Sprintf(warningFieldMissingFmt, "severity"))
|
||||
}
|
||||
|
||||
if len(warnings) > 0 {
|
||||
return fmt.Errorf(strings.Join(warnings, ", "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
parsedTemplatesCache *cache.Templates
|
||||
ShouldValidate bool
|
||||
NoStrictSyntax bool
|
||||
templateIDRegexp = regexp.MustCompile(`^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$`)
|
||||
)
|
||||
|
||||
const (
|
||||
SyntaxWarningStats = "syntax-warnings"
|
||||
SyntaxErrorStats = "syntax-errors"
|
||||
RuntimeWarningsStats = "runtime-warnings"
|
||||
UnsignedCodeWarning = "unsigned-warnings"
|
||||
HeadlessFlagWarningStats = "headless-flag-missing-warnings"
|
||||
TemplatesExecutedStats = "templates-executed"
|
||||
CodeFlagWarningStats = "code-flag-missing-warnings"
|
||||
FuzzFlagWarningStats = "fuzz-flag-missing-warnings"
|
||||
// Note: this is redefined in workflows.go to avoid circular dependency, so make sure to keep it in sync
|
||||
SkippedUnsignedStats = "skipped-unsigned-stats" // tracks loading of unsigned templates
|
||||
)
|
||||
|
||||
func init() {
|
||||
parsedTemplatesCache = cache.New()
|
||||
config.DefaultConfig.RegisterGlobalCache(parsedTemplatesCache)
|
||||
|
||||
stats.NewEntry(SyntaxWarningStats, "Found %d templates with syntax warning (use -validate flag for further examination)")
|
||||
stats.NewEntry(SyntaxErrorStats, "Found %d templates with syntax error (use -validate flag for further examination)")
|
||||
stats.NewEntry(RuntimeWarningsStats, "Found %d templates with runtime error (use -validate flag for further examination)")
|
||||
stats.NewEntry(UnsignedCodeWarning, "Found %d unsigned or tampered code template (carefully examine before using it & use -sign flag to sign them)")
|
||||
stats.NewEntry(HeadlessFlagWarningStats, "Excluded %d headless template[s] (disabled as default), use -headless option to run headless templates.")
|
||||
stats.NewEntry(CodeFlagWarningStats, "Excluded %d code template[s] (disabled as default), use -code option to run code templates.")
|
||||
stats.NewEntry(TemplatesExecutedStats, "Excluded %d template[s] with known weak matchers / tags excluded from default run using .nuclei-ignore")
|
||||
stats.NewEntry(FuzzFlagWarningStats, "Excluded %d fuzz template[s] (disabled as default), use -fuzz option to run fuzz templates.")
|
||||
stats.NewEntry(SkippedUnsignedStats, "Skipping %d unsigned template[s]")
|
||||
}
|
||||
|
||||
// ParseTemplate parses a template and returns a *templates.Template structure
|
||||
func ParseTemplate(templatePath string, catalog catalog.Catalog) (*templates.Template, error) {
|
||||
if value, err := parsedTemplatesCache.Has(templatePath); value != nil {
|
||||
return value.(*templates.Template), err
|
||||
}
|
||||
data, err := utils.ReadFromPathOrURL(templatePath, catalog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
template := &templates.Template{}
|
||||
|
||||
switch config.GetTemplateFormatFromExt(templatePath) {
|
||||
case config.JSON:
|
||||
err = json.Unmarshal(data, template)
|
||||
case config.YAML:
|
||||
if NoStrictSyntax {
|
||||
err = yaml.Unmarshal(data, template)
|
||||
} else {
|
||||
err = yaml.UnmarshalStrict(data, template)
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("failed to identify template format expected JSON or YAML but got %v", templatePath)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parsedTemplatesCache.Store(templatePath, template, nil)
|
||||
return template, nil
|
||||
}
|
||||
@@ -4,9 +4,8 @@ import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const input = "abcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmn"
|
||||
@@ -24,8 +23,8 @@ func TestHexDumpHighlighting(t *testing.T) {
|
||||
|
||||
t.Run("Test highlighting when the snippet is wrapped", func(t *testing.T) {
|
||||
result, err := toHighLightedHexDump(hex.Dump([]byte(input)), "defghij")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, highlightedHexDumpResponse, result.String())
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, highlightedHexDumpResponse, result.String())
|
||||
})
|
||||
|
||||
t.Run("Test highlight when the snippet contains separator character", func(t *testing.T) {
|
||||
@@ -36,8 +35,8 @@ func TestHexDumpHighlighting(t *testing.T) {
|
||||
"00000000 61 73 64 66 61 73 64 66 61 73 64 \x1b[32m61\x1b[0m \x1b[32m7c\x1b[0m \x1b[32m62\x1b[0m 61 73 |asdfasdfasd\x1b[32ma\x1b[0m\x1b[32m|\x1b[0m\x1b[32mb\x1b[0mas|\n" +
|
||||
"00000010 64 66 61 64 73 64 66 73 7c |dfadsdfs||\n"
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expected, result.String())
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, expected, result.String())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -59,7 +58,7 @@ func TestHighlight(t *testing.T) {
|
||||
|
||||
t.Run("Test highlighting when the snippet is wrapped", func(t *testing.T) {
|
||||
result := Highlight(&operatorResult, hex.Dump([]byte(input)), false, true)
|
||||
assert.Equal(t, multiSnippetHighlightHexDumpResponse, result)
|
||||
require.Equal(t, multiSnippetHighlightHexDumpResponse, result)
|
||||
})
|
||||
|
||||
t.Run("Test highlighting without hexdump", func(t *testing.T) {
|
||||
@@ -75,17 +74,17 @@ func TestHighlight(t *testing.T) {
|
||||
"a\x1b[0m\x1b[32mb\x1b[0mc\x1b[32md\x1b[0m\x1b[32me\x1b[0m\x1b[32mf\x1b[0m\x1b[32mg\x1b[0m\x1b[32mh\x1b[0m\x1b[32mi\x1b[0m\x1b[32mj\x1b[0mklmn\x1b[32m" +
|
||||
"a\x1b[0m\x1b[32mb\x1b[0mc\x1b[32md\x1b[0m\x1b[32me\x1b[0m\x1b[32mf\x1b[0m\x1b[32mg\x1b[0m\x1b[32mh\x1b[0m\x1b[32mi\x1b[0m\x1b[32mj\x1b[0mklmn"
|
||||
print(result)
|
||||
assert.Equal(t, expected, result)
|
||||
require.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("Test the response is not modified if noColor is true", func(t *testing.T) {
|
||||
result := Highlight(&operatorResult, input, true, false)
|
||||
assert.Equal(t, input, result)
|
||||
require.Equal(t, input, result)
|
||||
})
|
||||
|
||||
t.Run("Test the response is not modified if noColor is true", func(t *testing.T) {
|
||||
result := Highlight(&operatorResult, hex.Dump([]byte(input)), true, true)
|
||||
assert.Equal(t, hex.Dump([]byte(input)), result)
|
||||
require.Equal(t, hex.Dump([]byte(input)), result)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -107,5 +106,5 @@ start ValueToMatch-2.1 end
|
||||
"start \x1b[32mV\x1b[0m\x1b[32ma\x1b[0m\x1b[32ml\x1b[0m\x1b[32mu\x1b[0m\x1b[32me\x1b[0m\x1b[32mT\x1b[0m\x1b[32mo\x1b[0m\x1b[32mM\x1b[0m\x1b[32ma\x1b[0m\x1b[32mt\x1b[0m\x1b[32mc\x1b[0m\x1b[32mh\x1b[0m\x1b[32m-\x1b[0m\x1b[32m1\x1b[0m\x1b[32m.\x1b[0m\x1b[32m2\x1b[0m\x1b[32m.\x1b[0m\x1b[32m3\x1b[0m end\n" +
|
||||
"start \x1b[32mV\x1b[0m\x1b[32ma\x1b[0m\x1b[32ml\x1b[0m\x1b[32mu\x1b[0m\x1b[32me\x1b[0m\x1b[32mT\x1b[0m\x1b[32mo\x1b[0m\x1b[32mM\x1b[0m\x1b[32ma\x1b[0m\x1b[32mt\x1b[0m\x1b[32mc\x1b[0m\x1b[32mh\x1b[0m\x1b[32m-\x1b[0m\x1b[32m2\x1b[0m\x1b[32m.\x1b[0m\x1b[32m1\x1b[0m end \n"
|
||||
result := Highlight(&operatorResult, input, false, false)
|
||||
assert.Equal(t, expected, result)
|
||||
require.Equal(t, expected, result)
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package protocolstate
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/utils/env"
|
||||
httputil "github.com/projectdiscovery/utils/http"
|
||||
"github.com/projectdiscovery/utils/memguardian"
|
||||
)
|
||||
|
||||
var (
|
||||
MaxThreadsOnLowMemory = env.GetEnvOrDefault("MEMGUARDIAN_THREADS", 0)
|
||||
MaxBytesBufferAllocOnLowMemory = env.GetEnvOrDefault("MEMGUARDIAN_ALLOC", 0)
|
||||
memTimer *time.Ticker
|
||||
)
|
||||
|
||||
func StartActiveMemGuardian() {
|
||||
if memguardian.DefaultMemGuardian == nil {
|
||||
return
|
||||
}
|
||||
|
||||
memTimer := time.NewTicker(memguardian.DefaultInterval)
|
||||
go func() {
|
||||
for range memTimer.C {
|
||||
if IsLowOnMemory() {
|
||||
_ = GlobalGuardBytesBufferAlloc()
|
||||
} else {
|
||||
GlobalRestoreBytesBufferAlloc()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func StopActiveMemGuardian() {
|
||||
if memguardian.DefaultMemGuardian == nil {
|
||||
return
|
||||
}
|
||||
|
||||
memTimer.Stop()
|
||||
}
|
||||
|
||||
func IsLowOnMemory() bool {
|
||||
if memguardian.DefaultMemGuardian != nil && memguardian.DefaultMemGuardian.Warning.Load() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GuardThreads on caller
|
||||
func GuardThreadsOrDefault(current int) int {
|
||||
if MaxThreadsOnLowMemory > 0 {
|
||||
return MaxThreadsOnLowMemory
|
||||
}
|
||||
|
||||
fraction := int(current / 5)
|
||||
if fraction > 0 {
|
||||
return fraction
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
var muGlobalChange sync.Mutex
|
||||
|
||||
// Global setting
|
||||
func GlobalGuardBytesBufferAlloc() error {
|
||||
if muGlobalChange.TryLock() {
|
||||
return nil
|
||||
|
||||
}
|
||||
defer muGlobalChange.Unlock()
|
||||
|
||||
// if current capacity was not reduced decrease it
|
||||
if MaxBytesBufferAllocOnLowMemory > 0 && httputil.DefaultBytesBufferAlloc == httputil.GetPoolSize() {
|
||||
gologger.Debug().Msgf("reducing bytes.buffer pool size to: %d", MaxBytesBufferAllocOnLowMemory)
|
||||
delta := httputil.GetPoolSize() - int64(MaxBytesBufferAllocOnLowMemory)
|
||||
return httputil.ChangePoolSize(-delta)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Global setting
|
||||
func GlobalRestoreBytesBufferAlloc() {
|
||||
if muGlobalChange.TryLock() {
|
||||
return
|
||||
|
||||
}
|
||||
defer muGlobalChange.Unlock()
|
||||
|
||||
if httputil.DefaultBytesBufferAlloc != httputil.GetPoolSize() {
|
||||
delta := httputil.DefaultBytesBufferAlloc - httputil.GetPoolSize()
|
||||
gologger.Debug().Msgf("restoring bytes.buffer pool size to: %d", httputil.DefaultBytesBufferAlloc)
|
||||
_ = httputil.ChangePoolSize(delta)
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,16 @@ import (
|
||||
)
|
||||
|
||||
// Dialer is a shared fastdialer instance for host DNS resolution
|
||||
var Dialer *fastdialer.Dialer
|
||||
var (
|
||||
Dialer *fastdialer.Dialer
|
||||
)
|
||||
|
||||
// Init creates the Dialer instance based on user configuration
|
||||
func Init(options *types.Options) error {
|
||||
if Dialer != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
lfaAllowed = options.AllowLocalFileAccess
|
||||
opts := fastdialer.DefaultOptions
|
||||
if options.DialerTimeout > 0 {
|
||||
@@ -142,6 +145,8 @@ func Init(options *types.Options) error {
|
||||
return Dialer.Dial(ctx, "tcp", addr)
|
||||
})
|
||||
|
||||
StartActiveMemGuardian()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -202,4 +207,5 @@ func Close() {
|
||||
if Dialer != nil {
|
||||
Dialer.Close()
|
||||
}
|
||||
StopActiveMemGuardian()
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetRandomIp(t *testing.T) {
|
||||
@@ -110,15 +110,15 @@ func TestGetRandomIp(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ip, err := GetRandomIPWithCidr(test.cidr...)
|
||||
if test.valid {
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
anyInRange := false
|
||||
for _, cidr := range test.cidr {
|
||||
_, network, _ := net.ParseCIDR(cidr)
|
||||
anyInRange = anyInRange || network.Contains(ip)
|
||||
}
|
||||
assert.Truef(t, anyInRange, "the IP address returned %v is not in range of the provided CIDRs", ip)
|
||||
require.Truef(t, anyInRange, "the IP address returned %v is not in range of the provided CIDRs", ip)
|
||||
} else {
|
||||
assert.Error(t, err, test.errorMsg)
|
||||
require.Error(t, err, test.errorMsg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package replacer
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestReplacerReplace(t *testing.T) {
|
||||
@@ -77,7 +77,7 @@ func TestReplacerReplace(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, Replace(test.template, test.values))
|
||||
require.Equal(t, test.expected, Replace(test.template, test.values))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,7 @@ func TestReplacerReplaceOne(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, ReplaceOne(test.template, test.key, test.value))
|
||||
require.Equal(t, test.expected, ReplaceOne(test.template, test.key, test.value))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package tostring
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// UnsafeToString converts byte slice to string with zero allocations
|
||||
func UnsafeToString(bs []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&bs))
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httpclientpool"
|
||||
httputil "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils/http"
|
||||
"github.com/projectdiscovery/rawhttp"
|
||||
@@ -411,6 +412,10 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error {
|
||||
}
|
||||
}
|
||||
if len(request.Payloads) > 0 {
|
||||
// specifically for http requests high concurrency and and threads will lead to memory exausthion, hence reduce the maximum parallelism
|
||||
if protocolstate.IsLowOnMemory() {
|
||||
request.Threads = protocolstate.GuardThreadsOrDefault(request.Threads)
|
||||
}
|
||||
// if we have payloads, adjust threads if none specified
|
||||
request.Threads = options.GetThreadsForNPayloadRequests(request.Requests(), request.Threads)
|
||||
}
|
||||
|
||||
@@ -112,21 +112,21 @@ func (request *Request) responseToDSLMap(resp *http.Response, host, matched, raw
|
||||
data[k] = v
|
||||
}
|
||||
for _, cookie := range resp.Cookies() {
|
||||
data[strings.ToLower(cookie.Name)] = cookie.Value
|
||||
request.setHashOrDefault(data, strings.ToLower(cookie.Name), cookie.Value)
|
||||
}
|
||||
for k, v := range resp.Header {
|
||||
k = strings.ToLower(strings.ReplaceAll(strings.TrimSpace(k), "-", "_"))
|
||||
data[k] = strings.Join(v, " ")
|
||||
request.setHashOrDefault(data, k, strings.Join(v, " "))
|
||||
}
|
||||
data["host"] = host
|
||||
data["type"] = request.Type().String()
|
||||
data["matched"] = matched
|
||||
data["request"] = rawReq
|
||||
data["response"] = rawResp
|
||||
request.setHashOrDefault(data, "request", rawReq)
|
||||
request.setHashOrDefault(data, "response", rawResp)
|
||||
data["status_code"] = resp.StatusCode
|
||||
data["body"] = body
|
||||
data["all_headers"] = headers
|
||||
data["header"] = headers
|
||||
request.setHashOrDefault(data, "body", body)
|
||||
request.setHashOrDefault(data, "all_headers", headers)
|
||||
request.setHashOrDefault(data, "header", headers)
|
||||
data["duration"] = duration.Seconds()
|
||||
data["template-id"] = request.options.TemplateID
|
||||
data["template-info"] = request.options.TemplateInfo
|
||||
@@ -140,6 +140,15 @@ func (request *Request) responseToDSLMap(resp *http.Response, host, matched, raw
|
||||
return data
|
||||
}
|
||||
|
||||
// TODO: disabling hdd storage while testing backpressure mechanism
|
||||
func (request *Request) setHashOrDefault(data output.InternalEvent, k string, v string) {
|
||||
// if hash, err := request.options.Storage.SetString(v); err == nil {
|
||||
// data[k] = hash
|
||||
// } else {
|
||||
data[k] = v
|
||||
//}
|
||||
}
|
||||
|
||||
// MakeResultEvent creates a result event from internal wrapped event
|
||||
func (request *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*output.ResultEvent {
|
||||
return protocols.MakeDefaultResultEvent(request, wrapped)
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/eventcreator"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/responsehighlighter"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/tostring"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httpclientpool"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httputils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/signer"
|
||||
@@ -168,6 +168,10 @@ func (request *Request) executeParallelHTTP(input *contextargs.Context, dynamicV
|
||||
// Workers that keeps enqueuing new requests
|
||||
maxWorkers := request.Threads
|
||||
|
||||
if protocolstate.IsLowOnMemory() {
|
||||
maxWorkers = protocolstate.GuardThreadsOrDefault(request.Threads)
|
||||
}
|
||||
|
||||
// Stop-at-first-match logic while executing requests
|
||||
// parallely using threads
|
||||
shouldStop := (request.options.Options.StopAtFirstMatch || request.StopAtFirstMatch || request.options.StopAtFirstMatch)
|
||||
@@ -484,6 +488,35 @@ var errStopExecution = errors.New("stop execution due to unresolved variables")
|
||||
|
||||
// executeRequest executes the actual generated request and returns error if occurred
|
||||
func (request *Request) executeRequest(input *contextargs.Context, generatedRequest *generatedRequest, previousEvent output.InternalEvent, hasInteractMatchers bool, callback protocols.OutputEventCallback, requestCount int) error {
|
||||
outputEvent := output.InternalEvent{}
|
||||
var event *output.InternalWrappedEvent
|
||||
// event should never be nil as per existing logic
|
||||
defer func() {
|
||||
if event == nil {
|
||||
event := &output.InternalWrappedEvent{
|
||||
InternalEvent: map[string]interface{}{
|
||||
"template-id": request.options.TemplateID,
|
||||
"host": input.MetaInput.Input,
|
||||
},
|
||||
}
|
||||
if request.CompiledOperators != nil && request.CompiledOperators.HasDSL() {
|
||||
event.InternalEvent = outputEvent
|
||||
}
|
||||
callback(event)
|
||||
return
|
||||
}
|
||||
if event.InternalEvent == nil {
|
||||
event.InternalEvent = outputEvent
|
||||
}
|
||||
// make sure templateId is never nil
|
||||
if event.InternalEvent["template-id"] == nil {
|
||||
event.InternalEvent["template-id"] = request.options.TemplateID
|
||||
}
|
||||
if event.InternalEvent["host"] == nil {
|
||||
event.InternalEvent["host"] = input.MetaInput.Input
|
||||
}
|
||||
}()
|
||||
|
||||
request.setCustomHeaders(generatedRequest)
|
||||
|
||||
// Try to evaluate any payloads before replacement
|
||||
@@ -684,7 +717,7 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
|
||||
// In case of interactsh markers and request times out, still send
|
||||
// a callback event so in case we receive an interaction, correlation is possible.
|
||||
// Also, to log failed use-cases.
|
||||
outputEvent := request.responseToDSLMap(&http.Response{}, input.MetaInput.Input, formedURL, tostring.UnsafeToString(dumpedRequest), "", "", "", 0, generatedRequest.meta)
|
||||
outputEvent = request.responseToDSLMap(&http.Response{}, input.MetaInput.Input, formedURL, convUtil.String(dumpedRequest), "", "", "", 0, generatedRequest.meta)
|
||||
if i := strings.LastIndex(hostname, ":"); i != -1 {
|
||||
hostname = hostname[:i]
|
||||
}
|
||||
@@ -694,12 +727,6 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
|
||||
} else {
|
||||
outputEvent["ip"] = httpclientpool.Dialer.GetDialedIP(hostname)
|
||||
}
|
||||
|
||||
event := &output.InternalWrappedEvent{InternalEvent: outputEvent}
|
||||
if request.CompiledOperators != nil {
|
||||
event.InternalEvent = outputEvent
|
||||
}
|
||||
callback(event)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -771,7 +798,7 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
|
||||
}
|
||||
finalEvent := make(output.InternalEvent)
|
||||
|
||||
outputEvent := request.responseToDSLMap(respChain.Response(), input.MetaInput.Input, matchedURL, convUtil.String(dumpedRequest), respChain.FullResponse().String(), respChain.Body().String(), respChain.Headers().String(), duration, generatedRequest.meta)
|
||||
outputEvent = request.responseToDSLMap(respChain.Response(), input.MetaInput.Input, matchedURL, convUtil.String(dumpedRequest), respChain.FullResponse().String(), respChain.Body().String(), respChain.Headers().String(), duration, generatedRequest.meta)
|
||||
// add response fields to template context and merge templatectx variables to output event
|
||||
request.options.AddTemplateVars(input.MetaInput, request.Type(), request.ID, outputEvent)
|
||||
if request.options.HasTemplateCtx(input.MetaInput) {
|
||||
@@ -809,7 +836,7 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
|
||||
// prune signature internal values if any
|
||||
request.pruneSignatureInternalValues(generatedRequest.meta)
|
||||
|
||||
event := eventcreator.CreateEventWithAdditionalOptions(request, generators.MergeMaps(generatedRequest.dynamicValues, finalEvent), request.options.Options.Debug || request.options.Options.DebugResponse, func(internalWrappedEvent *output.InternalWrappedEvent) {
|
||||
event = eventcreator.CreateEventWithAdditionalOptions(request, generators.MergeMaps(generatedRequest.dynamicValues, finalEvent), request.options.Options.Debug || request.options.Options.DebugResponse, func(internalWrappedEvent *output.InternalWrappedEvent) {
|
||||
internalWrappedEvent.OperatorsResult.PayloadValues = generatedRequest.meta
|
||||
})
|
||||
if hasInteractMatchers {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/parsers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/loader/workflow"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
@@ -40,8 +40,9 @@ func setup() {
|
||||
Browser: nil,
|
||||
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
|
||||
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
|
||||
Parser: templates.NewParser(),
|
||||
}
|
||||
workflowLoader, err := parsers.NewLoader(&executerOpts)
|
||||
workflowLoader, err := workflow.NewLoader(&executerOpts)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create workflow loader: %s\n", err)
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/eventcreator"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/tostring"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
|
||||
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
"github.com/projectdiscovery/utils/conversion"
|
||||
)
|
||||
|
||||
var _ protocols.Request = &Request{}
|
||||
@@ -60,7 +60,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, metadata
|
||||
gologger.Error().Msgf("Could not read file path %s: %s\n", data, err)
|
||||
return
|
||||
}
|
||||
dataStr := tostring.UnsafeToString(buffer)
|
||||
dataStr := conversion.String(buffer)
|
||||
|
||||
resp, err := readResponseFromString(dataStr)
|
||||
if err != nil {
|
||||
@@ -86,7 +86,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, metadata
|
||||
return
|
||||
}
|
||||
|
||||
outputEvent := request.responseToDSLMap(resp, data, data, data, tostring.UnsafeToString(dumpedResponse), tostring.UnsafeToString(body), utils.HeadersToString(resp.Header), 0, nil)
|
||||
outputEvent := request.responseToDSLMap(resp, data, data, data, conversion.String(dumpedResponse), conversion.String(body), utils.HeadersToString(resp.Header), 0, nil)
|
||||
// add response fields to template context and merge templatectx variables to output event
|
||||
request.options.AddTemplateVars(input.MetaInput, request.Type(), request.GetID(), outputEvent)
|
||||
if request.options.HasTemplateCtx(input.MetaInput) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/input"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/js/compiler"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/loader/parser"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators/extractors"
|
||||
@@ -121,6 +122,7 @@ type ExecutorOptions struct {
|
||||
AuthProvider authprovider.AuthProvider
|
||||
//TemporaryDirectory is the directory to store temporary files
|
||||
TemporaryDirectory string
|
||||
Parser parser.Parser
|
||||
}
|
||||
|
||||
// GetThreadsForPayloadRequests returns the number of threads to use as default for
|
||||
|
||||
@@ -7,13 +7,13 @@ package dedupe
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/utils/conversion"
|
||||
)
|
||||
|
||||
// Storage is a duplicate detecting storage for nuclei scan events.
|
||||
@@ -75,29 +75,29 @@ func (s *Storage) Close() {
|
||||
func (s *Storage) Index(result *output.ResultEvent) (bool, error) {
|
||||
hasher := sha1.New()
|
||||
if result.TemplateID != "" {
|
||||
_, _ = hasher.Write(unsafeToBytes(result.TemplateID))
|
||||
_, _ = hasher.Write(conversion.Bytes(result.TemplateID))
|
||||
}
|
||||
if result.MatcherName != "" {
|
||||
_, _ = hasher.Write(unsafeToBytes(result.MatcherName))
|
||||
_, _ = hasher.Write(conversion.Bytes(result.MatcherName))
|
||||
}
|
||||
if result.ExtractorName != "" {
|
||||
_, _ = hasher.Write(unsafeToBytes(result.ExtractorName))
|
||||
_, _ = hasher.Write(conversion.Bytes(result.ExtractorName))
|
||||
}
|
||||
if result.Type != "" {
|
||||
_, _ = hasher.Write(unsafeToBytes(result.Type))
|
||||
_, _ = hasher.Write(conversion.Bytes(result.Type))
|
||||
}
|
||||
if result.Host != "" {
|
||||
_, _ = hasher.Write(unsafeToBytes(result.Host))
|
||||
_, _ = hasher.Write(conversion.Bytes(result.Host))
|
||||
}
|
||||
if result.Matched != "" {
|
||||
_, _ = hasher.Write(unsafeToBytes(result.Matched))
|
||||
_, _ = hasher.Write(conversion.Bytes(result.Matched))
|
||||
}
|
||||
for _, v := range result.ExtractedResults {
|
||||
_, _ = hasher.Write(unsafeToBytes(v))
|
||||
_, _ = hasher.Write(conversion.Bytes(v))
|
||||
}
|
||||
for k, v := range result.Metadata {
|
||||
_, _ = hasher.Write(unsafeToBytes(k))
|
||||
_, _ = hasher.Write(unsafeToBytes(types.ToString(v)))
|
||||
_, _ = hasher.Write(conversion.Bytes(k))
|
||||
_, _ = hasher.Write(conversion.Bytes(types.ToString(v)))
|
||||
}
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
@@ -112,12 +112,3 @@ func (s *Storage) Index(result *output.ResultEvent) (bool, error) {
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
// unsafeToBytes converts a string to byte slice and does it with
|
||||
// zero allocations.
|
||||
//
|
||||
// Reference - https://stackoverflow.com/questions/59209493/how-to-use-unsafe-get-a-byte-slice-from-a-string-without-memory-copy
|
||||
func unsafeToBytes(data string) []byte {
|
||||
var buf = (*[]byte)(unsafe.Pointer(&data))
|
||||
return *buf
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMarkDownHeaderCreation(t *testing.T) {
|
||||
@@ -21,7 +21,7 @@ func TestMarkDownHeaderCreation(t *testing.T) {
|
||||
|
||||
for _, currentTestCase := range testCases {
|
||||
t.Run(strings.Join(currentTestCase.headers, ","), func(t1 *testing.T) {
|
||||
assert.Equal(t1, CreateTableHeader(currentTestCase.headers...), currentTestCase.expectedValue)
|
||||
require.Equal(t1, CreateTableHeader(currentTestCase.headers...), currentTestCase.expectedValue)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -34,8 +34,8 @@ func TestCreateTemplateInfoTableTooManyColumns(t *testing.T) {
|
||||
{"h", "i"},
|
||||
})
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.Empty(t, table)
|
||||
require.NotNil(t, err)
|
||||
require.Empty(t, table)
|
||||
}
|
||||
|
||||
func TestCreateTemplateInfoTable1Column(t *testing.T) {
|
||||
@@ -48,8 +48,8 @@ func TestCreateTemplateInfoTable1Column(t *testing.T) {
|
||||
| c |
|
||||
`
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expected, table)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, expected, table)
|
||||
}
|
||||
|
||||
func TestCreateTemplateInfoTable2Columns(t *testing.T) {
|
||||
@@ -66,8 +66,8 @@ func TestCreateTemplateInfoTable2Columns(t *testing.T) {
|
||||
| d | e |
|
||||
`
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expected, table)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, expected, table)
|
||||
}
|
||||
|
||||
func TestCreateTemplateInfoTable3Columns(t *testing.T) {
|
||||
@@ -86,6 +86,6 @@ func TestCreateTemplateInfoTable3Columns(t *testing.T) {
|
||||
| h | i | |
|
||||
`
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expected, table)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, expected, table)
|
||||
}
|
||||
@@ -4,12 +4,11 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/stringslice"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/markdown/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestToMarkdownTableString(t *testing.T) {
|
||||
@@ -44,6 +43,6 @@ func TestToMarkdownTableString(t *testing.T) {
|
||||
|
||||
actualAttributeSlice := strings.Split(result, "\n")
|
||||
dynamicAttributeIndex := len(actualAttributeSlice) - len(expectedDynamicAttributes)
|
||||
assert.Equal(t, strings.Split(expectedOrderedAttributes, "\n"), actualAttributeSlice[:dynamicAttributeIndex]) // the first part of the result is ordered
|
||||
assert.ElementsMatch(t, expectedDynamicAttributes, actualAttributeSlice[dynamicAttributeIndex:]) // dynamic parameters are not ordered
|
||||
require.Equal(t, strings.Split(expectedOrderedAttributes, "\n"), actualAttributeSlice[:dynamicAttributeIndex]) // the first part of the result is ordered
|
||||
require.ElementsMatch(t, expectedDynamicAttributes, actualAttributeSlice[dynamicAttributeIndex:]) // dynamic parameters are not ordered
|
||||
}
|
||||
@@ -1,21 +1,22 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLinkCreation(t *testing.T) {
|
||||
jiraIntegration := &Integration{}
|
||||
link := jiraIntegration.CreateLink("ProjectDiscovery", "https://projectdiscovery.io")
|
||||
assert.Equal(t, "[ProjectDiscovery|https://projectdiscovery.io]", link)
|
||||
require.Equal(t, "[ProjectDiscovery|https://projectdiscovery.io]", link)
|
||||
}
|
||||
|
||||
func TestHorizontalLineCreation(t *testing.T) {
|
||||
jiraIntegration := &Integration{}
|
||||
horizontalLine := jiraIntegration.CreateHorizontalLine()
|
||||
assert.True(t, strings.Contains(horizontalLine, "----"))
|
||||
require.True(t, strings.Contains(horizontalLine, "----"))
|
||||
}
|
||||
|
||||
func TestTableCreation(t *testing.T) {
|
||||
@@ -27,11 +28,11 @@ func TestTableCreation(t *testing.T) {
|
||||
{"d", "e"},
|
||||
})
|
||||
|
||||
assert.Nil(t, err)
|
||||
require.Nil(t, err)
|
||||
expected := `| key | value |
|
||||
| a | b |
|
||||
| c | |
|
||||
| d | e |
|
||||
`
|
||||
assert.Equal(t, expected, table)
|
||||
require.Equal(t, expected, table)
|
||||
}
|
||||
+32
-15
@@ -10,19 +10,32 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
)
|
||||
|
||||
type ScanContextOption func(*ScanContext)
|
||||
|
||||
func WithEvents() ScanContextOption {
|
||||
return func(sc *ScanContext) {
|
||||
sc.withEvents = true
|
||||
}
|
||||
}
|
||||
|
||||
type ScanContext struct {
|
||||
context.Context
|
||||
// exported / configurable fields
|
||||
Input *contextargs.Context
|
||||
|
||||
// callbacks or hooks
|
||||
OnError func(error)
|
||||
OnResult func(e *output.InternalWrappedEvent)
|
||||
OnError func(error)
|
||||
OnResult func(e *output.InternalWrappedEvent)
|
||||
OnWarning func(string)
|
||||
|
||||
// unexported state fields
|
||||
errors []error
|
||||
warnings []string
|
||||
events []*output.InternalWrappedEvent
|
||||
results []*output.ResultEvent
|
||||
|
||||
// what to log
|
||||
withEvents bool
|
||||
|
||||
// might not be required but better to sync
|
||||
m sync.Mutex
|
||||
@@ -37,7 +50,8 @@ func NewScanContext(input *contextargs.Context) *ScanContext {
|
||||
func (s *ScanContext) GenerateResult() []*output.ResultEvent {
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
return aggregateResults(s.events)
|
||||
|
||||
return s.results
|
||||
}
|
||||
|
||||
// LogEvent logs events to all events and triggeres any callbacks
|
||||
@@ -48,10 +62,16 @@ func (s *ScanContext) LogEvent(e *output.InternalWrappedEvent) {
|
||||
// do not log nil events
|
||||
return
|
||||
}
|
||||
|
||||
if s.OnResult != nil {
|
||||
s.OnResult(e)
|
||||
}
|
||||
s.events = append(s.events, e)
|
||||
|
||||
if s.withEvents {
|
||||
s.events = append(s.events, e)
|
||||
}
|
||||
|
||||
s.results = append(s.results, e.Results...)
|
||||
}
|
||||
|
||||
// LogError logs error to all events and triggeres any callbacks
|
||||
@@ -68,10 +88,11 @@ func (s *ScanContext) LogError(err error) {
|
||||
s.errors = append(s.errors, err)
|
||||
|
||||
errorMessage := joinErrors(s.errors)
|
||||
results := aggregateResults(s.events)
|
||||
for _, result := range results {
|
||||
|
||||
for _, result := range s.results {
|
||||
result.Error = errorMessage
|
||||
}
|
||||
|
||||
for _, e := range s.events {
|
||||
e.InternalEvent["error"] = errorMessage
|
||||
}
|
||||
@@ -82,6 +103,11 @@ func (s *ScanContext) LogWarning(format string, args ...any) {
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
val := fmt.Sprintf(format, args...)
|
||||
|
||||
if s.OnWarning != nil {
|
||||
s.OnWarning(val)
|
||||
}
|
||||
|
||||
s.warnings = append(s.warnings, val)
|
||||
|
||||
for _, e := range s.events {
|
||||
@@ -91,15 +117,6 @@ func (s *ScanContext) LogWarning(format string, args ...any) {
|
||||
}
|
||||
}
|
||||
|
||||
// aggregateResults aggregates results from multiple events
|
||||
func aggregateResults(events []*output.InternalWrappedEvent) []*output.ResultEvent {
|
||||
var results []*output.ResultEvent
|
||||
for _, e := range events {
|
||||
results = append(results, e.Results...)
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// joinErrors joins multiple errors and returns a single error string
|
||||
func joinErrors(errors []error) string {
|
||||
var errorMessages []string
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"github.com/projectdiscovery/utils/conversion"
|
||||
mapsutil "github.com/projectdiscovery/utils/maps"
|
||||
)
|
||||
|
||||
// Templates is a cache for caching and storing templates for reuse.
|
||||
type Cache struct {
|
||||
items *mapsutil.SyncLockMap[string, parsedTemplate]
|
||||
}
|
||||
|
||||
// New returns a new templates cache
|
||||
func NewCache() *Cache {
|
||||
return &Cache{items: mapsutil.NewSyncLockMap[string, parsedTemplate]()}
|
||||
}
|
||||
|
||||
type parsedTemplate struct {
|
||||
template *Template
|
||||
raw string
|
||||
err error
|
||||
}
|
||||
|
||||
// Has returns true if the cache has a template. The template
|
||||
// is returned along with any errors if found.
|
||||
func (t *Cache) Has(template string) (*Template, []byte, error) {
|
||||
value, ok := t.items.Get(template)
|
||||
if !ok {
|
||||
return nil, nil, nil
|
||||
}
|
||||
return value.template, conversion.Bytes(value.raw), value.err
|
||||
}
|
||||
|
||||
// Store stores a template with data and error
|
||||
func (t *Cache) Store(id string, tpl *Template, raw []byte, err error) {
|
||||
_ = t.items.Set(id, parsedTemplate{template: tpl, raw: conversion.String(raw), err: err})
|
||||
}
|
||||
|
||||
// Purge the cache
|
||||
func (t *Cache) Purge() {
|
||||
t.items.Clear()
|
||||
}
|
||||
Vendored
-40
@@ -1,40 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
mapsutil "github.com/projectdiscovery/utils/maps"
|
||||
)
|
||||
|
||||
// Templates is a cache for caching and storing templates for reuse.
|
||||
type Templates struct {
|
||||
items *mapsutil.SyncLockMap[string, parsedTemplateErrHolder]
|
||||
}
|
||||
|
||||
// New returns a new templates cache
|
||||
func New() *Templates {
|
||||
return &Templates{items: mapsutil.NewSyncLockMap[string, parsedTemplateErrHolder]()}
|
||||
}
|
||||
|
||||
type parsedTemplateErrHolder struct {
|
||||
template interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
// Has returns true if the cache has a template. The template
|
||||
// is returned along with any errors if found.
|
||||
func (t *Templates) Has(template string) (interface{}, error) {
|
||||
value, ok := t.items.Get(template)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
return value.template, value.err
|
||||
}
|
||||
|
||||
// Store stores a template with data and error
|
||||
func (t *Templates) Store(template string, data interface{}, err error) {
|
||||
_ = t.items.Set(template, parsedTemplateErrHolder{template: data, err: err})
|
||||
}
|
||||
|
||||
// Purge the cache
|
||||
func (t *Templates) Purge() {
|
||||
t.items.Clear()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cache
|
||||
package templates
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -8,15 +8,17 @@ import (
|
||||
)
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
templates := New()
|
||||
templates := NewCache()
|
||||
testErr := errors.New("test error")
|
||||
|
||||
data, err := templates.Has("test")
|
||||
data, _, err := templates.Has("test")
|
||||
require.Nil(t, err, "invalid value for err")
|
||||
require.Nil(t, data, "invalid value for data")
|
||||
|
||||
templates.Store("test", "data", testErr)
|
||||
data, err = templates.Has("test")
|
||||
item := &Template{}
|
||||
|
||||
templates.Store("test", item, nil, testErr)
|
||||
data, _, err = templates.Has("test")
|
||||
require.Equal(t, testErr, err, "invalid value for err")
|
||||
require.Equal(t, "data", data, "invalid value for data")
|
||||
require.Equal(t, item, data, "invalid value for data")
|
||||
}
|
||||
@@ -252,6 +252,13 @@ func (e *ClusterExecuter) Execute(ctx *scan.ScanContext) (bool, error) {
|
||||
previous := make(map[string]interface{})
|
||||
dynamicValues := make(map[string]interface{})
|
||||
err := e.requests.ExecuteWithResults(inputItem, dynamicValues, previous, func(event *output.InternalWrappedEvent) {
|
||||
if event == nil {
|
||||
// unlikely but just in case
|
||||
return
|
||||
}
|
||||
if event.InternalEvent == nil {
|
||||
event.InternalEvent = make(map[string]interface{})
|
||||
}
|
||||
for _, operator := range e.operators {
|
||||
result, matched := operator.operator.Execute(event.InternalEvent, e.requests.Match, e.requests.Extract, e.options.Options.Debug || e.options.Options.DebugResponse)
|
||||
event.InternalEvent["template-id"] = operator.templateID
|
||||
|
||||
+18
-19
@@ -1,6 +1,7 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -18,11 +19,9 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/offlinehttp"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/cache"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/signer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/tmplexec"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
)
|
||||
@@ -30,7 +29,6 @@ import (
|
||||
var (
|
||||
ErrCreateTemplateExecutor = errors.New("cannot create template executer")
|
||||
ErrIncompatibleWithOfflineMatching = errors.New("template can't be used for offline matching")
|
||||
parsedTemplatesCache *cache.Templates
|
||||
// track how many templates are verfied and by which signer
|
||||
SignatureStats = map[string]*atomic.Uint64{}
|
||||
)
|
||||
@@ -40,41 +38,42 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
parsedTemplatesCache = cache.New()
|
||||
for _, verifier := range signer.DefaultTemplateVerifiers {
|
||||
SignatureStats[verifier.Identifier()] = &atomic.Uint64{}
|
||||
}
|
||||
SignatureStats[Unsigned] = &atomic.Uint64{}
|
||||
config.DefaultConfig.RegisterGlobalCache(parsedTemplatesCache)
|
||||
}
|
||||
|
||||
// Parse parses a yaml request template file
|
||||
// TODO make sure reading from the disk the template parsing happens once: see parsers.ParseTemplate vs templates.Parse
|
||||
//
|
||||
//nolint:gocritic // this cannot be passed by pointer
|
||||
func Parse(filePath string, preprocessor Preprocessor, options protocols.ExecutorOptions) (*Template, error) {
|
||||
parser, ok := options.Parser.(*Parser)
|
||||
if !ok {
|
||||
panic("not a parser")
|
||||
}
|
||||
if !options.DoNotCache {
|
||||
if value, err := parsedTemplatesCache.Has(filePath); value != nil {
|
||||
return value.(*Template), err
|
||||
if value, _, err := parser.compiledTemplatesCache.Has(filePath); value != nil {
|
||||
return value, err
|
||||
}
|
||||
}
|
||||
|
||||
var reader io.ReadCloser
|
||||
if utils.IsURL(filePath) {
|
||||
// use retryablehttp (tls verification is enabled by default in the standard library)
|
||||
resp, err := retryablehttp.DefaultClient().Get(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if !options.DoNotCache {
|
||||
_, raw, err := parser.parsedTemplatesCache.Has(filePath)
|
||||
if err == nil && raw != nil {
|
||||
reader = io.NopCloser(bytes.NewReader(raw))
|
||||
}
|
||||
reader = resp.Body
|
||||
} else {
|
||||
var err error
|
||||
reader, err = options.Catalog.OpenFile(filePath)
|
||||
}
|
||||
var err error
|
||||
if reader == nil {
|
||||
reader, err = utils.ReaderFromPathOrURL(filePath, options.Catalog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
defer reader.Close()
|
||||
|
||||
options.TemplatePath = filePath
|
||||
template, err := ParseTemplateFromReader(reader, preprocessor, options.Copy())
|
||||
if err != nil {
|
||||
@@ -90,7 +89,7 @@ func Parse(filePath string, preprocessor Preprocessor, options protocols.Executo
|
||||
}
|
||||
template.Path = filePath
|
||||
if !options.DoNotCache {
|
||||
parsedTemplatesCache.Store(filePath, template, err)
|
||||
parser.compiledTemplatesCache.Store(filePath, template, nil, err)
|
||||
}
|
||||
return template, nil
|
||||
}
|
||||
|
||||
@@ -13,12 +13,12 @@ import (
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/loader/workflow"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/stringslice"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/parsers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
@@ -47,8 +47,9 @@ func setup() {
|
||||
Browser: nil,
|
||||
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
|
||||
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
|
||||
Parser: templates.NewParser(),
|
||||
}
|
||||
workflowLoader, err := parsers.NewLoader(&executerOpts)
|
||||
workflowLoader, err := workflow.NewLoader(&executerOpts)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create workflow loader: %s\n", err)
|
||||
}
|
||||
|
||||
@@ -3,23 +3,23 @@ package templates
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_appendAtSignToAuthors(t *testing.T) {
|
||||
result := appendAtSignToAuthors([]string{"user1", "user2", "user3"})
|
||||
assert.Equal(t, result, "@user1,@user2,@user3")
|
||||
require.Equal(t, result, "@user1,@user2,@user3")
|
||||
}
|
||||
|
||||
func Test_appendAtSignToMissingAuthors(t *testing.T) {
|
||||
result := appendAtSignToAuthors([]string{})
|
||||
assert.Equal(t, result, "@none")
|
||||
require.Equal(t, result, "@none")
|
||||
|
||||
result = appendAtSignToAuthors(nil)
|
||||
assert.Equal(t, result, "@none")
|
||||
require.Equal(t, result, "@none")
|
||||
}
|
||||
|
||||
func Test_appendAtSignToOneAuthor(t *testing.T) {
|
||||
result := appendAtSignToAuthors([]string{"user1"})
|
||||
assert.Equal(t, result, "@user1")
|
||||
require.Equal(t, result, "@user1")
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/stats"
|
||||
yamlutil "github.com/projectdiscovery/nuclei/v3/pkg/utils/yaml"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type Parser struct {
|
||||
ShouldValidate bool
|
||||
NoStrictSyntax bool
|
||||
// this cache can be copied safely between ephemeral instances
|
||||
parsedTemplatesCache *Cache
|
||||
// this cache might potentially contain references to heap objects
|
||||
// it's recommended to always empty it at the end of execution
|
||||
compiledTemplatesCache *Cache
|
||||
}
|
||||
|
||||
func NewParser() *Parser {
|
||||
p := &Parser{
|
||||
parsedTemplatesCache: NewCache(),
|
||||
compiledTemplatesCache: NewCache(),
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// LoadTemplate returns true if the template is valid and matches the filtering criteria.
|
||||
func (p *Parser) LoadTemplate(templatePath string, t any, extraTags []string, catalog catalog.Catalog) (bool, error) {
|
||||
tagFilter, ok := t.(*TagFilter)
|
||||
if !ok {
|
||||
panic("not a *TagFilter")
|
||||
}
|
||||
t, templateParseError := p.ParseTemplate(templatePath, catalog)
|
||||
if templateParseError != nil {
|
||||
return false, ErrCouldNotLoadTemplate.Msgf(templatePath, templateParseError)
|
||||
}
|
||||
template, ok := t.(*Template)
|
||||
if !ok {
|
||||
panic("not a template")
|
||||
}
|
||||
|
||||
if len(template.Workflows) > 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
validationError := validateTemplateMandatoryFields(template)
|
||||
if validationError != nil {
|
||||
stats.Increment(SyntaxErrorStats)
|
||||
return false, ErrCouldNotLoadTemplate.Msgf(templatePath, validationError)
|
||||
}
|
||||
|
||||
ret, err := isTemplateInfoMetadataMatch(tagFilter, template, extraTags)
|
||||
if err != nil {
|
||||
return ret, ErrCouldNotLoadTemplate.Msgf(templatePath, err)
|
||||
}
|
||||
// if template loaded then check the template for optional fields to add warnings
|
||||
if ret {
|
||||
validationWarning := validateTemplateOptionalFields(template)
|
||||
if validationWarning != nil {
|
||||
stats.Increment(SyntaxWarningStats)
|
||||
return ret, ErrCouldNotLoadTemplate.Msgf(templatePath, validationWarning)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// ParseTemplate parses a template and returns a *templates.Template structure
|
||||
func (p *Parser) ParseTemplate(templatePath string, catalog catalog.Catalog) (any, error) {
|
||||
value, _, err := p.parsedTemplatesCache.Has(templatePath)
|
||||
if value != nil {
|
||||
return value, err
|
||||
}
|
||||
|
||||
reader, err := utils.ReaderFromPathOrURL(templatePath, catalog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
data, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// pre-process directives only for local files
|
||||
if fileutil.FileExists(templatePath) && config.GetTemplateFormatFromExt(templatePath) == config.YAML {
|
||||
data, err = yamlutil.PreProcess(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
template := &Template{}
|
||||
|
||||
switch config.GetTemplateFormatFromExt(templatePath) {
|
||||
case config.JSON:
|
||||
err = json.Unmarshal(data, template)
|
||||
case config.YAML:
|
||||
if p.NoStrictSyntax {
|
||||
err = yaml.Unmarshal(data, template)
|
||||
} else {
|
||||
err = yaml.UnmarshalStrict(data, template)
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("failed to identify template format expected JSON or YAML but got %v", templatePath)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.parsedTemplatesCache.Store(templatePath, template, data, nil)
|
||||
return template, nil
|
||||
}
|
||||
|
||||
// LoadWorkflow returns true if the workflow is valid and matches the filtering criteria.
|
||||
func (p *Parser) LoadWorkflow(templatePath string, catalog catalog.Catalog) (bool, error) {
|
||||
t, templateParseError := p.ParseTemplate(templatePath, catalog)
|
||||
if templateParseError != nil {
|
||||
return false, templateParseError
|
||||
}
|
||||
|
||||
template, ok := t.(*Template)
|
||||
if !ok {
|
||||
panic("not a template")
|
||||
}
|
||||
|
||||
if len(template.Workflows) > 0 {
|
||||
if validationError := validateTemplateMandatoryFields(template); validationError != nil {
|
||||
stats.Increment(SyntaxErrorStats)
|
||||
return false, validationError
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package templates
|
||||
|
||||
import "regexp"
|
||||
|
||||
var (
|
||||
ReTemplateID = regexp.MustCompile(`^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$`)
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMandatoryFieldMissingFmt = errorutil.NewWithFmt("mandatory '%s' field is missing")
|
||||
ErrInvalidField = errorutil.NewWithFmt("invalid field format for '%s' (allowed format is %s)")
|
||||
ErrWarningFieldMissing = errorutil.NewWithFmt("field '%s' is missing")
|
||||
ErrCouldNotLoadTemplate = errorutil.NewWithFmt("Could not load template %s: %s")
|
||||
ErrLoadedWithWarnings = errorutil.NewWithFmt("Loaded template %s: with syntax warning : %s")
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package templates
|
||||
|
||||
const (
|
||||
SyntaxWarningStats = "syntax-warnings"
|
||||
SyntaxErrorStats = "syntax-errors"
|
||||
RuntimeWarningsStats = "runtime-warnings"
|
||||
UnsignedCodeWarning = "unsigned-warnings"
|
||||
HeadlessFlagWarningStats = "headless-flag-missing-warnings"
|
||||
TemplatesExecutedStats = "templates-executed"
|
||||
CodeFlagWarningStats = "code-flag-missing-warnings"
|
||||
FuzzFlagWarningStats = "fuzz-flag-missing-warnings"
|
||||
SkippedUnsignedStats = "skipped-unsigned-stats" // tracks loading of unsigned templates
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
package parsers
|
||||
package templates
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -6,31 +6,28 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader/filter"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/stringslice"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLoadTemplate(t *testing.T) {
|
||||
catalog := disk.NewCatalog("")
|
||||
origTemplatesCache := parsedTemplatesCache
|
||||
defer func() { parsedTemplatesCache = origTemplatesCache }()
|
||||
p := NewParser()
|
||||
|
||||
tt := []struct {
|
||||
name string
|
||||
template *templates.Template
|
||||
template *Template
|
||||
templateErr error
|
||||
filter filter.Config
|
||||
filter TagFilterConfig
|
||||
|
||||
expectedErr error
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
template: &templates.Template{
|
||||
template: &Template{
|
||||
ID: "CVE-2021-27330",
|
||||
Info: model.Info{
|
||||
Name: "Valid template",
|
||||
@@ -42,24 +39,24 @@ func TestLoadTemplate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "emptyTemplate",
|
||||
template: &templates.Template{},
|
||||
template: &Template{},
|
||||
isValid: false,
|
||||
expectedErr: errors.New("mandatory 'name' field is missing, mandatory 'author' field is missing, mandatory 'id' field is missing"),
|
||||
expectedErr: errors.New("mandatory 'name' field is missing\nmandatory 'author' field is missing\nmandatory 'id' field is missing"),
|
||||
},
|
||||
{
|
||||
name: "emptyNameWithInvalidID",
|
||||
template: &templates.Template{
|
||||
template: &Template{
|
||||
ID: "invalid id",
|
||||
Info: model.Info{
|
||||
Authors: stringslice.StringSlice{Value: "Author"},
|
||||
SeverityHolder: severity.Holder{Severity: severity.Medium},
|
||||
},
|
||||
},
|
||||
expectedErr: errors.New("mandatory 'name' field is missing, invalid field format for 'id' (allowed format is ^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$)"),
|
||||
expectedErr: errors.New("mandatory 'name' field is missing\ninvalid field format for 'id' (allowed format is ^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$)"),
|
||||
},
|
||||
{
|
||||
name: "emptySeverity",
|
||||
template: &templates.Template{
|
||||
template: &Template{
|
||||
ID: "CVE-2021-27330",
|
||||
Info: model.Info{
|
||||
Name: "Valid template",
|
||||
@@ -71,7 +68,7 @@ func TestLoadTemplate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "template-without-severity-with-correct-filter-id",
|
||||
template: &templates.Template{
|
||||
template: &Template{
|
||||
ID: "CVE-2021-27330",
|
||||
Info: model.Info{
|
||||
Name: "Valid template",
|
||||
@@ -81,11 +78,11 @@ func TestLoadTemplate(t *testing.T) {
|
||||
// should be error because the template is loaded
|
||||
expectedErr: errors.New("field 'severity' is missing"),
|
||||
isValid: true,
|
||||
filter: filter.Config{IncludeIds: []string{"CVE-2021-27330"}},
|
||||
filter: TagFilterConfig{IncludeIds: []string{"CVE-2021-27330"}},
|
||||
},
|
||||
{
|
||||
name: "template-without-severity-with-diff-filter-id",
|
||||
template: &templates.Template{
|
||||
template: &Template{
|
||||
ID: "CVE-2021-27330",
|
||||
Info: model.Info{
|
||||
Name: "Valid template",
|
||||
@@ -93,7 +90,7 @@ func TestLoadTemplate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
isValid: false,
|
||||
filter: filter.Config{IncludeIds: []string{"another-id"}},
|
||||
filter: TagFilterConfig{IncludeIds: []string{"another-id"}},
|
||||
// no error because the template is not loaded
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -101,11 +98,11 @@ func TestLoadTemplate(t *testing.T) {
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
parsedTemplatesCache.Store(tc.name, tc.template, tc.templateErr)
|
||||
p.parsedTemplatesCache.Store(tc.name, tc.template, nil, tc.templateErr)
|
||||
|
||||
tagFilter, err := filter.New(&tc.filter)
|
||||
tagFilter, err := NewTagFilter(&tc.filter)
|
||||
require.Nil(t, err)
|
||||
success, err := LoadTemplate(tc.name, tagFilter, nil, catalog)
|
||||
success, err := p.LoadTemplate(tc.name, tagFilter, nil, catalog)
|
||||
if tc.expectedErr == nil {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
@@ -135,7 +132,7 @@ func TestLoadTemplate(t *testing.T) {
|
||||
for i, tc := range tt {
|
||||
name := fmt.Sprintf("regexp%d", i)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
template := &templates.Template{
|
||||
template := &Template{
|
||||
ID: tc.id,
|
||||
Info: model.Info{
|
||||
Name: "Valid template",
|
||||
@@ -143,11 +140,11 @@ func TestLoadTemplate(t *testing.T) {
|
||||
SeverityHolder: severity.Holder{Severity: severity.Medium},
|
||||
},
|
||||
}
|
||||
parsedTemplatesCache.Store(name, template, nil)
|
||||
p.parsedTemplatesCache.Store(name, template, nil, nil)
|
||||
|
||||
tagFilter, err := filter.New(&filter.Config{})
|
||||
tagFilter, err := NewTagFilter(&TagFilterConfig{})
|
||||
require.Nil(t, err)
|
||||
success, err := LoadTemplate(name, tagFilter, nil, catalog)
|
||||
success, err := p.LoadTemplate(name, tagFilter, nil, catalog)
|
||||
if tc.success {
|
||||
require.NoError(t, err)
|
||||
require.True(t, success)
|
||||
@@ -0,0 +1,64 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
|
||||
)
|
||||
|
||||
// validateTemplateMandatoryFields validates the mandatory fields of a template
|
||||
// return error from this function will cause hard fail and not proceed further
|
||||
func validateTemplateMandatoryFields(template *Template) error {
|
||||
info := template.Info
|
||||
|
||||
var validateErrors []error
|
||||
|
||||
if utils.IsBlank(info.Name) {
|
||||
validateErrors = append(validateErrors, ErrMandatoryFieldMissingFmt.Msgf("name"))
|
||||
}
|
||||
|
||||
if info.Authors.IsEmpty() {
|
||||
validateErrors = append(validateErrors, ErrMandatoryFieldMissingFmt.Msgf("author"))
|
||||
}
|
||||
|
||||
if template.ID == "" {
|
||||
validateErrors = append(validateErrors, ErrMandatoryFieldMissingFmt.Msgf("id"))
|
||||
} else if !ReTemplateID.MatchString(template.ID) {
|
||||
validateErrors = append(validateErrors, ErrInvalidField.Msgf("id", ReTemplateID.String()))
|
||||
}
|
||||
|
||||
if len(validateErrors) > 0 {
|
||||
return errors.Join(validateErrors...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isTemplateInfoMetadataMatch(tagFilter *TagFilter, template *Template, extraTags []string) (bool, error) {
|
||||
match, err := tagFilter.Match(template, extraTags)
|
||||
|
||||
if err == ErrExcluded {
|
||||
return false, ErrExcluded
|
||||
}
|
||||
|
||||
return match, err
|
||||
}
|
||||
|
||||
// validateTemplateOptionalFields validates the optional fields of a template
|
||||
// return error from this function will throw a warning and proceed further
|
||||
func validateTemplateOptionalFields(template *Template) error {
|
||||
info := template.Info
|
||||
|
||||
var warnings []error
|
||||
|
||||
if template.Type() != types.WorkflowProtocol && utils.IsBlank(info.SeverityHolder.Severity.String()) {
|
||||
warnings = append(warnings, ErrWarningFieldMissing.Msgf("severity"))
|
||||
}
|
||||
|
||||
if len(warnings) > 0 {
|
||||
return errors.Join(warnings...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package templates
|
||||
|
||||
import "github.com/projectdiscovery/nuclei/v3/pkg/utils/stats"
|
||||
|
||||
func init() {
|
||||
stats.NewEntry(SyntaxWarningStats, "Found %d templates with syntax warning (use -validate flag for further examination)")
|
||||
stats.NewEntry(SyntaxErrorStats, "Found %d templates with syntax error (use -validate flag for further examination)")
|
||||
stats.NewEntry(RuntimeWarningsStats, "Found %d templates with runtime error (use -validate flag for further examination)")
|
||||
stats.NewEntry(UnsignedCodeWarning, "Found %d unsigned or tampered code template (carefully examine before using it & use -sign flag to sign them)")
|
||||
stats.NewEntry(HeadlessFlagWarningStats, "Excluded %d headless template[s] (disabled as default), use -headless option to run headless templates.")
|
||||
stats.NewEntry(CodeFlagWarningStats, "Excluded %d code template[s] (disabled as default), use -code option to run code templates.")
|
||||
stats.NewEntry(TemplatesExecutedStats, "Excluded %d template[s] with known weak matchers / tags excluded from default run using .nuclei-ignore")
|
||||
stats.NewEntry(FuzzFlagWarningStats, "Excluded %d fuzz template[s] (disabled as default), use -fuzz option to run fuzz templates.")
|
||||
stats.NewEntry(SkippedUnsignedStats, "Skipping %d unsigned template[s]")
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package filter
|
||||
package templates
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators/common/dsl"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators/extractors"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
)
|
||||
@@ -42,7 +41,7 @@ var ErrExcluded = errors.New("the template was excluded")
|
||||
// unless it is explicitly specified by user using the includeTags (matchAllows field).
|
||||
// Matching rule: (tag1 OR tag2...) AND (author1 OR author2...) AND (severity1 OR severity2...) AND (extraTags1 OR extraTags2...)
|
||||
// Returns true if the template matches the filter criteria, false otherwise.
|
||||
func (tagFilter *TagFilter) Match(template *templates.Template, extraTags []string) (bool, error) {
|
||||
func (tagFilter *TagFilter) Match(template *Template, extraTags []string) (bool, error) {
|
||||
templateTags := template.Info.Tags.ToSlice()
|
||||
for _, templateTag := range templateTags {
|
||||
_, blocked := tagFilter.block[templateTag]
|
||||
@@ -193,7 +192,7 @@ func isIdMatch(tagFilter *TagFilter, templateId string) bool {
|
||||
return included && !excluded
|
||||
}
|
||||
|
||||
func tryCollectConditionsMatchinfo(template *templates.Template) map[string]interface{} {
|
||||
func tryCollectConditionsMatchinfo(template *Template) map[string]interface{} {
|
||||
// attempts to unwrap fields to their basic types
|
||||
// mapping must be manual because of various abstraction layers, custom marshaling and forceful validation
|
||||
parameters := map[string]interface{}{
|
||||
@@ -319,7 +318,7 @@ func collectExtractorTypes(extractors []*extractors.Extractor) []string {
|
||||
return extractorTypes
|
||||
}
|
||||
|
||||
func isConditionMatch(tagFilter *TagFilter, template *templates.Template) bool {
|
||||
func isConditionMatch(tagFilter *TagFilter, template *Template) bool {
|
||||
if len(tagFilter.includeConditions) == 0 {
|
||||
return true
|
||||
}
|
||||
@@ -348,7 +347,7 @@ func isConditionMatch(tagFilter *TagFilter, template *templates.Template) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
type TagFilterConfig struct {
|
||||
Tags []string
|
||||
ExcludeTags []string
|
||||
Authors []string
|
||||
@@ -365,7 +364,7 @@ type Config struct {
|
||||
// New returns a tag filter for nuclei tag based execution
|
||||
//
|
||||
// It takes into account Tags, Severities, ExcludeSeverities, Authors, IncludeTags, ExcludeTags, Conditions.
|
||||
func New(config *Config) (*TagFilter, error) {
|
||||
func NewTagFilter(config *TagFilterConfig) (*TagFilter, error) {
|
||||
filter := &TagFilter{
|
||||
allowedTags: make(map[string]struct{}),
|
||||
authors: make(map[string]struct{}),
|
||||
@@ -1,4 +1,4 @@
|
||||
package filter
|
||||
package templates
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -8,14 +8,13 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/stringslice"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/dns"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTagBasedFilter(t *testing.T) {
|
||||
newDummyTemplate := func(id string, tags, authors []string, severityValue severity.Severity, protocolType types.ProtocolType) *templates.Template {
|
||||
dummyTemplate := &templates.Template{}
|
||||
newDummyTemplate := func(id string, tags, authors []string, severityValue severity.Severity, protocolType types.ProtocolType) *Template {
|
||||
dummyTemplate := &Template{}
|
||||
if id != "" {
|
||||
dummyTemplate.ID = id
|
||||
}
|
||||
@@ -35,7 +34,7 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
return dummyTemplate
|
||||
}
|
||||
|
||||
filter, err := New(&Config{
|
||||
filter, err := NewTagFilter(&TagFilterConfig{
|
||||
Tags: []string{"cves", "2021", "jira"},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
@@ -62,7 +61,7 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("not-match-excludes", func(t *testing.T) {
|
||||
filter, err := New(&Config{
|
||||
filter, err := NewTagFilter(&TagFilterConfig{
|
||||
ExcludeTags: []string{"dos"},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
@@ -72,7 +71,7 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
require.Equal(t, ErrExcluded, err, "could not get correct error")
|
||||
})
|
||||
t.Run("match-includes", func(t *testing.T) {
|
||||
filter, err := New(&Config{
|
||||
filter, err := NewTagFilter(&TagFilterConfig{
|
||||
Tags: []string{"cves", "fuzz"},
|
||||
ExcludeTags: []string{"dos", "fuzz"},
|
||||
IncludeTags: []string{"fuzz"},
|
||||
@@ -84,7 +83,7 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
require.True(t, matched, "could not get correct match")
|
||||
})
|
||||
t.Run("match-includes", func(t *testing.T) {
|
||||
filter, err := New(&Config{
|
||||
filter, err := NewTagFilter(&TagFilterConfig{
|
||||
IncludeTags: []string{"fuzz"},
|
||||
ExcludeTags: []string{"fuzz"},
|
||||
})
|
||||
@@ -95,7 +94,7 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
require.True(t, matched, "could not get correct match")
|
||||
})
|
||||
t.Run("match-author", func(t *testing.T) {
|
||||
filter, err := New(&Config{
|
||||
filter, err := NewTagFilter(&TagFilterConfig{
|
||||
Authors: []string{"pdteam"},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
@@ -104,7 +103,7 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
require.True(t, matched, "could not get correct match")
|
||||
})
|
||||
t.Run("match-severity", func(t *testing.T) {
|
||||
filter, err := New(&Config{
|
||||
filter, err := NewTagFilter(&TagFilterConfig{
|
||||
Severities: severity.Severities{severity.High},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
@@ -113,7 +112,7 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
require.True(t, matched, "could not get correct match")
|
||||
})
|
||||
t.Run("match-id", func(t *testing.T) {
|
||||
filter, err := New(&Config{
|
||||
filter, err := NewTagFilter(&TagFilterConfig{
|
||||
IncludeIds: []string{"cve-test"},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
@@ -122,7 +121,7 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
require.True(t, matched, "could not get correct match")
|
||||
})
|
||||
t.Run("match-exclude-severity", func(t *testing.T) {
|
||||
filter, err := New(&Config{
|
||||
filter, err := NewTagFilter(&TagFilterConfig{
|
||||
ExcludeSeverities: severity.Severities{severity.Low},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
@@ -134,7 +133,7 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
require.False(t, matched, "could not get correct match")
|
||||
})
|
||||
t.Run("match-exclude-with-tags", func(t *testing.T) {
|
||||
filter, err := New(&Config{
|
||||
filter, err := NewTagFilter(&TagFilterConfig{
|
||||
Tags: []string{"tag"},
|
||||
ExcludeTags: []string{"another"},
|
||||
})
|
||||
@@ -144,7 +143,7 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
require.False(t, matched, "could not get correct match")
|
||||
})
|
||||
t.Run("match-conditions", func(t *testing.T) {
|
||||
filter, err := New(&Config{
|
||||
filter, err := NewTagFilter(&TagFilterConfig{
|
||||
Authors: []string{"pdteam"},
|
||||
Tags: []string{"jira"},
|
||||
Severities: severity.Severities{severity.High},
|
||||
@@ -165,7 +164,7 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
require.False(t, matched, "could not get correct match")
|
||||
})
|
||||
t.Run("match-type", func(t *testing.T) {
|
||||
filter, err := New(&Config{
|
||||
filter, err := NewTagFilter(&TagFilterConfig{
|
||||
Protocols: []types.ProtocolType{types.HTTPProtocol},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
@@ -175,7 +174,7 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
require.True(t, matched, "could not get correct match")
|
||||
})
|
||||
t.Run("match-exclude-id", func(t *testing.T) {
|
||||
filter, err := New(&Config{
|
||||
filter, err := NewTagFilter(&TagFilterConfig{
|
||||
ExcludeIds: []string{"cve-test"},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
@@ -187,7 +186,7 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
require.False(t, matched, "could not get correct match")
|
||||
})
|
||||
t.Run("match-exclude-type", func(t *testing.T) {
|
||||
filter, err := New(&Config{
|
||||
filter, err := NewTagFilter(&TagFilterConfig{
|
||||
ExcludeProtocols: []types.ProtocolType{types.HTTPProtocol},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
@@ -267,9 +266,9 @@ func TestTagBasedFilter(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func testAdvancedFiltering(t *testing.T, includeConditions []string, template *templates.Template, shouldError, shouldMatch bool) {
|
||||
func testAdvancedFiltering(t *testing.T, includeConditions []string, template *Template, shouldError, shouldMatch bool) {
|
||||
// basic properties
|
||||
advancedFilter, err := New(&Config{IncludeConditions: includeConditions})
|
||||
advancedFilter, err := NewTagFilter(&TagFilterConfig{IncludeConditions: includeConditions})
|
||||
if shouldError {
|
||||
require.NotNil(t, err)
|
||||
return
|
||||
@@ -10,11 +10,6 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/workflows"
|
||||
)
|
||||
|
||||
const (
|
||||
// Note: we redefine to avoid cyclic dependency but it should be same as parsers.SkippedUnsignedStats
|
||||
SkippedUnsignedStats = "skipped-unsigned-stats" // tracks loading of unsigned templates
|
||||
)
|
||||
|
||||
// compileWorkflow compiles the workflow for execution
|
||||
func compileWorkflow(path string, preprocessor Preprocessor, options *protocols.ExecutorOptions, workflow *workflows.Workflow, loader model.WorkflowLoader) {
|
||||
for _, workflow := range workflow.Workflows {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/parsers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/loader/workflow"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
@@ -35,8 +35,9 @@ func setup() {
|
||||
Browser: nil,
|
||||
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
|
||||
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
|
||||
Parser: templates.NewParser(),
|
||||
}
|
||||
workflowLoader, err := parsers.NewLoader(&executerOpts)
|
||||
workflowLoader, err := workflow.NewLoader(&executerOpts)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create workflow loader: %s\n", err)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/parsers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/loader/workflow"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
@@ -35,8 +35,9 @@ func setup() {
|
||||
Browser: nil,
|
||||
Catalog: disk.NewCatalog(config.DefaultConfig.TemplatesDirectory),
|
||||
RateLimiter: ratelimit.New(context.Background(), uint(options.RateLimit), time.Second),
|
||||
Parser: templates.NewParser(),
|
||||
}
|
||||
workflowLoader, err := parsers.NewLoader(&executerOpts)
|
||||
workflowLoader, err := workflow.NewLoader(&executerOpts)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create workflow loader: %s\n", err)
|
||||
}
|
||||
|
||||
+3
-24
@@ -7,10 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/yaml"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
)
|
||||
|
||||
func IsBlank(value string) bool {
|
||||
@@ -35,38 +32,20 @@ func IsURL(input string) bool {
|
||||
}
|
||||
|
||||
// ReadFromPathOrURL reads and returns the contents of a file or url.
|
||||
func ReadFromPathOrURL(templatePath string, catalog catalog.Catalog) (data []byte, err error) {
|
||||
var reader io.Reader
|
||||
func ReaderFromPathOrURL(templatePath string, catalog catalog.Catalog) (io.ReadCloser, error) {
|
||||
if IsURL(templatePath) {
|
||||
resp, err := retryablehttp.DefaultClient().Get(templatePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
reader = resp.Body
|
||||
return resp.Body, nil
|
||||
} else {
|
||||
f, err := catalog.OpenFile(templatePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
reader = f
|
||||
return f, nil
|
||||
}
|
||||
|
||||
data, err = io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// pre-process directives only for local files
|
||||
if fileutil.FileExists(templatePath) && config.GetTemplateFormatFromExt(templatePath) == config.YAML {
|
||||
data, err = yaml.PreProcess(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// StringSliceContains checks if a string slice contains a string.
|
||||
|
||||
Reference in new issue
Block a user