mirror of https://github.com/daffainfo/nuclei.git
commit
5398c40b0d
21
v2/go.mod
21
v2/go.mod
|
@ -5,31 +5,26 @@ go 1.14
|
|||
require (
|
||||
github.com/Knetic/govaluate v3.0.0+incompatible
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/coocood/freecache v1.1.1 // indirect
|
||||
github.com/d5/tengo/v2 v2.6.2
|
||||
github.com/google/go-github/v32 v32.1.0
|
||||
github.com/json-iterator/go v1.1.10
|
||||
github.com/karrick/godirwalk v1.16.1
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/miekg/dns v1.1.34
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/miekg/dns v1.1.35
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/projectdiscovery/collaborator v0.0.0-20201023080839-2aa1290ed09d
|
||||
github.com/projectdiscovery/clistats v0.0.5
|
||||
github.com/projectdiscovery/collaborator v0.0.1
|
||||
github.com/projectdiscovery/fastdialer v0.0.1
|
||||
github.com/projectdiscovery/gologger v1.0.1
|
||||
github.com/projectdiscovery/hmap v0.0.0-20201018163424-9cdfe6188601
|
||||
github.com/projectdiscovery/httpx v1.0.2
|
||||
github.com/projectdiscovery/hmap v0.0.1
|
||||
github.com/projectdiscovery/rawhttp v0.0.4
|
||||
github.com/projectdiscovery/retryabledns v1.0.4
|
||||
github.com/projectdiscovery/retryabledns v1.0.5
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.1
|
||||
github.com/remeh/sizedwaitgroup v1.0.0
|
||||
github.com/segmentio/ksuid v1.0.3
|
||||
github.com/spaolacci/murmur3 v1.1.0
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/vbauerster/mpb/v5 v5.3.0
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/ratelimit v0.1.0
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect
|
||||
golang.org/x/net v0.0.0-20201022231255-08b38378de70
|
||||
golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd // indirect
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
)
|
||||
|
|
82
v2/go.sum
82
v2/go.sum
|
@ -1,38 +1,27 @@
|
|||
github.com/Knetic/govaluate v1.5.0 h1:L4MyqdJSld9xr2eZcZHCWLfeIX2SBjqrwIKG1pcm/+4=
|
||||
github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg=
|
||||
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
|
||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||
github.com/blang/semver v1.1.0 h1:ol1rO7QQB5uy7umSNV7VAmLugfLRD+17sYJujRNYPhg=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/coocood/freecache v1.1.0 h1:ENiHOsWdj1BrrlPwblhbn4GdAsMymK3pZORJ+bJGAjA=
|
||||
github.com/coocood/freecache v1.1.0/go.mod h1:ePwxCDzOYvARfHdr1pByNct1at3CoKnsipOHwKlNbzI=
|
||||
github.com/coocood/freecache v1.1.1 h1:uukNF7QKCZEdZ9gAV7WQzvh0SbjwdMF6m3x3rxEkaPc=
|
||||
github.com/coocood/freecache v1.1.1/go.mod h1:OKrEjkGVoxZhyWAJoeFi5BMLUJm2Tit0kpGkIr7NGYY=
|
||||
github.com/d5/tengo v1.24.8 h1:PRJ+NWt7ae/9sSbIfThOBTkPSvNV+dwYoBAvwfNgNJY=
|
||||
github.com/d5/tengo/v2 v2.6.2 h1:AnPhA/Y5qrNLb5QSWHU9uXq25T3QTTdd2waTgsAHMdc=
|
||||
github.com/d5/tengo/v2 v2.6.2/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
|
||||
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/hbakhtiyor/strsim v0.0.0-20190107154042-4d2bbb273edf/go.mod h1:V99KdStnMHZsvVOwIvhfcUzYgYkRZeQWUtumtL+SKxA=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
|
@ -41,48 +30,40 @@ github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1q
|
|||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
|
||||
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.34 h1:SgTzfkN+oLoIHF1bgUP+C71mzuDl3AhLApHzCCIAMWM=
|
||||
github.com/miekg/dns v1.1.34/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
|
||||
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/projectdiscovery/collaborator v0.0.0-20201023080839-2aa1290ed09d h1:iHb2v6VX1Fjl9IX8UaJFLKLej+KyJJ59W1pWIRCug4Q=
|
||||
github.com/projectdiscovery/collaborator v0.0.0-20201023080839-2aa1290ed09d/go.mod h1:M7Csn+hQVDOLCEEFkj6dazmtgG1tIqJpbuPHlRlpYGQ=
|
||||
github.com/projectdiscovery/gologger v1.0.0/go.mod h1:Ok+axMqK53bWNwDSU1nTNwITLYMXMdZtRc8/y1c7sWE=
|
||||
github.com/projectdiscovery/clistats v0.0.5 h1:vcvOR9PrFRawO/7FWD6pER9nYVSoSTD2F+/fkRs73a0=
|
||||
github.com/projectdiscovery/clistats v0.0.5/go.mod h1:lV6jUHAv2bYWqrQstqW8iVIydKJhWlVaLl3Xo9ioVGg=
|
||||
github.com/projectdiscovery/collaborator v0.0.1 h1:dbQ5BCL/a3c+BB9cGtrGgiLs23+EfSzoaTzX/pxqiTI=
|
||||
github.com/projectdiscovery/collaborator v0.0.1/go.mod h1:J1z0fC7Svutz3LJqoRyTHA3F0Suh4livmkYv8MnKw20=
|
||||
github.com/projectdiscovery/fastdialer v0.0.1 h1:MgBkJ/zkciFu/PcbAz0DYGiZn2aqv6b39NvfXxfN8qg=
|
||||
github.com/projectdiscovery/fastdialer v0.0.1/go.mod h1:d24GUzSb93wOY7lu4gJmXAzfomqAGEcRrInEVrM6zbc=
|
||||
github.com/projectdiscovery/gologger v1.0.1 h1:FzoYQZnxz9DCvSi/eg5A6+ET4CQ0CDUs27l6Exr8zMQ=
|
||||
github.com/projectdiscovery/gologger v1.0.1/go.mod h1:Ok+axMqK53bWNwDSU1nTNwITLYMXMdZtRc8/y1c7sWE=
|
||||
github.com/projectdiscovery/hmap v0.0.0-20201018163424-9cdfe6188601 h1:08fefqfMG8xmhexCYWC2R9XGIkGoSgVEHhBBKkdY24k=
|
||||
github.com/projectdiscovery/hmap v0.0.0-20201018163424-9cdfe6188601/go.mod h1:VDEfgzkKQdq7iGTKz8Ooul0NuYHQ8qiDs6r8bPD1Sb0=
|
||||
github.com/projectdiscovery/httpx v1.0.2 h1:g7EeRAPckZgWcHkcAH2Qzv9MkRACVRLF+T2LJcM7SCk=
|
||||
github.com/projectdiscovery/httpx v1.0.2/go.mod h1:OwvMc5ogx69xukKXY6kIrDP6dgOYr4VtEWyr6o573Xs=
|
||||
github.com/projectdiscovery/hmap v0.0.1 h1:VAONbJw5jP+syI5smhsfkrq9XPGn4aiYy5pR6KR1wog=
|
||||
github.com/projectdiscovery/hmap v0.0.1/go.mod h1:VDEfgzkKQdq7iGTKz8Ooul0NuYHQ8qiDs6r8bPD1Sb0=
|
||||
github.com/projectdiscovery/rawhttp v0.0.4 h1:O5IreNGk83d4xTD9e6SpkKbX0sHTs8K1Q33Bz4eYl2E=
|
||||
github.com/projectdiscovery/rawhttp v0.0.4/go.mod h1:PQERZAhAv7yxI/hR6hdDPgK1WTU56l204BweXrBec+0=
|
||||
github.com/projectdiscovery/retryabledns v1.0.4 h1:0Va7qHlWQsIXjRLISTjzfN3tnJmHYDudY05Nu3IJd60=
|
||||
github.com/projectdiscovery/retryabledns v1.0.4/go.mod h1:/UzJn4I+cPdQl6pKiiQfvVAT636YZvJQYZhYhGB0dUQ=
|
||||
github.com/projectdiscovery/retryabledns v1.0.5 h1:bQivGy5CuqKlwcxRkgA5ENincqIed/BR2sA6t2gdwuI=
|
||||
github.com/projectdiscovery/retryabledns v1.0.5/go.mod h1:/UzJn4I+cPdQl6pKiiQfvVAT636YZvJQYZhYhGB0dUQ=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.1 h1:V7wUvsZNq1Rcz7+IlcyoyQlNwshuwptuBVYWw9lx8RE=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.1/go.mod h1:SrN6iLZilNG1X4neq1D+SBxoqfAF4nyzvmevkTkWsek=
|
||||
github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7Kyl5E=
|
||||
github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/segmentio/ksuid v1.0.3 h1:FoResxvleQwYiPAVKe1tMUlEirodZqlqglIuFsdDntY=
|
||||
github.com/segmentio/ksuid v1.0.3/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
@ -91,8 +72,6 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
|
|||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/vbauerster/mpb/v5 v5.3.0 h1:vgrEJjUzHaSZKDRRxul5Oh4C72Yy/5VEMb0em+9M0mQ=
|
||||
github.com/vbauerster/mpb/v5 v5.3.0/go.mod h1:4yTkvAb8Cm4eylAp6t0JRq6pXDkFJ4krUlDqWYkakAs=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/ratelimit v0.1.0 h1:U2AruXqeTb4Eh9sYQSTrMhH8Cb7M0Ian2ibBOnBcnAw=
|
||||
|
@ -100,43 +79,36 @@ go.uber.org/ratelimit v0.1.0/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU=
|
||||
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY=
|
||||
golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd h1:WgqgiQvkiZWz7XLhphjt2GI2GcGCTIZs9jqXMWmH+oc=
|
||||
golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201113233024-12cec1faf1ba h1:xmhUJGQGbxlod18iJGqVEp9cHIPLl7QiX2aA3to708s=
|
||||
golang.org/x/sys v0.0.0-20201113233024-12cec1faf1ba/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
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/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
// Tracks enumeration progress information and implements visible tracking with one or more progress bars.
|
||||
// Package progress implements progress display mechanism with very
|
||||
// simple command line statistics printing on runtime.
|
||||
package progress
|
||||
|
|
|
@ -2,231 +2,145 @@ package progress
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/projectdiscovery/clistats"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/vbauerster/mpb/v5"
|
||||
"github.com/vbauerster/mpb/v5/decor"
|
||||
)
|
||||
|
||||
const (
|
||||
// global output refresh rate
|
||||
refreshHz = 8
|
||||
settleMilis = 250
|
||||
mili = 1000.
|
||||
)
|
||||
|
||||
// IProgress encapsulates progress tracking.
|
||||
type IProgress interface {
|
||||
InitProgressbar(hostCount int64, templateCount int, requestCount int64)
|
||||
AddToTotal(delta int64)
|
||||
Update()
|
||||
Drop(count int64)
|
||||
Wait()
|
||||
}
|
||||
|
||||
// Progress is a progress instance for showing program stats
|
||||
type Progress struct {
|
||||
progress *mpb.Progress
|
||||
bar *mpb.Bar
|
||||
total int64
|
||||
initialTotal int64
|
||||
|
||||
totalMutex *sync.Mutex
|
||||
colorizer *aurora.Aurora
|
||||
|
||||
renderChan chan time.Time
|
||||
captureData *captureData
|
||||
stdCaptureMutex *sync.Mutex
|
||||
stdOut *strings.Builder
|
||||
stdErr *strings.Builder
|
||||
stdStopRenderEvent chan bool
|
||||
stdRenderEvent *time.Ticker
|
||||
stdRenderWaitGroup *sync.WaitGroup
|
||||
active bool
|
||||
stats clistats.StatisticsClient
|
||||
tickDuration time.Duration
|
||||
}
|
||||
|
||||
// NewProgress creates and returns a new progress tracking object.
|
||||
func NewProgress(colorizer aurora.Aurora, active bool) IProgress {
|
||||
if !active {
|
||||
return &NoOpProgress{}
|
||||
func NewProgress(active bool) *Progress {
|
||||
var tickDuration time.Duration
|
||||
if active {
|
||||
tickDuration = 5 * time.Second
|
||||
} else {
|
||||
tickDuration = -1
|
||||
}
|
||||
|
||||
refreshMillis := int64(1. / float64(refreshHz) * mili)
|
||||
|
||||
renderChan := make(chan time.Time)
|
||||
p := &Progress{
|
||||
progress: mpb.New(
|
||||
mpb.WithOutput(os.Stderr),
|
||||
mpb.PopCompletedMode(),
|
||||
mpb.WithManualRefresh(renderChan),
|
||||
),
|
||||
totalMutex: &sync.Mutex{},
|
||||
colorizer: &colorizer,
|
||||
|
||||
renderChan: renderChan,
|
||||
stdCaptureMutex: &sync.Mutex{},
|
||||
stdOut: &strings.Builder{},
|
||||
stdErr: &strings.Builder{},
|
||||
stdStopRenderEvent: make(chan bool),
|
||||
stdRenderEvent: time.NewTicker(time.Millisecond * time.Duration(refreshMillis)),
|
||||
stdRenderWaitGroup: &sync.WaitGroup{},
|
||||
var progress Progress
|
||||
if active {
|
||||
stats, err := clistats.New()
|
||||
if err != nil {
|
||||
gologger.Warningf("Couldn't create progress engine: %s\n", err)
|
||||
}
|
||||
progress.active = active
|
||||
progress.stats = stats
|
||||
progress.tickDuration = tickDuration
|
||||
}
|
||||
|
||||
return p
|
||||
return &progress
|
||||
}
|
||||
|
||||
// Creates and returns a progress bar that tracks all the progress.
|
||||
func (p *Progress) InitProgressbar(hostCount int64, rulesCount int, requestCount int64) {
|
||||
if p.bar != nil {
|
||||
panic("A global progressbar is already present.")
|
||||
// Init initializes the progress display mechanism by setting counters, etc.
|
||||
func (p *Progress) Init(hostCount int64, rulesCount int, requestCount int64) {
|
||||
if p.active {
|
||||
p.stats.AddStatic("templates", rulesCount)
|
||||
p.stats.AddStatic("hosts", hostCount)
|
||||
p.stats.AddStatic("startedAt", time.Now())
|
||||
p.stats.AddCounter("requests", uint64(0))
|
||||
p.stats.AddCounter("errors", uint64(0))
|
||||
p.stats.AddCounter("total", uint64(requestCount))
|
||||
if err := p.stats.Start(makePrintCallback(), p.tickDuration); err != nil {
|
||||
gologger.Warningf("Couldn't start statistics: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
color := *p.colorizer
|
||||
|
||||
barName := color.Sprintf(
|
||||
color.Cyan("%d %s, %d %s"),
|
||||
color.Bold(color.Cyan(rulesCount)),
|
||||
pluralize(int64(rulesCount), "rule", "rules"),
|
||||
color.Bold(color.Cyan(hostCount)),
|
||||
pluralize(hostCount, "host", "hosts"))
|
||||
|
||||
p.bar = p.setupProgressbar("["+barName+"]", requestCount, 0)
|
||||
|
||||
// creates r/w pipes and divert stdout/stderr writers to them and start capturing their output
|
||||
p.captureData = startCapture(p.stdCaptureMutex, p.stdOut, p.stdErr)
|
||||
|
||||
// starts rendering both the progressbar and the captured stdout/stderr data
|
||||
p.renderStdData()
|
||||
}
|
||||
|
||||
// Update total progress request count
|
||||
// AddToTotal adds a value to the total request count
|
||||
func (p *Progress) AddToTotal(delta int64) {
|
||||
p.totalMutex.Lock()
|
||||
p.total += delta
|
||||
p.bar.SetTotal(p.total, false)
|
||||
p.totalMutex.Unlock()
|
||||
if p.active {
|
||||
p.stats.IncrementCounter("total", int(delta))
|
||||
}
|
||||
}
|
||||
|
||||
// Update progress tracking information and increments the request counter by one unit.
|
||||
func (p *Progress) Update() {
|
||||
p.bar.Increment()
|
||||
if p.active {
|
||||
p.stats.IncrementCounter("requests", 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Drops the specified number of requests from the progress bar total.
|
||||
// Drop drops the specified number of requests from the progress bar total.
|
||||
// This may be the case when uncompleted requests are encountered and shouldn't be part of the total count.
|
||||
func (p *Progress) Drop(count int64) {
|
||||
// mimic dropping by incrementing the completed requests
|
||||
p.bar.IncrInt64(count)
|
||||
}
|
||||
|
||||
// Ensures that a progress bar's total count is up-to-date if during an enumeration there were uncompleted requests and
|
||||
// wait for all the progress bars to finish.
|
||||
func (p *Progress) Wait() {
|
||||
p.totalMutex.Lock()
|
||||
if p.total == 0 {
|
||||
p.bar.Abort(true)
|
||||
} else if p.initialTotal != p.total {
|
||||
p.bar.SetTotal(p.total, true)
|
||||
if p.active {
|
||||
// mimic dropping by incrementing the completed requests
|
||||
p.stats.IncrementCounter("errors", int(count))
|
||||
}
|
||||
p.totalMutex.Unlock()
|
||||
p.progress.Wait()
|
||||
|
||||
// close the writers and wait for the EOF condition
|
||||
stopCapture(p.captureData)
|
||||
|
||||
// stop the renderer and wait for it
|
||||
p.stdStopRenderEvent <- true
|
||||
p.stdRenderWaitGroup.Wait()
|
||||
|
||||
// drain any stdout/stderr data
|
||||
p.drainStringBuilderTo(p.stdOut, os.Stdout)
|
||||
p.drainStringBuilderTo(p.stdErr, os.Stderr)
|
||||
}
|
||||
|
||||
func (p *Progress) renderStdData() {
|
||||
// trigger a render event
|
||||
p.renderChan <- time.Now()
|
||||
const bufferSize = 128
|
||||
|
||||
gologger.Infof("Waiting for your terminal to settle..")
|
||||
time.Sleep(time.Millisecond * settleMilis)
|
||||
func makePrintCallback() func(stats clistats.StatisticsClient) {
|
||||
builder := &strings.Builder{}
|
||||
builder.Grow(bufferSize)
|
||||
|
||||
p.stdRenderWaitGroup.Add(1)
|
||||
return func(stats clistats.StatisticsClient) {
|
||||
builder.WriteRune('[')
|
||||
startedAt, _ := stats.GetStatic("startedAt")
|
||||
duration := time.Since(startedAt.(time.Time))
|
||||
builder.WriteString(fmtDuration(duration))
|
||||
builder.WriteRune(']')
|
||||
|
||||
go func(waitGroup *sync.WaitGroup) {
|
||||
for {
|
||||
select {
|
||||
case <-p.stdStopRenderEvent:
|
||||
waitGroup.Done()
|
||||
return
|
||||
case <-p.stdRenderEvent.C:
|
||||
p.stdCaptureMutex.Lock()
|
||||
{
|
||||
hasStdout := p.stdOut.Len() > 0
|
||||
hasStderr := p.stdErr.Len() > 0
|
||||
hasOutput := hasStdout || hasStderr
|
||||
templates, _ := stats.GetStatic("templates")
|
||||
builder.WriteString(" | Templates: ")
|
||||
builder.WriteString(clistats.String(templates))
|
||||
hosts, _ := stats.GetStatic("hosts")
|
||||
builder.WriteString(" | Hosts: ")
|
||||
builder.WriteString(clistats.String(hosts))
|
||||
|
||||
if hasOutput {
|
||||
stdout := p.captureData.backupStdout
|
||||
stderr := p.captureData.backupStderr
|
||||
requests, _ := stats.GetCounter("requests")
|
||||
total, _ := stats.GetCounter("total")
|
||||
|
||||
// go back one line and clean it all
|
||||
fmt.Fprint(stderr, "\u001b[1A\u001b[2K")
|
||||
p.drainStringBuilderTo(p.stdOut, stdout)
|
||||
p.drainStringBuilderTo(p.stdErr, stderr)
|
||||
builder.WriteString(" | RPS: ")
|
||||
builder.WriteString(clistats.String(uint64(float64(requests) / duration.Seconds())))
|
||||
|
||||
// make space for the progressbar to render itself
|
||||
fmt.Fprintln(stderr, "")
|
||||
}
|
||||
errors, _ := stats.GetCounter("errors")
|
||||
builder.WriteString(" | Errors: ")
|
||||
builder.WriteString(clistats.String(errors))
|
||||
|
||||
// always trigger a render event to try ensure it's visible even with fast output
|
||||
p.renderChan <- time.Now()
|
||||
}
|
||||
p.stdCaptureMutex.Unlock()
|
||||
}
|
||||
}
|
||||
}(p.stdRenderWaitGroup)
|
||||
}
|
||||
builder.WriteString(" | Requests: ")
|
||||
builder.WriteString(clistats.String(requests))
|
||||
builder.WriteRune('/')
|
||||
builder.WriteString(clistats.String(total))
|
||||
builder.WriteRune(' ')
|
||||
builder.WriteRune('(')
|
||||
//nolint:gomnd // this is not a magic number
|
||||
builder.WriteString(clistats.String(uint64(float64(requests) / float64(total) * 100.0)))
|
||||
builder.WriteRune('%')
|
||||
builder.WriteRune(')')
|
||||
builder.WriteRune('\n')
|
||||
|
||||
// Creates and returns a progress bar.
|
||||
func (p *Progress) setupProgressbar(name string, total int64, priority int) *mpb.Bar {
|
||||
color := *p.colorizer
|
||||
|
||||
p.total = total
|
||||
p.initialTotal = total
|
||||
|
||||
return p.progress.AddBar(
|
||||
total,
|
||||
mpb.BarPriority(priority),
|
||||
mpb.BarNoPop(),
|
||||
mpb.BarRemoveOnComplete(),
|
||||
mpb.PrependDecorators(
|
||||
decor.Name(name, decor.WCSyncSpaceR),
|
||||
decor.CountersNoUnit(color.BrightBlue(" %d/%d").String(), decor.WCSyncSpace),
|
||||
decor.NewPercentage(color.Bold("%d").String(), decor.WCSyncSpace),
|
||||
),
|
||||
mpb.AppendDecorators(
|
||||
decor.AverageSpeed(0, color.BrightYellow("%.2f").Bold().String()+color.BrightYellow("r/s").String(), decor.WCSyncSpace),
|
||||
decor.Elapsed(decor.ET_STYLE_GO, decor.WCSyncSpace),
|
||||
decor.AverageETA(decor.ET_STYLE_GO, decor.WCSyncSpace),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func pluralize(count int64, singular, plural string) string {
|
||||
if count > 1 {
|
||||
return plural
|
||||
}
|
||||
|
||||
return singular
|
||||
}
|
||||
|
||||
func (p *Progress) drainStringBuilderTo(builder *strings.Builder, writer io.Writer) {
|
||||
if builder.Len() > 0 {
|
||||
fmt.Fprint(writer, builder.String())
|
||||
fmt.Fprintf(os.Stderr, "%s", builder.String())
|
||||
builder.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
// fmtDuration formats the duration for the time elapsed
|
||||
func fmtDuration(d time.Duration) string {
|
||||
d = d.Round(time.Second)
|
||||
h := d / time.Hour
|
||||
d -= h * time.Hour
|
||||
m := d / time.Minute
|
||||
d -= m * time.Minute
|
||||
s := d / time.Second
|
||||
return fmt.Sprintf("%d:%02d:%02d", h, m, s)
|
||||
}
|
||||
|
||||
// Stop stops the progress bar execution
|
||||
func (p *Progress) Stop() {
|
||||
if p.active {
|
||||
if err := p.stats.Stop(); err != nil {
|
||||
gologger.Warningf("Couldn't stop statistics: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
package progress
|
||||
|
||||
type NoOpProgress struct{}
|
||||
|
||||
func (p *NoOpProgress) InitProgressbar(hostCount int64, templateCount int, requestCount int64) {}
|
||||
func (p *NoOpProgress) AddToTotal(delta int64) {}
|
||||
func (p *NoOpProgress) Update() {}
|
||||
func (p *NoOpProgress) Drop(count int64) {}
|
||||
func (p *NoOpProgress) Wait() {}
|
|
@ -1,102 +0,0 @@
|
|||
package progress
|
||||
|
||||
/**
|
||||
Inspired by the https://github.com/PumpkinSeed/cage module
|
||||
*/
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
)
|
||||
|
||||
const (
|
||||
fourMegas = 4 * 1024
|
||||
two = 2
|
||||
)
|
||||
|
||||
type captureData struct {
|
||||
backupStdout *os.File
|
||||
writerStdout *os.File
|
||||
backupStderr *os.File
|
||||
writerStderr *os.File
|
||||
waitFinishRead *sync.WaitGroup
|
||||
}
|
||||
|
||||
func startCapture(writeLocker sync.Locker, stdout, stderr *strings.Builder) *captureData {
|
||||
rStdout, wStdout, errStdout := os.Pipe()
|
||||
if errStdout != nil {
|
||||
panic(errStdout)
|
||||
}
|
||||
|
||||
rStderr, wStderr, errStderr := os.Pipe()
|
||||
if errStderr != nil {
|
||||
panic(errStderr)
|
||||
}
|
||||
|
||||
c := &captureData{
|
||||
backupStdout: os.Stdout,
|
||||
writerStdout: wStdout,
|
||||
|
||||
backupStderr: os.Stderr,
|
||||
writerStderr: wStderr,
|
||||
|
||||
waitFinishRead: &sync.WaitGroup{},
|
||||
}
|
||||
|
||||
os.Stdout = c.writerStdout
|
||||
os.Stderr = c.writerStderr
|
||||
|
||||
stdCopy := func(builder *strings.Builder, reader *os.File, waitGroup *sync.WaitGroup) {
|
||||
r := bufio.NewReader(reader)
|
||||
buf := make([]byte, 0, fourMegas)
|
||||
|
||||
for {
|
||||
n, err := r.Read(buf[:cap(buf)])
|
||||
buf = buf[:n]
|
||||
|
||||
if n == 0 {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if err == io.EOF {
|
||||
waitGroup.Done()
|
||||
break
|
||||
}
|
||||
|
||||
waitGroup.Done()
|
||||
gologger.Fatalf("stdcapture error: %s", err)
|
||||
}
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
waitGroup.Done()
|
||||
gologger.Fatalf("stdcapture error: %s", err)
|
||||
}
|
||||
|
||||
writeLocker.Lock()
|
||||
builder.Write(buf)
|
||||
writeLocker.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
c.waitFinishRead.Add(two)
|
||||
|
||||
go stdCopy(stdout, rStdout, c.waitFinishRead)
|
||||
go stdCopy(stderr, rStderr, c.waitFinishRead)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func stopCapture(c *captureData) {
|
||||
_ = c.writerStdout.Close()
|
||||
_ = c.writerStderr.Close()
|
||||
|
||||
c.waitFinishRead.Wait()
|
||||
|
||||
os.Stdout = c.backupStdout
|
||||
os.Stderr = c.backupStderr
|
||||
}
|
|
@ -76,7 +76,7 @@ func ParseOptions() *Options {
|
|||
flag.BoolVar(&options.Silent, "silent", false, "Show only results in output")
|
||||
flag.BoolVar(&options.Version, "version", false, "Show version of nuclei")
|
||||
flag.BoolVar(&options.Verbose, "v", false, "Show Verbose output")
|
||||
flag.BoolVar(&options.NoColor, "nC", false, "Don't Use colors in output")
|
||||
flag.BoolVar(&options.NoColor, "no-color", false, "Disable colors in output")
|
||||
flag.IntVar(&options.Timeout, "timeout", 5, "Time to wait in seconds before timeout")
|
||||
flag.IntVar(&options.Retries, "retries", 1, "Number of times to retry a failed request")
|
||||
flag.Var(&options.CustomHeaders, "H", "Custom Header.")
|
||||
|
@ -85,10 +85,10 @@ func ParseOptions() *Options {
|
|||
flag.StringVar(&options.TraceLogFile, "trace-log", "", "File to write sent requests trace log")
|
||||
flag.StringVar(&options.TemplatesDirectory, "update-directory", "", "Directory to use for storing nuclei-templates")
|
||||
flag.BoolVar(&options.JSON, "json", false, "Write json output to files")
|
||||
flag.BoolVar(&options.JSONRequests, "json-requests", false, "Write requests/responses for matches in JSON output")
|
||||
flag.BoolVar(&options.EnableProgressBar, "pbar", false, "Enable the progress bar")
|
||||
flag.BoolVar(&options.JSONRequests, "include-rr", false, "Write requests/responses for matches in JSON output")
|
||||
flag.BoolVar(&options.EnableProgressBar, "stats", false, "Display stats of the running scan")
|
||||
flag.BoolVar(&options.TemplateList, "tl", false, "List available templates")
|
||||
flag.IntVar(&options.RateLimit, "rate-limit", 150, "Rate-Limit Per Target (maximum requests/second")
|
||||
flag.IntVar(&options.RateLimit, "rate-limit", 150, "Rate-Limit (maximum requests/second")
|
||||
flag.BoolVar(&options.StopAtFirstMatch, "stop-at-first-match", false, "Stop processing http requests at first match (this may break template/workflow logic)")
|
||||
flag.IntVar(&options.BulkSize, "bulk-size", 25, "Maximum Number of hosts analyzed in parallel per template")
|
||||
flag.IntVar(&options.TemplateThreads, "c", 10, "Maximum Number of templates executed in parallel")
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package runner
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http/cookiejar"
|
||||
|
@ -30,7 +29,7 @@ type workflowTemplates struct {
|
|||
}
|
||||
|
||||
// processTemplateWithList processes a template and runs the enumeration on all the targets
|
||||
func (r *Runner) processTemplateWithList(p progress.IProgress, template *templates.Template, request interface{}) bool {
|
||||
func (r *Runner) processTemplateWithList(p *progress.Progress, template *templates.Template, request interface{}) bool {
|
||||
var httpExecuter *executer.HTTPExecuter
|
||||
var dnsExecuter *executer.DNSExecuter
|
||||
var err error
|
||||
|
@ -50,6 +49,7 @@ func (r *Runner) processTemplateWithList(p progress.IProgress, template *templat
|
|||
ColoredOutput: !r.options.NoColor,
|
||||
Colorizer: r.colorizer,
|
||||
Decolorizer: r.decolorizer,
|
||||
RateLimiter: r.ratelimiter,
|
||||
})
|
||||
case *requests.BulkHTTPRequest:
|
||||
httpExecuter, err = executer.NewHTTPExecuter(&executer.HTTPOptions{
|
||||
|
@ -72,7 +72,8 @@ func (r *Runner) processTemplateWithList(p progress.IProgress, template *templat
|
|||
Decolorizer: r.decolorizer,
|
||||
StopAtFirstMatch: r.options.StopAtFirstMatch,
|
||||
PF: r.pf,
|
||||
Dialer: &r.dialer,
|
||||
Dialer: r.dialer,
|
||||
RateLimiter: r.ratelimiter,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -87,9 +88,8 @@ func (r *Runner) processTemplateWithList(p progress.IProgress, template *templat
|
|||
|
||||
wg := sizedwaitgroup.New(r.options.BulkSize)
|
||||
|
||||
scanner := bufio.NewScanner(strings.NewReader(r.input))
|
||||
for scanner.Scan() {
|
||||
URL := scanner.Text()
|
||||
r.hm.Scan(func(k, _ []byte) error {
|
||||
URL := string(k)
|
||||
wg.Add()
|
||||
go func(URL string) {
|
||||
defer wg.Done()
|
||||
|
@ -110,7 +110,9 @@ func (r *Runner) processTemplateWithList(p progress.IProgress, template *templat
|
|||
gologger.Warningf("[%s] Could not execute step: %s\n", r.colorizer.Colorizer.BrightBlue(template.ID), result.Error)
|
||||
}
|
||||
}(URL)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
wg.Wait()
|
||||
|
||||
|
@ -119,13 +121,12 @@ func (r *Runner) processTemplateWithList(p progress.IProgress, template *templat
|
|||
}
|
||||
|
||||
// ProcessWorkflowWithList coming from stdin or list of targets
|
||||
func (r *Runner) processWorkflowWithList(p progress.IProgress, workflow *workflows.Workflow) bool {
|
||||
func (r *Runner) processWorkflowWithList(p *progress.Progress, workflow *workflows.Workflow) bool {
|
||||
result := false
|
||||
|
||||
workflowTemplatesList, err := r.preloadWorkflowTemplates(p, workflow)
|
||||
if err != nil {
|
||||
gologger.Warningf("Could not preload templates for workflow %s: %s\n", workflow.ID, err)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -133,9 +134,8 @@ func (r *Runner) processWorkflowWithList(p progress.IProgress, workflow *workflo
|
|||
|
||||
wg := sizedwaitgroup.New(r.options.BulkSize)
|
||||
|
||||
scanner := bufio.NewScanner(strings.NewReader(r.input))
|
||||
for scanner.Scan() {
|
||||
targetURL := scanner.Text()
|
||||
r.hm.Scan(func(k, _ []byte) error {
|
||||
targetURL := string(k)
|
||||
wg.Add()
|
||||
|
||||
go func(targetURL string) {
|
||||
|
@ -152,7 +152,6 @@ func (r *Runner) processWorkflowWithList(p progress.IProgress, workflow *workflo
|
|||
err := script.Add(name, variable)
|
||||
if err != nil {
|
||||
gologger.Errorf("Could not initialize script for workflow '%s': %s\n", workflow.ID, err)
|
||||
|
||||
continue
|
||||
}
|
||||
variables[name] = variable
|
||||
|
@ -170,20 +169,20 @@ func (r *Runner) processWorkflowWithList(p progress.IProgress, workflow *workflo
|
|||
}
|
||||
}
|
||||
}(targetURL)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (r *Runner) preloadWorkflowTemplates(p progress.IProgress, workflow *workflows.Workflow) (*[]workflowTemplates, error) {
|
||||
func (r *Runner) preloadWorkflowTemplates(p *progress.Progress, workflow *workflows.Workflow) (*[]workflowTemplates, error) {
|
||||
var jar *cookiejar.Jar
|
||||
|
||||
if workflow.CookieReuse {
|
||||
var err error
|
||||
jar, err = cookiejar.New(nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -218,22 +217,26 @@ func (r *Runner) preloadWorkflowTemplates(p progress.IProgress, workflow *workfl
|
|||
template := &workflows.Template{Progress: p}
|
||||
if len(t.BulkRequestsHTTP) > 0 {
|
||||
template.HTTPOptions = &executer.HTTPOptions{
|
||||
TraceLog: r.traceLog,
|
||||
Debug: r.options.Debug,
|
||||
Writer: r.output,
|
||||
Template: t,
|
||||
Timeout: r.options.Timeout,
|
||||
Retries: r.options.Retries,
|
||||
ProxyURL: r.options.ProxyURL,
|
||||
ProxySocksURL: r.options.ProxySocksURL,
|
||||
CustomHeaders: r.options.CustomHeaders,
|
||||
JSON: r.options.JSON,
|
||||
JSONRequests: r.options.JSONRequests,
|
||||
CookieJar: jar,
|
||||
ColoredOutput: !r.options.NoColor,
|
||||
Colorizer: &r.colorizer,
|
||||
Decolorizer: r.decolorizer,
|
||||
PF: r.pf,
|
||||
TraceLog: r.traceLog,
|
||||
Debug: r.options.Debug,
|
||||
Writer: r.output,
|
||||
Template: t,
|
||||
Timeout: r.options.Timeout,
|
||||
Retries: r.options.Retries,
|
||||
ProxyURL: r.options.ProxyURL,
|
||||
ProxySocksURL: r.options.ProxySocksURL,
|
||||
CustomHeaders: r.options.CustomHeaders,
|
||||
JSON: r.options.JSON,
|
||||
JSONRequests: r.options.JSONRequests,
|
||||
CookieJar: jar,
|
||||
ColoredOutput: !r.options.NoColor,
|
||||
Colorizer: &r.colorizer,
|
||||
Decolorizer: r.decolorizer,
|
||||
PF: r.pf,
|
||||
RateLimiter: r.ratelimiter,
|
||||
NoMeta: r.options.NoMeta,
|
||||
StopAtFirstMatch: r.options.StopAtFirstMatch,
|
||||
Dialer: r.dialer,
|
||||
}
|
||||
} else if len(t.RequestsDNS) > 0 {
|
||||
template.DNSOptions = &executer.DNSOptions{
|
||||
|
@ -246,6 +249,8 @@ func (r *Runner) preloadWorkflowTemplates(p progress.IProgress, workflow *workfl
|
|||
ColoredOutput: !r.options.NoColor,
|
||||
Colorizer: r.colorizer,
|
||||
Decolorizer: r.decolorizer,
|
||||
NoMeta: r.options.NoMeta,
|
||||
RateLimiter: r.ratelimiter,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,12 +300,14 @@ func (r *Runner) preloadWorkflowTemplates(p progress.IProgress, workflow *workfl
|
|||
ProxySocksURL: r.options.ProxySocksURL,
|
||||
CustomHeaders: r.options.CustomHeaders,
|
||||
CookieJar: jar,
|
||||
TraceLog: r.traceLog,
|
||||
}
|
||||
} else if len(t.RequestsDNS) > 0 {
|
||||
template.DNSOptions = &executer.DNSOptions{
|
||||
Debug: r.options.Debug,
|
||||
Template: t,
|
||||
Writer: r.output,
|
||||
TraceLog: r.traceLog,
|
||||
}
|
||||
}
|
||||
if template.DNSOptions != nil || template.HTTPOptions != nil {
|
||||
|
@ -308,7 +315,6 @@ func (r *Runner) preloadWorkflowTemplates(p progress.IProgress, workflow *workfl
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
wflTemplatesList = append(wflTemplatesList, workflowTemplates{Name: name, Templates: wtlst})
|
||||
}
|
||||
|
||||
|
|
|
@ -2,35 +2,31 @@ package runner
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/fastdialer/fastdialer"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/httpx/common/cache"
|
||||
"github.com/projectdiscovery/hmap/store/hybrid"
|
||||
"github.com/projectdiscovery/nuclei/v2/internal/bufwriter"
|
||||
"github.com/projectdiscovery/nuclei/v2/internal/progress"
|
||||
"github.com/projectdiscovery/nuclei/v2/internal/tracelog"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/atomicboolean"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/collaborator"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/colorizer"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/globalratelimiter"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/projectfile"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/workflows"
|
||||
"github.com/remeh/sizedwaitgroup"
|
||||
"go.uber.org/ratelimit"
|
||||
)
|
||||
|
||||
// Runner is a client for running the enumeration process.
|
||||
type Runner struct {
|
||||
input string
|
||||
inputCount int64
|
||||
tempFile string
|
||||
|
||||
traceLog tracelog.Log
|
||||
|
||||
|
@ -44,14 +40,18 @@ type Runner struct {
|
|||
pf *projectfile.ProjectFile
|
||||
|
||||
// progress tracking
|
||||
progress progress.IProgress
|
||||
progress *progress.Progress
|
||||
|
||||
// output coloring
|
||||
colorizer colorizer.NucleiColorizer
|
||||
decolorizer *regexp.Regexp
|
||||
|
||||
// http dialer
|
||||
dialer cache.DialerFunc
|
||||
// rate limiter
|
||||
ratelimiter ratelimit.Limiter
|
||||
|
||||
// input deduplication
|
||||
hm *hybrid.HybridMap
|
||||
dialer *fastdialer.Dialer
|
||||
}
|
||||
|
||||
// New creates a new client for running enumeration process.
|
||||
|
@ -94,79 +94,70 @@ func New(options *Options) (*Runner, error) {
|
|||
runner.readNucleiIgnoreFile()
|
||||
}
|
||||
|
||||
// If we have stdin, write it to a new file
|
||||
if options.Stdin {
|
||||
tempInput, err := ioutil.TempFile("", "stdin-input-*")
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := io.Copy(tempInput, os.Stdin); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
runner.tempFile = tempInput.Name()
|
||||
tempInput.Close()
|
||||
}
|
||||
// If we have single target, write it to a new file
|
||||
if options.Target != "" {
|
||||
tempInput, err := ioutil.TempFile("", "stdin-input-*")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Fprintf(tempInput, "%s\n", options.Target)
|
||||
runner.tempFile = tempInput.Name()
|
||||
tempInput.Close()
|
||||
if hm, err := hybrid.New(hybrid.DefaultDiskOptions); err != nil {
|
||||
gologger.Fatalf("Could not create temporary input file: %s\n", err)
|
||||
} else {
|
||||
runner.hm = hm
|
||||
}
|
||||
|
||||
// Setup input, handle a list of hosts as argument
|
||||
var err error
|
||||
|
||||
var input *os.File
|
||||
|
||||
if options.Targets != "" {
|
||||
input, err = os.Open(options.Targets)
|
||||
} else if options.Stdin || options.Target != "" {
|
||||
input, err = os.Open(runner.tempFile)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
gologger.Fatalf("Could not open targets file '%s': %s\n", options.Targets, err)
|
||||
}
|
||||
|
||||
// Sanitize input and pre-compute total number of targets
|
||||
var usedInput = make(map[string]struct{})
|
||||
|
||||
dupeCount := 0
|
||||
sb := strings.Builder{}
|
||||
scanner := bufio.NewScanner(input)
|
||||
runner.inputCount = 0
|
||||
dupeCount := 0
|
||||
|
||||
// Handle single target
|
||||
if options.Target != "" {
|
||||
runner.inputCount++
|
||||
// nolint:errcheck // ignoring error
|
||||
runner.hm.Set(options.Target, nil)
|
||||
}
|
||||
|
||||
// Handle stdin
|
||||
if options.Stdin {
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
url := strings.TrimSpace(scanner.Text())
|
||||
// skip empty lines
|
||||
if url == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// skip dupes
|
||||
if _, ok := runner.hm.Get(url); ok {
|
||||
dupeCount++
|
||||
continue
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
url := scanner.Text()
|
||||
// skip empty lines
|
||||
if url == "" {
|
||||
continue
|
||||
}
|
||||
// deduplication
|
||||
if _, ok := usedInput[url]; !ok {
|
||||
usedInput[url] = struct{}{}
|
||||
runner.inputCount++
|
||||
|
||||
// allocate global rate limiters
|
||||
globalratelimiter.Add(url, options.RateLimit)
|
||||
|
||||
sb.WriteString(url)
|
||||
sb.WriteString("\n")
|
||||
} else {
|
||||
dupeCount++
|
||||
// nolint:errcheck // ignoring error
|
||||
runner.hm.Set(url, nil)
|
||||
}
|
||||
}
|
||||
input.Close()
|
||||
|
||||
runner.input = sb.String()
|
||||
// Handle taget file
|
||||
if options.Targets != "" {
|
||||
input, err := os.Open(options.Targets)
|
||||
if err != nil {
|
||||
gologger.Fatalf("Could not open targets file '%s': %s\n", options.Targets, err)
|
||||
}
|
||||
scanner := bufio.NewScanner(input)
|
||||
for scanner.Scan() {
|
||||
url := strings.TrimSpace(scanner.Text())
|
||||
// skip empty lines
|
||||
if url == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// skip dupes
|
||||
if _, ok := runner.hm.Get(url); ok {
|
||||
dupeCount++
|
||||
continue
|
||||
}
|
||||
|
||||
runner.inputCount++
|
||||
// nolint:errcheck // ignoring error
|
||||
runner.hm.Set(url, nil)
|
||||
}
|
||||
input.Close()
|
||||
}
|
||||
|
||||
if dupeCount > 0 {
|
||||
gologger.Labelf("Supplied input was automatically deduplicated (%d removed).", dupeCount)
|
||||
|
@ -174,22 +165,22 @@ func New(options *Options) (*Runner, error) {
|
|||
|
||||
// Create the output file if asked
|
||||
if options.Output != "" {
|
||||
output, err := bufwriter.New(options.Output)
|
||||
if err != nil {
|
||||
gologger.Fatalf("Could not create output file '%s': %s\n", options.Output, err)
|
||||
output, errBufWriter := bufwriter.New(options.Output)
|
||||
if errBufWriter != nil {
|
||||
gologger.Fatalf("Could not create output file '%s': %s\n", options.Output, errBufWriter)
|
||||
}
|
||||
runner.output = output
|
||||
}
|
||||
|
||||
// Creates the progress tracking object
|
||||
runner.progress = progress.NewProgress(runner.colorizer.Colorizer, options.EnableProgressBar)
|
||||
runner.progress = progress.NewProgress(options.EnableProgressBar)
|
||||
|
||||
// create project file if requested or load existing one
|
||||
if options.Project {
|
||||
var err error
|
||||
runner.pf, err = projectfile.New(&projectfile.Options{Path: options.ProjectPath, Cleanup: options.ProjectPath == ""})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var projectFileErr error
|
||||
runner.pf, projectFileErr = projectfile.New(&projectfile.Options{Path: options.ProjectPath, Cleanup: options.ProjectPath == ""})
|
||||
if projectFileErr != nil {
|
||||
return nil, projectFileErr
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,11 +190,18 @@ func New(options *Options) (*Runner, error) {
|
|||
}
|
||||
|
||||
// Create Dialer
|
||||
runner.dialer, err = cache.NewDialer(cache.DefaultOptions)
|
||||
var err error
|
||||
runner.dialer, err = fastdialer.NewDialer(fastdialer.DefaultOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if options.RateLimit > 0 {
|
||||
runner.ratelimiter = ratelimit.New(options.RateLimit)
|
||||
} else {
|
||||
runner.ratelimiter = ratelimit.NewUnlimited()
|
||||
}
|
||||
|
||||
return runner, nil
|
||||
}
|
||||
|
||||
|
@ -212,7 +210,7 @@ func (r *Runner) Close() {
|
|||
if r.output != nil {
|
||||
r.output.Close()
|
||||
}
|
||||
os.Remove(r.tempFile)
|
||||
r.hm.Close()
|
||||
if r.pf != nil {
|
||||
r.pf.Close()
|
||||
}
|
||||
|
@ -282,7 +280,7 @@ func (r *Runner) RunEnumeration() {
|
|||
} else if totalRequests > 0 || hasWorkflows {
|
||||
// tracks global progress and captures stdout/stderr until p.Wait finishes
|
||||
p := r.progress
|
||||
p.InitProgressbar(r.inputCount, templateCount, totalRequests)
|
||||
p.Init(r.inputCount, templateCount, totalRequests)
|
||||
|
||||
for _, t := range availableTemplates {
|
||||
wgtemplates.Add()
|
||||
|
@ -303,7 +301,7 @@ func (r *Runner) RunEnumeration() {
|
|||
}
|
||||
|
||||
wgtemplates.Wait()
|
||||
p.Wait()
|
||||
p.Stop()
|
||||
}
|
||||
|
||||
if !results.Get() {
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/projectdiscovery/nuclei/v2/pkg/requests"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
||||
retryabledns "github.com/projectdiscovery/retryabledns"
|
||||
"go.uber.org/ratelimit"
|
||||
)
|
||||
|
||||
// DNSExecuter is a client for performing a DNS request
|
||||
|
@ -32,6 +33,7 @@ type DNSExecuter struct {
|
|||
template *templates.Template
|
||||
dnsRequest *requests.DNSRequest
|
||||
writer *bufwriter.Writer
|
||||
ratelimiter ratelimit.Limiter
|
||||
|
||||
colorizer colorizer.NucleiColorizer
|
||||
decolorizer *regexp.Regexp
|
||||
|
@ -59,6 +61,7 @@ type DNSOptions struct {
|
|||
|
||||
Colorizer colorizer.NucleiColorizer
|
||||
Decolorizer *regexp.Regexp
|
||||
RateLimiter ratelimit.Limiter
|
||||
}
|
||||
|
||||
// NewDNSExecuter creates a new DNS executer from a template
|
||||
|
@ -79,13 +82,14 @@ func NewDNSExecuter(options *DNSOptions) *DNSExecuter {
|
|||
coloredOutput: options.ColoredOutput,
|
||||
colorizer: options.Colorizer,
|
||||
decolorizer: options.Decolorizer,
|
||||
ratelimiter: options.RateLimiter,
|
||||
}
|
||||
|
||||
return executer
|
||||
}
|
||||
|
||||
// ExecuteDNS executes the DNS request on a URL
|
||||
func (e *DNSExecuter) ExecuteDNS(p progress.IProgress, reqURL string) *Result {
|
||||
func (e *DNSExecuter) ExecuteDNS(p *progress.Progress, reqURL string) *Result {
|
||||
result := &Result{}
|
||||
|
||||
// Parse the URL and return domain if URL.
|
||||
|
@ -101,9 +105,7 @@ func (e *DNSExecuter) ExecuteDNS(p progress.IProgress, reqURL string) *Result {
|
|||
if err != nil {
|
||||
e.traceLog.Request(e.template.ID, domain, "dns", err)
|
||||
result.Error = errors.Wrap(err, "could not make dns request")
|
||||
|
||||
p.Drop(1)
|
||||
|
||||
return result
|
||||
}
|
||||
e.traceLog.Request(e.template.ID, domain, "dns", nil)
|
||||
|
@ -117,12 +119,9 @@ func (e *DNSExecuter) ExecuteDNS(p progress.IProgress, reqURL string) *Result {
|
|||
resp, err := e.dnsClient.Do(compiledRequest)
|
||||
if err != nil {
|
||||
result.Error = errors.Wrap(err, "could not send dns request")
|
||||
|
||||
p.Drop(1)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
p.Update()
|
||||
|
||||
gologger.Verbosef("Sent for [%s] to %s\n", "dns-request", e.template.ID, reqURL)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package executer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
|
@ -19,14 +20,13 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/fastdialer/fastdialer"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/httpx/common/cache"
|
||||
"github.com/projectdiscovery/nuclei/v2/internal/bufwriter"
|
||||
"github.com/projectdiscovery/nuclei/v2/internal/progress"
|
||||
"github.com/projectdiscovery/nuclei/v2/internal/tracelog"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/colorizer"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/generators"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/globalratelimiter"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/matchers"
|
||||
projetctfile "github.com/projectdiscovery/nuclei/v2/pkg/projectfile"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/requests"
|
||||
|
@ -34,6 +34,7 @@ import (
|
|||
"github.com/projectdiscovery/rawhttp"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
"github.com/remeh/sizedwaitgroup"
|
||||
"go.uber.org/ratelimit"
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
|
@ -65,6 +66,7 @@ type HTTPExecuter struct {
|
|||
jsonRequest bool
|
||||
noMeta bool
|
||||
stopAtFirstMatch bool
|
||||
ratelimiter ratelimit.Limiter
|
||||
}
|
||||
|
||||
// HTTPOptions contains configuration options for the HTTP executer.
|
||||
|
@ -89,7 +91,8 @@ type HTTPOptions struct {
|
|||
ColoredOutput bool
|
||||
StopAtFirstMatch bool
|
||||
PF *projetctfile.ProjectFile
|
||||
Dialer *cache.DialerFunc
|
||||
RateLimiter ratelimit.Limiter
|
||||
Dialer *fastdialer.Dialer
|
||||
}
|
||||
|
||||
// NewHTTPExecuter creates a new HTTP executer from a template
|
||||
|
@ -109,10 +112,7 @@ func NewHTTPExecuter(options *HTTPOptions) (*HTTPExecuter, error) {
|
|||
}
|
||||
|
||||
// Create the HTTP Client
|
||||
client, err := makeHTTPClient(proxyURL, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := makeHTTPClient(proxyURL, options)
|
||||
// nolint:bodyclose // false positive there is no body to close yet
|
||||
client.CheckRetry = retryablehttp.HostSprayRetryPolicy()
|
||||
|
||||
|
@ -147,6 +147,7 @@ func NewHTTPExecuter(options *HTTPOptions) (*HTTPExecuter, error) {
|
|||
decolorizer: options.Decolorizer,
|
||||
stopAtFirstMatch: options.StopAtFirstMatch,
|
||||
pf: options.PF,
|
||||
ratelimiter: options.RateLimiter,
|
||||
}
|
||||
|
||||
return executer, nil
|
||||
|
@ -194,7 +195,7 @@ func (e *HTTPExecuter) ExecuteRaceRequest(reqURL string) *Result {
|
|||
return result
|
||||
}
|
||||
|
||||
func (e *HTTPExecuter) ExecuteParallelHTTP(p progress.IProgress, reqURL string) *Result {
|
||||
func (e *HTTPExecuter) ExecuteParallelHTTP(p *progress.Progress, reqURL string) *Result {
|
||||
result := &Result{
|
||||
Matches: make(map[string]interface{}),
|
||||
Extractions: make(map[string]interface{}),
|
||||
|
@ -223,7 +224,7 @@ func (e *HTTPExecuter) ExecuteParallelHTTP(p progress.IProgress, reqURL string)
|
|||
go func(httpRequest *requests.HTTPRequest) {
|
||||
defer swg.Done()
|
||||
|
||||
globalratelimiter.Take(reqURL)
|
||||
e.ratelimiter.Take()
|
||||
|
||||
// If the request was built correctly then execute it
|
||||
err = e.handleHTTP(reqURL, httpRequest, dynamicvalues, result, "")
|
||||
|
@ -236,9 +237,9 @@ func (e *HTTPExecuter) ExecuteParallelHTTP(p progress.IProgress, reqURL string)
|
|||
}
|
||||
}(request)
|
||||
}
|
||||
p.Update()
|
||||
e.bulkHTTPRequest.Increment(reqURL)
|
||||
}
|
||||
|
||||
swg.Wait()
|
||||
|
||||
return result
|
||||
|
@ -309,14 +310,12 @@ func (e *HTTPExecuter) ExecuteTurboHTTP(reqURL string) *Result {
|
|||
|
||||
e.bulkHTTPRequest.Increment(reqURL)
|
||||
}
|
||||
|
||||
swg.Wait()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ExecuteHTTP executes the HTTP request on a URL
|
||||
func (e *HTTPExecuter) ExecuteHTTP(p progress.IProgress, reqURL string) *Result {
|
||||
func (e *HTTPExecuter) ExecuteHTTP(p *progress.Progress, reqURL string) *Result {
|
||||
// verify if pipeline was requested
|
||||
if e.bulkHTTPRequest.Pipeline {
|
||||
return e.ExecuteTurboHTTP(reqURL)
|
||||
|
@ -357,7 +356,7 @@ func (e *HTTPExecuter) ExecuteHTTP(p progress.IProgress, reqURL string) *Result
|
|||
result.Error = err
|
||||
p.Drop(remaining)
|
||||
} else {
|
||||
globalratelimiter.Take(reqURL)
|
||||
e.ratelimiter.Take()
|
||||
// If the request was built correctly then execute it
|
||||
format := "%s_" + strconv.Itoa(requestNumber)
|
||||
err = e.handleHTTP(reqURL, httpRequest, dynamicvalues, result, format)
|
||||
|
@ -369,6 +368,7 @@ func (e *HTTPExecuter) ExecuteHTTP(p progress.IProgress, reqURL string) *Result
|
|||
e.traceLog.Request(e.template.ID, reqURL, "http", nil)
|
||||
}
|
||||
}
|
||||
p.Update()
|
||||
|
||||
// Check if has to stop processing at first valid result
|
||||
if e.stopAtFirstMatch && result.GotResults {
|
||||
|
@ -378,12 +378,9 @@ func (e *HTTPExecuter) ExecuteHTTP(p progress.IProgress, reqURL string) *Result
|
|||
|
||||
// move always forward with requests
|
||||
e.bulkHTTPRequest.Increment(reqURL)
|
||||
p.Update()
|
||||
remaining--
|
||||
}
|
||||
|
||||
gologger.Verbosef("Sent for [%s] to %s\n", "http-request", e.template.ID, reqURL)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -466,14 +463,14 @@ func (e *HTTPExecuter) handleHTTP(reqURL string, request *requests.HTTPRequest,
|
|||
|
||||
duration := time.Since(timeStart)
|
||||
|
||||
// Dump response - Step 1 - Decompression not yet handled
|
||||
var dumpedResponse []byte
|
||||
if e.debug {
|
||||
dumpedResponse, dumpErr := httputil.DumpResponse(resp, true)
|
||||
var dumpErr error
|
||||
dumpedResponse, dumpErr = httputil.DumpResponse(resp, true)
|
||||
if dumpErr != nil {
|
||||
return errors.Wrap(dumpErr, "could not dump http response")
|
||||
}
|
||||
|
||||
gologger.Infof("Dumped HTTP response for %s (%s)\n\n", reqURL, e.template.ID)
|
||||
fmt.Fprintf(os.Stderr, "%s\n", string(dumpedResponse))
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
|
@ -493,11 +490,19 @@ func (e *HTTPExecuter) handleHTTP(reqURL string, request *requests.HTTPRequest,
|
|||
|
||||
// net/http doesn't automatically decompress the response body if an encoding has been specified by the user in the request
|
||||
// so in case we have to manually do it
|
||||
dataOrig := data
|
||||
data, err = requests.HandleDecompression(request, data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not decompress http body")
|
||||
}
|
||||
|
||||
// Dump response - step 2 - replace gzip body with deflated one or with itself (NOP operation)
|
||||
if e.debug {
|
||||
dumpedResponse = bytes.ReplaceAll(dumpedResponse, dataOrig, data)
|
||||
gologger.Infof("Dumped HTTP response for %s (%s)\n\n", reqURL, e.template.ID)
|
||||
fmt.Fprintf(os.Stderr, "%s\n", string(dumpedResponse))
|
||||
}
|
||||
|
||||
// if nuclei-project is enabled store the response if not previously done
|
||||
if e.pf != nil && !fromcache {
|
||||
err := e.pf.Set(dumpedRequest, resp, data)
|
||||
|
@ -537,7 +542,7 @@ func (e *HTTPExecuter) handleHTTP(reqURL string, request *requests.HTTPRequest,
|
|||
result.Meta = request.Meta
|
||||
result.GotResults = true
|
||||
result.Unlock()
|
||||
e.writeOutputHTTP(request, resp, body, matcher, nil, result.Meta)
|
||||
e.writeOutputHTTP(request, resp, body, matcher, nil, result.Meta, reqURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -568,7 +573,7 @@ func (e *HTTPExecuter) handleHTTP(reqURL string, request *requests.HTTPRequest,
|
|||
// Write a final string of output if matcher type is
|
||||
// AND or if we have extractors for the mechanism too.
|
||||
if len(outputExtractorResults) > 0 || matcherCondition == matchers.ANDCondition {
|
||||
e.writeOutputHTTP(request, resp, body, nil, outputExtractorResults, result.Meta)
|
||||
e.writeOutputHTTP(request, resp, body, nil, outputExtractorResults, result.Meta, reqURL)
|
||||
result.Lock()
|
||||
result.GotResults = true
|
||||
result.Unlock()
|
||||
|
@ -581,7 +586,7 @@ func (e *HTTPExecuter) handleHTTP(reqURL string, request *requests.HTTPRequest,
|
|||
func (e *HTTPExecuter) Close() {}
|
||||
|
||||
// makeHTTPClient creates a http client
|
||||
func makeHTTPClient(proxyURL *url.URL, options *HTTPOptions) (*retryablehttp.Client, error) {
|
||||
func makeHTTPClient(proxyURL *url.URL, options *HTTPOptions) *retryablehttp.Client {
|
||||
// Multiple Host
|
||||
retryablehttpOptions := retryablehttp.DefaultOptionsSpraying
|
||||
disableKeepAlives := true
|
||||
|
@ -603,7 +608,7 @@ func makeHTTPClient(proxyURL *url.URL, options *HTTPOptions) (*retryablehttp.Cli
|
|||
maxRedirects := options.BulkHTTPRequest.MaxRedirects
|
||||
|
||||
transport := &http.Transport{
|
||||
DialContext: *options.Dialer,
|
||||
DialContext: options.Dialer.Dial,
|
||||
MaxIdleConns: maxIdleConns,
|
||||
MaxIdleConnsPerHost: maxIdleConnsPerHost,
|
||||
MaxConnsPerHost: maxConnsPerHost,
|
||||
|
@ -644,7 +649,7 @@ func makeHTTPClient(proxyURL *url.URL, options *HTTPOptions) (*retryablehttp.Cli
|
|||
Transport: transport,
|
||||
Timeout: time.Duration(options.Timeout) * time.Second,
|
||||
CheckRedirect: makeCheckRedirectFunc(followRedirects, maxRedirects),
|
||||
}, retryablehttpOptions), nil
|
||||
}, retryablehttpOptions)
|
||||
}
|
||||
|
||||
type checkRedirectFunc func(_ *http.Request, requests []*http.Request) error
|
||||
|
|
|
@ -20,6 +20,7 @@ func (e *DNSExecuter) writeOutputDNS(domain string, req, resp *dns.Msg, matcher
|
|||
if !e.noMeta {
|
||||
output["template"] = e.template.ID
|
||||
output["type"] = "dns"
|
||||
output["host"] = domain
|
||||
for k, v := range e.template.Info {
|
||||
output[k] = v
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
)
|
||||
|
||||
// writeOutputHTTP writes http output to streams
|
||||
func (e *HTTPExecuter) writeOutputHTTP(req *requests.HTTPRequest, resp *http.Response, body string, matcher *matchers.Matcher, extractorResults []string, meta map[string]interface{}) {
|
||||
func (e *HTTPExecuter) writeOutputHTTP(req *requests.HTTPRequest, resp *http.Response, body string, matcher *matchers.Matcher, extractorResults []string, meta map[string]interface{}, reqURL string) {
|
||||
var URL string
|
||||
if req.RawRequest != nil {
|
||||
URL = req.RawRequest.FullURL
|
||||
|
@ -28,6 +28,7 @@ func (e *HTTPExecuter) writeOutputHTTP(req *requests.HTTPRequest, resp *http.Res
|
|||
if !e.noMeta {
|
||||
output["template"] = e.template.ID
|
||||
output["type"] = "http"
|
||||
output["host"] = reqURL
|
||||
if len(meta) > 0 {
|
||||
output["meta"] = meta
|
||||
}
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
package globalratelimiter
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"go.uber.org/ratelimit"
|
||||
)
|
||||
|
||||
var defaultrwmutex sync.RWMutex
|
||||
var defaultGlobalRateLimiter GlobalRateLimiter = GlobalRateLimiter{ratesLimiters: make(map[string]ratelimit.Limiter)}
|
||||
|
||||
type GlobalRateLimiter struct {
|
||||
sync.RWMutex
|
||||
ratesLimiters map[string]ratelimit.Limiter
|
||||
}
|
||||
|
||||
func Add(k string, rateLimit int) {
|
||||
defaultrwmutex.Lock()
|
||||
defer defaultrwmutex.Unlock()
|
||||
|
||||
if rateLimit > 0 {
|
||||
defaultGlobalRateLimiter.ratesLimiters[k] = ratelimit.New(rateLimit)
|
||||
} else {
|
||||
defaultGlobalRateLimiter.ratesLimiters[k] = ratelimit.NewUnlimited()
|
||||
}
|
||||
}
|
||||
|
||||
func Take(k string) {
|
||||
rl := take(k)
|
||||
|
||||
rl.Take()
|
||||
}
|
||||
|
||||
func take(k string) ratelimit.Limiter {
|
||||
defaultrwmutex.RLock()
|
||||
defer defaultrwmutex.RUnlock() //nolint
|
||||
|
||||
return defaultGlobalRateLimiter.ratesLimiters[k]
|
||||
}
|
||||
|
||||
func Del(k string, rateLimit int) {
|
||||
defaultrwmutex.Lock()
|
||||
defer defaultrwmutex.Unlock() //nolint
|
||||
|
||||
delete(defaultGlobalRateLimiter.ratesLimiters, k)
|
||||
}
|
||||
|
||||
func New() *GlobalRateLimiter {
|
||||
var globalRateLimiter GlobalRateLimiter
|
||||
globalRateLimiter.ratesLimiters = make(map[string]ratelimit.Limiter)
|
||||
return &globalRateLimiter
|
||||
}
|
||||
|
||||
func (grl *GlobalRateLimiter) Add(k string, rateLimit int) {
|
||||
grl.Lock()
|
||||
defer grl.Unlock()
|
||||
|
||||
if rateLimit > 0 {
|
||||
grl.ratesLimiters[k] = ratelimit.New(rateLimit)
|
||||
} else {
|
||||
grl.ratesLimiters[k] = ratelimit.NewUnlimited()
|
||||
}
|
||||
}
|
||||
|
||||
func (grl *GlobalRateLimiter) take(k string) ratelimit.Limiter {
|
||||
grl.RLock()
|
||||
defer grl.RUnlock() //nolint
|
||||
|
||||
return grl.ratesLimiters[k]
|
||||
}
|
||||
|
||||
func (grl *GlobalRateLimiter) Take(k string) {
|
||||
rl := grl.take(k)
|
||||
rl.Take()
|
||||
}
|
||||
|
||||
func (grl *GlobalRateLimiter) Del(k string, rateLimit int) {
|
||||
grl.Lock()
|
||||
defer grl.Unlock()
|
||||
|
||||
delete(grl.ratesLimiters, k)
|
||||
}
|
|
@ -278,7 +278,6 @@ func (r *BulkHTTPRequest) fillRequest(req *http.Request, values map[string]inter
|
|||
|
||||
// In case of multiple threads the underlying connection should remain open to allow reuse
|
||||
if r.Threads <= 0 && req.Header.Get("Connection") == "" {
|
||||
setHeader(req, "Connection", "close")
|
||||
req.Close = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package requests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
|
@ -10,6 +11,9 @@ import (
|
|||
|
||||
func Dump(req *HTTPRequest, reqURL string) ([]byte, error) {
|
||||
if req.Request != nil {
|
||||
// Create a copy on the fly of the request body - ignore errors
|
||||
bodyBytes, _ := req.Request.BodyBytes()
|
||||
req.Request.Request.Body = ioutil.NopCloser(bytes.NewReader(bodyBytes))
|
||||
return httputil.DumpRequest(req.Request.Request, true)
|
||||
}
|
||||
|
||||
|
|
|
@ -35,8 +35,8 @@ func HandleDecompression(r *HTTPRequest, bodyOrig []byte) (bodyDec []byte, err e
|
|||
return bodyOrig, nil
|
||||
}
|
||||
|
||||
encodingHeader := strings.ToLower(r.Request.Header.Get("Accept-Encoding"))
|
||||
if encodingHeader == "gzip" {
|
||||
encodingHeader := strings.TrimSpace(strings.ToLower(r.Request.Header.Get("Accept-Encoding")))
|
||||
if encodingHeader == "gzip" || encodingHeader == "gzip, deflate" {
|
||||
gzipreader, err := gzip.NewReader(bytes.NewReader(bodyOrig))
|
||||
if err != nil {
|
||||
return bodyDec, err
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package workflows
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
|
@ -22,11 +22,14 @@ func Parse(file string) (*Workflow, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if len(workflow.Workflows) > 0 {
|
||||
if err := workflow.generateLogicFromWorkflows(); err != nil {
|
||||
return nil, errors.Wrap(err, "could not generate workflow")
|
||||
}
|
||||
}
|
||||
if workflow.Logic == "" {
|
||||
return nil, errors.New("no logic provided")
|
||||
}
|
||||
|
||||
workflow.path = file
|
||||
|
||||
return workflow, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package workflows
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/segmentio/ksuid"
|
||||
)
|
||||
|
||||
// generateLogicFromWorkflows generates a workflow logic using the
|
||||
// yaml based workflow declaration.
|
||||
//
|
||||
// The implementation is very basic and contains a simple yaml->tengo
|
||||
// convertor that implements basic required features.
|
||||
func (w *Workflow) generateLogicFromWorkflows() error {
|
||||
w.Variables = make(map[string]string)
|
||||
|
||||
workflowBuilder := &strings.Builder{}
|
||||
for _, template := range w.Workflows {
|
||||
if err := w.generateTemplateFunc(template, workflowBuilder); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.Logic = workflowBuilder.String()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Workflow) generateTemplateFunc(template *WorkflowTemplate, workflowBuilder *strings.Builder) error {
|
||||
builder := &strings.Builder{}
|
||||
builder.WriteString("var_")
|
||||
builder.WriteString(ksuid.New().String())
|
||||
ID := builder.String()
|
||||
w.Variables[ID] = template.Template
|
||||
|
||||
if len(template.Subtemplates) > 0 && len(template.Matchers) > 0 {
|
||||
return errors.New("subtemplates and matchers cannot be present together")
|
||||
}
|
||||
workflowBuilder.WriteRune('\n')
|
||||
if len(template.Matchers) > 0 {
|
||||
workflowBuilder.WriteString(ID)
|
||||
workflowBuilder.WriteString("()\n")
|
||||
|
||||
for _, matcher := range template.Matchers {
|
||||
if len(matcher.Subtemplates) == 0 {
|
||||
return errors.New("no subtemplates present for matcher")
|
||||
}
|
||||
workflowBuilder.WriteString("\nif ")
|
||||
workflowBuilder.WriteString(ID)
|
||||
workflowBuilder.WriteString("[\"")
|
||||
workflowBuilder.WriteString(matcher.Name)
|
||||
workflowBuilder.WriteString("\"] {")
|
||||
|
||||
for _, subtemplate := range matcher.Subtemplates {
|
||||
if err := w.generateTemplateFunc(subtemplate, workflowBuilder); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
workflowBuilder.WriteString("\n}")
|
||||
}
|
||||
}
|
||||
if len(template.Subtemplates) > 0 {
|
||||
workflowBuilder.WriteString("if ")
|
||||
workflowBuilder.WriteString(ID)
|
||||
workflowBuilder.WriteString("() {")
|
||||
|
||||
for _, subtemplate := range template.Subtemplates {
|
||||
if err := w.generateTemplateFunc(subtemplate, workflowBuilder); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
workflowBuilder.WriteString("\n}")
|
||||
}
|
||||
if len(template.Matchers) == 0 && len(template.Subtemplates) == 0 {
|
||||
workflowBuilder.WriteString(ID)
|
||||
workflowBuilder.WriteString("();")
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -28,7 +28,7 @@ type NucleiVar struct {
|
|||
type Template struct {
|
||||
HTTPOptions *executer.HTTPOptions
|
||||
DNSOptions *executer.DNSOptions
|
||||
Progress progress.IProgress
|
||||
Progress *progress.Progress
|
||||
}
|
||||
|
||||
// TypeName of the variable
|
||||
|
|
|
@ -12,7 +12,22 @@ type Workflow struct {
|
|||
Variables map[string]string `yaml:"variables"`
|
||||
// Logic contains the workflow pseudo-code
|
||||
Logic string `yaml:"logic"`
|
||||
path string
|
||||
// Workflows is a yaml based workflow declaration code.
|
||||
Workflows []*WorkflowTemplate `yaml:"workflows"`
|
||||
path string
|
||||
}
|
||||
|
||||
// WorkflowTemplate is a template to be ran as part of a workflow
|
||||
type WorkflowTemplate struct {
|
||||
Template string `yaml:"template"`
|
||||
Matchers []*Matcher `yaml:"matchers"`
|
||||
Subtemplates []*WorkflowTemplate `yaml:"subtemplates"`
|
||||
}
|
||||
|
||||
// Matcher performs conditional matching on the workflow template results.
|
||||
type Matcher struct {
|
||||
Name string `yaml:"name"`
|
||||
Subtemplates []*WorkflowTemplate `yaml:"subtemplates"`
|
||||
}
|
||||
|
||||
// GetPath of the workflow
|
||||
|
|
Loading…
Reference in New Issue