mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2026-11-07 17:43:04 -10:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1571002607 | ||
|
|
59b7caa941 | ||
|
|
79bbd51eac | ||
|
|
2875fb06b6 | ||
|
|
4bb48d9949 | ||
|
|
c8394ce1fb | ||
|
|
3ba11aaf74 |
No files matched your search
@@ -415,6 +415,11 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.BoolVarP(&options.PreFetchSecrets, "prefetch-secrets", "ps", false, "prefetch secrets from the secrets file"),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("tests", "Tests",
|
||||
flagSet.BoolVarP(&options.AutogenerateTests, "autogenerate-tests", "agt", false, "enable autogeneration of nuclei test-cases (http only now)"),
|
||||
flagSet.BoolVarP(&options.RunNucleiTests, "run-tests", "rts", false, "run generated nuclei test-cases"),
|
||||
)
|
||||
|
||||
flagSet.SetCustomHelpText(`EXAMPLES:
|
||||
Run nuclei on single host:
|
||||
$ nuclei -target example.com
|
||||
|
||||
@@ -70,6 +70,7 @@ require (
|
||||
github.com/go-ldap/ldap/v3 v3.4.5
|
||||
github.com/go-pg/pg v8.0.7+incompatible
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/goccy/go-yaml v1.11.3
|
||||
github.com/h2non/filetype v1.1.3
|
||||
github.com/labstack/echo/v4 v4.10.2
|
||||
github.com/leslie-qiwa/flat v0.0.0-20230424180412-f9d1cf014baa
|
||||
@@ -80,6 +81,7 @@ require (
|
||||
github.com/praetorian-inc/fingerprintx v1.1.9
|
||||
github.com/projectdiscovery/dsl v0.0.52
|
||||
github.com/projectdiscovery/fasttemplate v0.0.2
|
||||
github.com/projectdiscovery/freeport v0.0.5
|
||||
github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb
|
||||
github.com/projectdiscovery/goflags v0.1.48
|
||||
github.com/projectdiscovery/gologger v1.1.12
|
||||
@@ -87,7 +89,9 @@ require (
|
||||
github.com/projectdiscovery/gozero v0.0.2
|
||||
github.com/projectdiscovery/httpx v1.6.0
|
||||
github.com/projectdiscovery/mapcidr v1.1.16
|
||||
github.com/projectdiscovery/martian/v3 v3.0.0-20240219194442-fed3b744f477
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5
|
||||
github.com/projectdiscovery/proxify v0.0.13-0.20240407134611-de309e8af9ca
|
||||
github.com/projectdiscovery/ratelimit v0.0.35
|
||||
github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917
|
||||
github.com/projectdiscovery/sarif v0.0.1
|
||||
@@ -114,8 +118,10 @@ require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||
github.com/Shopify/sarama v1.38.1 // indirect
|
||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
|
||||
@@ -143,8 +149,11 @@ require (
|
||||
github.com/docker/docker v24.0.9+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/eapache/channels v1.1.0 // indirect
|
||||
github.com/eapache/go-resiliency v1.3.0 // indirect
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20230111030713-bf00bc1b83b6 // indirect
|
||||
github.com/eapache/queue v1.1.0 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/elastic/go-elasticsearch/v7 v7.17.10 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/free5gc/util v1.0.5-0.20230511064842-2e120956883b // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/geoffgarside/ber v1.1.0 // indirect
|
||||
@@ -163,9 +172,12 @@ require (
|
||||
github.com/google/go-github/v30 v30.1.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.6 // indirect
|
||||
github.com/haxii/fastproxy v0.5.37 // indirect
|
||||
github.com/hbakhtiyor/strsim v0.0.0-20190107154042-4d2bbb273edf // indirect
|
||||
github.com/invopop/yaml v0.2.0 // indirect
|
||||
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
|
||||
@@ -200,11 +212,13 @@ require (
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/projectdiscovery/asnmap v1.1.0 // indirect
|
||||
github.com/projectdiscovery/cdncheck v1.0.9 // indirect
|
||||
github.com/projectdiscovery/freeport v0.0.5 // indirect
|
||||
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb // indirect
|
||||
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 // indirect
|
||||
github.com/projectdiscovery/roundrobin v0.0.6 // indirect
|
||||
github.com/projectdiscovery/stringsutil v0.0.2 // indirect
|
||||
github.com/projectdiscovery/tinydns v0.0.31 // indirect
|
||||
github.com/quic-go/quic-go v0.42.0 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/refraction-networking/utls v1.6.1 // indirect
|
||||
github.com/sashabaranov/go-openai v1.15.3 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
@@ -234,6 +248,7 @@ require (
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
gopkg.in/djherbis/times.v1 v1.3.0 // indirect
|
||||
mellium.im/sasl v0.3.1 // indirect
|
||||
)
|
||||
|
||||
@@ -85,7 +85,12 @@ github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAc
|
||||
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
|
||||
github.com/RumbleDiscovery/rumble-tools v0.0.0-20201105153123-f2adbb3244d2/go.mod h1:jD2+mU+E2SZUuAOHZvZj4xP4frlOo+N/YrXDvASFhkE=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/sarama v1.38.1 h1:lqqPUPQZ7zPqYlWpTh+LQ9bhYNu2xJL6k1SJN4WVe2A=
|
||||
github.com/Shopify/sarama v1.38.1/go.mod h1:iwv9a67Ha8VNa+TifujYoWGxWnu2kNVAQdSdZ4X2o5g=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/Shopify/toxiproxy/v2 v2.5.0 h1:i4LPT+qrSlKNtQf5QliVjdP08GyAH8+BUIc9gT0eahc=
|
||||
github.com/Shopify/toxiproxy/v2 v2.5.0/go.mod h1:yhM2epWtAmel9CB8r2+L+PCmhH6yH2pITaPAo7jxJl0=
|
||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
@@ -299,10 +304,16 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
|
||||
github.com/eapache/channels v1.1.0 h1:F1taHcn7/F0i8DYqKXJnyhJcVpp2kgFcNePxXtnyu4k=
|
||||
github.com/eapache/channels v1.1.0/go.mod h1:jMm2qB5Ubtg9zLd+inMZd2/NUvXgzmWXsDaLyQIGfH0=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-resiliency v1.3.0 h1:RRL0nge+cWGlxXbUzJ7yMcq6w2XBEr19dCN6HECGaT0=
|
||||
github.com/eapache/go-resiliency v1.3.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20230111030713-bf00bc1b83b6 h1:8yY/I9ndfrgrXUbOGObLHKBR4Fl3nZXwM2c7OYTT8hM=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20230111030713-bf00bc1b83b6/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0=
|
||||
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elastic/go-elasticsearch/v7 v7.17.10 h1:TCQ8i4PmIJuBunvBS6bwT2ybzVFxxUhhltAs3Gyu1yo=
|
||||
github.com/elastic/go-elasticsearch/v7 v7.17.10/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4=
|
||||
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.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
@@ -313,10 +324,12 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
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=
|
||||
@@ -341,8 +354,6 @@ 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=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-echarts/go-echarts/v2 v2.3.3 h1:uImZAk6qLkC6F9ju6mZ5SPBqTyK8xjZKwSmwnCg4bxg=
|
||||
github.com/go-echarts/go-echarts/v2 v2.3.3/go.mod h1:56YlvzhW/a+du15f3S2qUGNDfKnFOeJSThBIrVFHDtI=
|
||||
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
||||
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
@@ -409,6 +420,8 @@ github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk=
|
||||
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I=
|
||||
github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
|
||||
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
@@ -528,6 +541,8 @@ github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy
|
||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
@@ -536,6 +551,8 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
@@ -559,6 +576,9 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/haxii/fastproxy v0.5.37 h1:grfso8V9sNO8jZjI/vkizFXzz8fE/yrsgl+XxTb3fio=
|
||||
github.com/haxii/fastproxy v0.5.37/go.mod h1:VFy3M4EmTbeKu+IccQ6UiJ1W4sPeQ75GV+1JDa8h864=
|
||||
github.com/haxii/log v1.0.0/go.mod h1:y9MlOm+u2ny65yQxScWfSGZFOhRVLXz2vJlkiIx2jfI=
|
||||
github.com/hbakhtiyor/strsim v0.0.0-20190107154042-4d2bbb273edf h1:umfGUaWdFP2s6457fz1+xXYIWDxdGc7HdkLS9aJ1skk=
|
||||
github.com/hbakhtiyor/strsim v0.0.0-20190107154042-4d2bbb273edf/go.mod h1:V99KdStnMHZsvVOwIvhfcUzYgYkRZeQWUtumtL+SKxA=
|
||||
github.com/hdm/jarm-go v0.0.7 h1:Eq0geenHrBSYuKrdVhrBdMMzOmA+CAMLzN2WrF3eL6A=
|
||||
@@ -864,10 +884,14 @@ github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 h1:ZScL
|
||||
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983/go.mod h1:3G3BRKui7nMuDFAZKR/M2hiOLtaOmyukT20g88qRQjI=
|
||||
github.com/projectdiscovery/mapcidr v1.1.16 h1:rjj1w5D6hbTsUQXYClLcGdfBEy9bryclgi70t0vBggo=
|
||||
github.com/projectdiscovery/mapcidr v1.1.16/go.mod h1:rGqpBhStdwOQ2uS62QM9qPsybwMwIhT7CTd2bxoHs8Q=
|
||||
github.com/projectdiscovery/martian/v3 v3.0.0-20240219194442-fed3b744f477 h1:VJaBELAC5Hw+kc+ylrFF5nSf7Wasnb9mZxMlF/kR3gg=
|
||||
github.com/projectdiscovery/martian/v3 v3.0.0-20240219194442-fed3b744f477/go.mod h1:wPvVUl2C/XOFacugXwsUp65GN0upmKfwKMyimA/AAaM=
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5 h1:L/e8z8yw1pfT6bg35NiN7yd1XKtJap5Nk6lMwQ0RNi8=
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5/go.mod h1:pGW2ncnTxTxHtP9wzcIJAB+3/NMp6IiuQWd2NK7K+oc=
|
||||
github.com/projectdiscovery/networkpolicy v0.0.8 h1:XvfBaBwSDNTesSfNQP9VLk3HX9I7x7gHm028TJ5XwI8=
|
||||
github.com/projectdiscovery/networkpolicy v0.0.8/go.mod h1:xnjNqhemxUPxU+UD5Jgsc3+K8IVmcqT1SJeo6UzMtkI=
|
||||
github.com/projectdiscovery/proxify v0.0.13-0.20240407134611-de309e8af9ca h1:Adduz+Lli837Ga2K1NqxVFOFxRYsWjK4zRWzHQWZtV4=
|
||||
github.com/projectdiscovery/proxify v0.0.13-0.20240407134611-de309e8af9ca/go.mod h1:9x1db4kybvc6BpSvpvbMzAuWjNFrt6Foy1t1VI7CUTc=
|
||||
github.com/projectdiscovery/ratelimit v0.0.35 h1:epEzFATOcXZ4tssV4Hax5Op9lrbUnQMEGMV5PoUpTKc=
|
||||
github.com/projectdiscovery/ratelimit v0.0.35/go.mod h1:mPqa8UpV5I7eAN5/ZcsjLiXMhjtVvZRrHtpBRsTPuyA=
|
||||
github.com/projectdiscovery/rawhttp v0.1.45 h1:5jssUybhRz2ljI3bW4kUO9MTvZ37ArG8pISYtgqzwBg=
|
||||
@@ -878,10 +902,14 @@ github.com/projectdiscovery/retryabledns v1.0.58 h1:ut1FSB9+GZ6zQIlKJFLqIz2RZs81
|
||||
github.com/projectdiscovery/retryabledns v1.0.58/go.mod h1:RobmKoNBgngAVE4H9REQtaLP1pa4TCyypHy1MWHT1mY=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.55 h1:ADgugnl9jKkNXn5m/Zd8TGPq1P7GplYlqUNKm/qTmls=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.55/go.mod h1:Kpvh4ruFPOEPYaYxgbFmlvBJr4lJKqpcbGvx1j0r/Ng=
|
||||
github.com/projectdiscovery/roundrobin v0.0.6 h1:zoJAFRgP9XK7B+iKSjR+djRAuDYxnc57+Fx+qpoPvds=
|
||||
github.com/projectdiscovery/roundrobin v0.0.6/go.mod h1:vTxcWqNLyMH6VE2Q/hsNNvDHFLiIzHozC1rLLT/vocQ=
|
||||
github.com/projectdiscovery/sarif v0.0.1 h1:C2Tyj0SGOKbCLgHrx83vaE6YkzXEVrMXYRGLkKCr/us=
|
||||
github.com/projectdiscovery/sarif v0.0.1/go.mod h1:cEYlDu8amcPf6b9dSakcz2nNnJsoz4aR6peERwV+wuQ=
|
||||
github.com/projectdiscovery/stringsutil v0.0.2 h1:uzmw3IVLJSMW1kEg8eCStG/cGbYYZAja8BH3LqqJXMA=
|
||||
github.com/projectdiscovery/stringsutil v0.0.2/go.mod h1:EJ3w6bC5fBYjVou6ryzodQq37D5c6qbAYQpGmAy+DC0=
|
||||
github.com/projectdiscovery/tinydns v0.0.31 h1:iQL1ze2/4PHWH+Nzsxg35kVIWkvEIatAZCDcP0c1/v4=
|
||||
github.com/projectdiscovery/tinydns v0.0.31/go.mod h1:6rj/dEzXZPn+nLl4O1wUMJdfkdUQ9eCV7NsDWPakYv8=
|
||||
github.com/projectdiscovery/tlsx v1.1.6 h1:iw2zwKbd2+kRQ8J1G4dLmS0CLyemd/tKz1UzcNsC77A=
|
||||
github.com/projectdiscovery/tlsx v1.1.6/go.mod h1:s7SRRFdrwIZBK/RXXZi4CR/CubqFSvp8h5Bk1srEZIo=
|
||||
github.com/projectdiscovery/uncover v1.0.7 h1:ut+2lTuvmftmveqF5RTjMWAgyLj8ltPQC7siFy9sj0A=
|
||||
@@ -929,6 +957,8 @@ github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB
|
||||
github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
|
||||
github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY=
|
||||
github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
|
||||
github.com/refraction-networking/utls v1.6.1 h1:n1JG5karzdGWsI6iZmGrOv3SNzR4c+4M8J6KWGsk3lA=
|
||||
@@ -946,6 +976,7 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.11.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
@@ -1481,6 +1512,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
@@ -1537,6 +1570,8 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20220706185917-7780775163c4 h1:7YDGQC/0sigNGzsEWyb9s72jTxlFdwVEYNJHbfQ+Dtg=
|
||||
google.golang.org/genproto v0.0.0-20220706185917-7780775163c4/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||
@@ -1554,6 +1589,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
|
||||
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/jsonl"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/markdown"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/sarif"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testing"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/yaml"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
@@ -50,6 +51,8 @@ func ConfigureOptions() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var proxyServer *testing.ProxyServer
|
||||
|
||||
// ParseOptions parses the command line flags provided by a user
|
||||
func ParseOptions(options *types.Options) {
|
||||
// Check if stdin pipe was given
|
||||
@@ -83,6 +86,19 @@ func ParseOptions(options *types.Options) {
|
||||
if err := ValidateOptions(options); err != nil {
|
||||
gologger.Fatal().Msgf("Program exiting: %s\n", err)
|
||||
}
|
||||
if options.AutogenerateTests {
|
||||
ps, err := testing.NewProxyServer()
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("Could not create proxy server: %s\n", err)
|
||||
}
|
||||
proxyServer = ps
|
||||
|
||||
// Add the proxy server to the options
|
||||
proxyURL := fmt.Sprintf("http://%s", proxyServer.ListenAddr)
|
||||
options.Proxy = goflags.StringSlice([]string{proxyURL})
|
||||
types.ProxyURL = proxyURL
|
||||
gologger.Info().Msgf("Using %s as proxy server for automatic test-generation\n", proxyServer.ListenAddr)
|
||||
}
|
||||
|
||||
// Load the resolvers if user asked for them
|
||||
loadResolvers(options)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/input/provider"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/installer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/loader/parser"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testing"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/scan/events"
|
||||
uncoverlib "github.com/projectdiscovery/uncover"
|
||||
pdcpauth "github.com/projectdiscovery/utils/auth/pdcp"
|
||||
@@ -345,6 +347,9 @@ func (r *Runner) runStandardEnumeration(executerOpts protocols.ExecutorOptions,
|
||||
|
||||
// Close releases all the resources and cleans up
|
||||
func (r *Runner) Close() {
|
||||
if proxyServer != nil {
|
||||
proxyServer.Close()
|
||||
}
|
||||
if r.output != nil {
|
||||
r.output.Close()
|
||||
}
|
||||
@@ -508,6 +513,14 @@ func (r *Runner) RunEnumeration() error {
|
||||
return nil // exit
|
||||
}
|
||||
store.Load()
|
||||
|
||||
if len(store.Templates()) > 1 && r.options.AutogenerateTests {
|
||||
return errors.New("autogenerate-tests flag can only be used with a single template")
|
||||
}
|
||||
if r.inputProvider.Count() > 1 && r.options.AutogenerateTests {
|
||||
return errors.New("autogenerate-tests flag can only be used with a single input")
|
||||
}
|
||||
|
||||
// TODO: remove below functions after v3 or update warning messages
|
||||
disk.PrintDeprecatedPathsMsgIfApplicable(r.options.Silent)
|
||||
templates.PrintDeprecatedProtocolNameMsgIfApplicable(r.options.Silent, r.options.Verbose)
|
||||
@@ -535,6 +548,17 @@ func (r *Runner) RunEnumeration() error {
|
||||
// display execution info like version , templates used etc
|
||||
r.displayExecutionInfo(store)
|
||||
|
||||
// If prompted, we need to run the tests for all the loaded
|
||||
// templates by first trying to detect test definitions and then
|
||||
// executing them with the relevant inputs.
|
||||
if r.options.RunNucleiTests {
|
||||
gologger.Info().Msgf("Running nuclei-templates mock-generated tests")
|
||||
if err := r.runNucleiTests(store, executorEngine); err != nil {
|
||||
return errors.Wrap(err, "could not run nuclei tests")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// prefetch secrets if enabled
|
||||
if executorOpts.AuthProvider != nil && r.options.PreFetchSecrets {
|
||||
gologger.Info().Msgf("Pre-fetching secrets from authprovider[s]")
|
||||
@@ -577,10 +601,17 @@ func (r *Runner) RunEnumeration() error {
|
||||
return err
|
||||
}
|
||||
|
||||
var isInteractMatcher bool
|
||||
if r.interactsh != nil {
|
||||
matched := r.interactsh.Close()
|
||||
if matched {
|
||||
results.CompareAndSwap(false, true)
|
||||
isInteractMatcher = true
|
||||
}
|
||||
}
|
||||
if r.options.AutogenerateTests && proxyServer != nil {
|
||||
if err := r.handleTestAutogenerate(results, isInteractMatcher, store); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if executorOpts.InputHelper != nil {
|
||||
@@ -600,6 +631,108 @@ func (r *Runner) RunEnumeration() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Runner) runNucleiTests(store *loader.Store, engine *core.Engine) error {
|
||||
// For each loaded template, check to see if there's
|
||||
// a corresponding test file for that template with extension
|
||||
// .nuclei_test
|
||||
testFiles := make(map[string]string)
|
||||
templatePathToTemplates := make(map[string]*templates.Template)
|
||||
|
||||
for _, template := range store.Templates() {
|
||||
testFile := fmt.Sprintf("%s.nuclei_test", template.Path)
|
||||
if !fileutil.FileExists(testFile) {
|
||||
continue
|
||||
}
|
||||
testFiles[template.Path] = testFile
|
||||
templatePathToTemplates[template.Path] = template
|
||||
|
||||
gologger.Verbose().Msgf("Found test file for template %s: %s", template.ID, filepath.Base(testFile))
|
||||
}
|
||||
|
||||
if len(testFiles) == 0 {
|
||||
gologger.Info().Msgf("No test files found for nuclei-templates")
|
||||
return nil
|
||||
}
|
||||
|
||||
gologger.Info().Msgf("Loaded %d test files for nuclei-templates", len(testFiles))
|
||||
|
||||
for templatePath, testFile := range testFiles {
|
||||
testTemplate, err := testing.ReadNucleiTestTemplate(testFile)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not read nuclei test template")
|
||||
}
|
||||
|
||||
mockHandler, err := testTemplate.MockServer()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create mock handler for template")
|
||||
}
|
||||
|
||||
testServer := httptest.NewServer(mockHandler)
|
||||
template, ok := templatePathToTemplates[templatePath]
|
||||
if !ok {
|
||||
testServer.Close()
|
||||
return errors.Errorf("no template found for path %s", templatePath)
|
||||
}
|
||||
|
||||
// If we have interactsh matcher, we need to handle it separately
|
||||
// by creating a new interactsh client and closing it after the test
|
||||
// waiting for callbacks.
|
||||
// TODO: Parallelize
|
||||
if testTemplate.IsInteractshMatcher {
|
||||
interactClient, err := interactsh.New(interactsh.DefaultOptions(r.output, r.issuesClient, r.progress))
|
||||
if err != nil {
|
||||
testServer.Close()
|
||||
return errors.Wrap(err, "could not create interactsh client")
|
||||
}
|
||||
template.Options.Interactsh = interactClient
|
||||
}
|
||||
|
||||
results := engine.ExecuteScanWithOpts([]*templates.Template{template}, provider.NewSimpleInputProviderWithUrls(testServer.URL), r.options.DisableClustering)
|
||||
if !results.Load() && !testTemplate.IsInteractshMatcher {
|
||||
testServer.Close()
|
||||
return errors.Errorf("no results found for template %s", template.ID)
|
||||
}
|
||||
|
||||
if testTemplate.IsInteractshMatcher {
|
||||
matched := template.Options.Interactsh.Close()
|
||||
if !matched {
|
||||
testServer.Close()
|
||||
return errors.Errorf("no results found for template %s", template.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
gologger.Info().Msgf("All tests passed successfully")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runner) handleTestAutogenerate(results *atomic.Bool, isInteractMatcher bool, store *loader.Store) error {
|
||||
if !results.Load() {
|
||||
return errors.New("no results found to autogenerate tests")
|
||||
}
|
||||
|
||||
intercepted := proxyServer.Intercepted()
|
||||
if len(intercepted) == 0 {
|
||||
return errors.New("no intercepted requests")
|
||||
}
|
||||
gologger.Verbose().Msgf("Intercepted requests and responses (%d)", len(intercepted))
|
||||
for _, pair := range intercepted {
|
||||
if r.options.Debug {
|
||||
fmt.Printf("Pair: %+v\n", pair)
|
||||
}
|
||||
}
|
||||
var gotInput string
|
||||
r.inputProvider.Iterate(func(value *contextargs.MetaInput) bool {
|
||||
gotInput = value.Input
|
||||
return false
|
||||
})
|
||||
err := testing.GenerateTestsFromPair(intercepted, store.Templates()[0], gotInput, isInteractMatcher)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not generate tests from intercepted requests")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runner) isInputNonHTTP() bool {
|
||||
var nonURLInput bool
|
||||
r.inputProvider.Iterate(func(value *contextargs.MetaInput) bool {
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/projectdiscovery/freeport"
|
||||
"github.com/projectdiscovery/martian/v3"
|
||||
martianlog "github.com/projectdiscovery/martian/v3/log"
|
||||
"github.com/projectdiscovery/proxify"
|
||||
"github.com/projectdiscovery/proxify/pkg/certs"
|
||||
"github.com/projectdiscovery/proxify/pkg/logger/elastic"
|
||||
"github.com/projectdiscovery/proxify/pkg/logger/kafka"
|
||||
"github.com/projectdiscovery/proxify/pkg/types"
|
||||
httputils "github.com/projectdiscovery/utils/http"
|
||||
)
|
||||
|
||||
// ProxyServer is an intercepting proxy launched through nuclei
|
||||
// using proxify for logging requests and responses.
|
||||
//
|
||||
// This is intended for unit testing of http templates in nuclei.
|
||||
type ProxyServer struct {
|
||||
ListenAddr string
|
||||
|
||||
intercepted []RequestResponsePair
|
||||
mutex *sync.RWMutex
|
||||
|
||||
proxy *proxify.Proxy
|
||||
tempdir string
|
||||
}
|
||||
|
||||
// RequestResponsePair is a pair of request and response
|
||||
type RequestResponsePair struct {
|
||||
Request string `json:"request" yaml:"request"`
|
||||
Response string `json:"response" yaml:"response"`
|
||||
Protocol string `json:"protocol" yaml:"protocol"`
|
||||
}
|
||||
|
||||
func responsePairFromResp(resp *http.Response) RequestResponsePair {
|
||||
if resp.Request.Method == http.MethodConnect {
|
||||
return RequestResponsePair{}
|
||||
}
|
||||
|
||||
save := resp.Body
|
||||
savecl := resp.ContentLength
|
||||
|
||||
var err error
|
||||
if resp.Body != nil {
|
||||
save, resp.Body, err = drainBody(resp.Body)
|
||||
if err != nil {
|
||||
return RequestResponsePair{}
|
||||
}
|
||||
}
|
||||
chain := httputils.NewResponseChain(resp, -1)
|
||||
defer chain.Close()
|
||||
|
||||
if err := chain.Fill(); err != nil {
|
||||
log.Printf("[error] [proxy] Could not fill response chain: %s\n", err)
|
||||
return RequestResponsePair{}
|
||||
}
|
||||
resp.Body = save
|
||||
resp.ContentLength = savecl
|
||||
|
||||
if !chain.Has() {
|
||||
return RequestResponsePair{}
|
||||
}
|
||||
respDump := chain.FullResponse()
|
||||
|
||||
return RequestResponsePair{
|
||||
Response: respDump.String(),
|
||||
}
|
||||
}
|
||||
|
||||
// Intercepted returns the intercepted requests and responsesq
|
||||
func (p *ProxyServer) Intercepted() []RequestResponsePair {
|
||||
p.mutex.RLock()
|
||||
defer p.mutex.RUnlock()
|
||||
return p.intercepted
|
||||
}
|
||||
|
||||
// AddIntercepted adds a new request response pair to the intercepted list
|
||||
func (p *ProxyServer) AddIntercepted(pair RequestResponsePair) {
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
|
||||
p.intercepted = append(p.intercepted, pair)
|
||||
}
|
||||
|
||||
// NewProxyServer creates a new proxy server instance
|
||||
func NewProxyServer() (*ProxyServer, error) {
|
||||
ps := &ProxyServer{
|
||||
mutex: &sync.RWMutex{},
|
||||
}
|
||||
|
||||
onRequestFunc := func(req *http.Request, ctx *martian.Context) error {
|
||||
dumped, err := httputil.DumpRequestOut(req, true)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
ctx.Set(ctx.ID(), RequestResponsePair{
|
||||
Request: string(dumped),
|
||||
})
|
||||
ctx.Set("user-data", types.UserData{})
|
||||
return nil
|
||||
}
|
||||
|
||||
onResponseFunc := func(resp *http.Response, ctx *martian.Context) error {
|
||||
pair := responsePairFromResp(resp)
|
||||
if pair.Response == "" {
|
||||
return nil
|
||||
}
|
||||
req, ok := ctx.Get(ctx.ID())
|
||||
if !ok {
|
||||
log.Printf("[error] [proxy] Could not get request from context\n")
|
||||
return nil
|
||||
}
|
||||
valid, ok := req.(RequestResponsePair)
|
||||
if !ok {
|
||||
log.Printf("[error] [proxy] Could not validate request from context\n")
|
||||
return nil
|
||||
}
|
||||
pair.Request = valid.Request
|
||||
pair.Protocol = "http"
|
||||
ps.AddIntercepted(pair)
|
||||
return nil
|
||||
}
|
||||
_, _ = onRequestFunc, onResponseFunc
|
||||
|
||||
tempdir, err := os.MkdirTemp("", "proxify-*")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ps.tempdir = tempdir
|
||||
|
||||
err = certs.LoadCerts(tempdir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostname := "127.0.0.1"
|
||||
port, err := freeport.GetFreePort(hostname, freeport.TCP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ps.ListenAddr = fmt.Sprintf("%s:%d", hostname, port.Port)
|
||||
|
||||
martianlog.SetLogger(&noopMartianLogger{})
|
||||
opts := &proxify.Options{
|
||||
OutputJsonl: true,
|
||||
MaxSize: math.MaxInt,
|
||||
Verbosity: types.VerbosityDefault,
|
||||
CertCacheSize: 256,
|
||||
Directory: tempdir,
|
||||
ListenAddrHTTP: ps.ListenAddr,
|
||||
OnRequestCallback: onRequestFunc,
|
||||
OnResponseCallback: onResponseFunc,
|
||||
UpstreamProxyRequestsNumber: 1,
|
||||
Elastic: &elastic.Options{},
|
||||
Kafka: &kafka.Options{},
|
||||
}
|
||||
proxy, err := proxify.NewProxy(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ps.proxy = proxy
|
||||
|
||||
go func() {
|
||||
err = proxy.Run()
|
||||
if err != nil {
|
||||
log.Printf("[error] [proxy] Could not run proxy: %s\n", err)
|
||||
}
|
||||
}()
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
type noopMartianLogger struct{}
|
||||
|
||||
func (l *noopMartianLogger) Infof(format string, args ...interface{}) {}
|
||||
func (l *noopMartianLogger) Debugf(format string, args ...interface{}) {}
|
||||
func (l *noopMartianLogger) Errorf(format string, args ...interface{}) {}
|
||||
|
||||
func (p *ProxyServer) Close() {
|
||||
_ = os.RemoveAll(p.tempdir)
|
||||
p.proxy.Stop()
|
||||
}
|
||||
|
||||
// from net/http/httputil.DumpResponse
|
||||
func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
|
||||
if b == nil || b == http.NoBody {
|
||||
// No copying needed. Preserve the magic sentinel meaning of NoBody.
|
||||
return http.NoBody, http.NoBody, nil
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if _, err = buf.ReadFrom(b); err != nil {
|
||||
return nil, b, err
|
||||
}
|
||||
if err = b.Close(); err != nil {
|
||||
return nil, b, err
|
||||
}
|
||||
return io.NopCloser(&buf), io.NopCloser(bytes.NewReader(buf.Bytes())), nil
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/gologger/levels"
|
||||
)
|
||||
|
||||
func Test_Proxy_Template(t *testing.T) {
|
||||
gologger.DefaultLogger.SetMaxLevel(levels.LevelVerbose)
|
||||
|
||||
pairs, err := doIntercept()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Printf("Intercepted: %+v\n", len(pairs))
|
||||
|
||||
tmpl := &NucleiTestTemplate{
|
||||
Requests: pairs,
|
||||
TemplateID: "test-template",
|
||||
}
|
||||
handler, err := tmpl.MockServer()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Run("valid_mock_server", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest("GET", "http://scanme.sh/demo", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Header.Set("X-Custom-Header", "myvalue")
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf("expected 200, got %d", resp.StatusCode)
|
||||
}
|
||||
})
|
||||
t.Run("invalid_mock_server", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest("GET", "http://scanme.sh/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
if resp.StatusCode != 404 {
|
||||
t.Fatalf("expected 404, got %d", resp.StatusCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func doIntercept() ([]RequestResponsePair, error) {
|
||||
ps, err := NewProxyServer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ps.Close()
|
||||
|
||||
// Make a http request by using the proxy server
|
||||
// and check the intercepted requests and responses
|
||||
// to see if the request was intercepted and correctly
|
||||
// logged.
|
||||
req, err := http.NewRequest("GET", "http://scanme.sh/demo", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("X-Custom-Header", "myvalue")
|
||||
|
||||
proxyURL := "http://" + ps.ListenAddr
|
||||
parsed, err := url.Parse(proxyURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("Proxy URL: %s\n", parsed.String())
|
||||
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyURL(parsed),
|
||||
},
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
_, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
intercepted := ps.Intercepted()
|
||||
if len(intercepted) == 0 {
|
||||
return nil, fmt.Errorf("no requests were intercepted")
|
||||
}
|
||||
return intercepted, nil
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/input/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
|
||||
"github.com/goccy/go-yaml"
|
||||
)
|
||||
|
||||
// NucleiTestTemplate is a template for testing nuclei templates
|
||||
type NucleiTestTemplate struct {
|
||||
Requests []RequestResponsePair `yaml:"requests"`
|
||||
TemplateID string `yaml:"template_id"`
|
||||
IsInteractshMatcher bool `yaml:"is_interactsh_matcher,omitempty"`
|
||||
}
|
||||
|
||||
type internalRouteDetails struct {
|
||||
Method string
|
||||
|
||||
ResponseStatus int
|
||||
ResponseHeaders map[string][]string
|
||||
ResponseBody string
|
||||
}
|
||||
|
||||
// MockServer creates a mock server from the test template
|
||||
func (n *NucleiTestTemplate) MockServer() (http.HandlerFunc, error) {
|
||||
requestPathToMethods := make(map[string][]internalRouteDetails)
|
||||
for _, reqResp := range n.Requests {
|
||||
parsed, err := types.ParseRawRequest(strings.TrimLeft(reqResp.Request, "\r\n"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not parse request")
|
||||
}
|
||||
|
||||
parsedResponse, err := http.ReadResponse(bufio.NewReader(strings.NewReader(strings.TrimLeft(reqResp.Response, "\r\n"))), nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not parse response")
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(parsedResponse.Body)
|
||||
if err != nil {
|
||||
_ = parsedResponse.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
_ = parsedResponse.Body.Close()
|
||||
|
||||
if _, ok := requestPathToMethods[parsed.URL.Path]; !ok {
|
||||
requestPathToMethods[parsed.URL.Path] = []internalRouteDetails{}
|
||||
}
|
||||
|
||||
requestPathToMethods[parsed.URL.Path] = append(requestPathToMethods[parsed.URL.Path], internalRouteDetails{
|
||||
Method: parsed.Request.Method,
|
||||
|
||||
ResponseStatus: parsedResponse.StatusCode,
|
||||
ResponseHeaders: parsedResponse.Header,
|
||||
ResponseBody: string(data),
|
||||
})
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
routeDetails, ok := requestPathToMethods[r.URL.Path]
|
||||
if !ok {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
for _, route := range routeDetails {
|
||||
if route.Method != r.Method {
|
||||
continue
|
||||
}
|
||||
|
||||
if n.IsInteractshMatcher {
|
||||
dumped, _ := httputil.DumpRequest(r, true)
|
||||
decoded, _ := url.QueryUnescape(string(dumped))
|
||||
|
||||
for _, url := range extractURLs(decoded) {
|
||||
go func(url string) {
|
||||
if err := doCallbackToInteractsh(url); err != nil {
|
||||
gologger.Warning().Msgf("[test-server] Could not send request to interactsh: %s", err)
|
||||
}
|
||||
}(url)
|
||||
}
|
||||
}
|
||||
for k, v := range route.ResponseHeaders {
|
||||
// Ignore certain headers
|
||||
if k == "Content-Encoding" {
|
||||
continue
|
||||
}
|
||||
w.Header()[k] = v
|
||||
}
|
||||
|
||||
w.WriteHeader(route.ResponseStatus)
|
||||
_, _ = w.Write([]byte(route.ResponseBody))
|
||||
return
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
|
||||
// ReadNucleiTestTemplate reads a nuclei test template from a file
|
||||
func ReadNucleiTestTemplate(path string) (*NucleiTestTemplate, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var testTemplate NucleiTestTemplate
|
||||
dec := yaml.NewDecoder(file)
|
||||
if err := dec.Decode(&testTemplate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &testTemplate, nil
|
||||
}
|
||||
|
||||
// GenerateTestsFromPair generates a test from a pair of request and response
|
||||
// and a template object
|
||||
func GenerateTestsFromPair(pair []RequestResponsePair, template *templates.Template, target string, isInteractshMatcher bool) error {
|
||||
host := target
|
||||
|
||||
if strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://") {
|
||||
parsed, err := url.Parse(target)
|
||||
if err == nil {
|
||||
host = parsed.Hostname()
|
||||
}
|
||||
}
|
||||
if strings.Contains(host, ":") {
|
||||
host = strings.Split(host, ":")[0]
|
||||
}
|
||||
|
||||
testTemplate := NucleiTestTemplate{
|
||||
TemplateID: template.ID,
|
||||
IsInteractshMatcher: isInteractshMatcher,
|
||||
}
|
||||
for _, reqResp := range pair {
|
||||
testTemplate.Requests = append(testTemplate.Requests, RequestResponsePair{
|
||||
Request: redactUserInput(host, reqResp.Request),
|
||||
Response: redactUserInput(host, reqResp.Response),
|
||||
Protocol: reqResp.Protocol,
|
||||
})
|
||||
}
|
||||
|
||||
baseDirectory := filepath.Dir(template.Path)
|
||||
filename := filepath.Base(template.Path)
|
||||
|
||||
newFile := fmt.Sprintf("%s.nuclei_test", filename)
|
||||
gologger.Info().Msgf("Writing test template=%s and target=%s to %s", template.ID, target, newFile)
|
||||
newFilename := filepath.Join(baseDirectory, newFile)
|
||||
|
||||
file, err := os.Create(newFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
enc := yaml.NewEncoder(file, yaml.UseLiteralStyleIfMultiline(true), yaml.UseSingleQuote(true))
|
||||
if err := enc.Encode(testTemplate); err != nil {
|
||||
return err
|
||||
}
|
||||
defer enc.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
urlsRegex = regexp.MustCompile(`https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)`)
|
||||
ipRegex = regexp.MustCompile(`((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}`)
|
||||
)
|
||||
|
||||
const redactedTestDomain = "redacted.nuclei-test.domain"
|
||||
|
||||
func redactUserInput(input string, value string) string {
|
||||
urlMatches := urlsRegex.FindAllString(value, -1)
|
||||
for _, match := range urlMatches {
|
||||
parsed, err := url.Parse(match)
|
||||
if err == nil {
|
||||
parsed.Host = redactedTestDomain
|
||||
value = strings.ReplaceAll(value, match, parsed.String())
|
||||
}
|
||||
}
|
||||
ips := ipRegex.FindAllString(value, -1)
|
||||
for _, ip := range ips {
|
||||
value = strings.ReplaceAll(value, ip, "127.0.0.1")
|
||||
}
|
||||
return strings.ReplaceAll(value, input, redactedTestDomain)
|
||||
}
|
||||
|
||||
func extractURLs(text string) []string {
|
||||
matches := domainRegex.FindAllString(text, -1)
|
||||
|
||||
for i, match := range matches {
|
||||
if !strings.HasPrefix(match, "http://") && !strings.HasPrefix(match, "https://") {
|
||||
matches[i] = "http://" + match
|
||||
}
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
var domainRegex = regexp.MustCompile(`(?:https?:\/\/)?[A-Za-z0-9-_\.]+oast\.(?:pro|today|live|site|online|fun|me)`)
|
||||
|
||||
func doCallbackToInteractsh(URL string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", URL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
_, err = io.ReadAll(resp.Body)
|
||||
return err
|
||||
}
|
||||
@@ -261,6 +261,12 @@ type Options struct {
|
||||
AllowLocalFileAccess bool
|
||||
// RestrictLocalNetworkAccess restricts local network access from templates requests
|
||||
RestrictLocalNetworkAccess bool
|
||||
// AutogenerateTests enables autogeneration of tests for templates (currently http)
|
||||
// by using a type of proxy and recording requests and responses, generating test
|
||||
// cases from them.
|
||||
AutogenerateTests bool
|
||||
// RunNucleiTests enables running nuclei tests
|
||||
RunNucleiTests bool
|
||||
// ShowMatchLine enables display of match line number
|
||||
ShowMatchLine bool
|
||||
// EnablePprof enables exposing pprof runtime information with a webserver.
|
||||
|
||||
Reference in new issue
Block a user