diff --git a/integration_tests/headless/headless-basic.yaml b/integration_tests/headless/headless-basic.yaml
new file mode 100644
index 000000000..cfc7dcb3c
--- /dev/null
+++ b/integration_tests/headless/headless-basic.yaml
@@ -0,0 +1,18 @@
+id: headless-basic
+info:
+ name: Headless Basic
+ author: pdteam
+ severity: info
+ tags: headless
+
+headless:
+ - steps:
+ - action: navigate
+ args:
+ url: "{{BaseURL}}/"
+
+ - action: waitload
+ matchers:
+ - type: word
+ words:
+ - ""
\ No newline at end of file
diff --git a/integration_tests/headless/headless-extract-values.yaml b/integration_tests/headless/headless-extract-values.yaml
new file mode 100644
index 000000000..e780ac32c
--- /dev/null
+++ b/integration_tests/headless/headless-extract-values.yaml
@@ -0,0 +1,31 @@
+
+id: headless-extract-values
+info:
+ name: Headless Extract Value
+ author: pdteam
+ severity: info
+ tags: headless
+
+headless:
+ - steps:
+ - action: navigate
+ args:
+ url: "{{BaseURL}}"
+ - action: waitload
+ # From headless/extract-urls.yaml
+ - action: script
+ name: extract
+ args:
+ code: |
+ '\n' + [...new Set(Array.from(document.querySelectorAll('[src], [href], [url], [action]')).map(i => i.src || i.href || i.url || i.action))].join('\r\n') + '\n'
+
+ matchers:
+ - type: word
+ words:
+ - "test.html"
+
+ extractors:
+ - type: kval
+ part: extract
+ kval:
+ - extract
\ No newline at end of file
diff --git a/integration_tests/headless/headless-header-action.yaml b/integration_tests/headless/headless-header-action.yaml
new file mode 100644
index 000000000..ca3c329d8
--- /dev/null
+++ b/integration_tests/headless/headless-header-action.yaml
@@ -0,0 +1,24 @@
+id: headless-header-action
+info:
+ name: Headless Header Action
+ author: pdteam
+ severity: info
+ tags: headless
+
+headless:
+ - steps:
+ - action: setheader
+ args:
+ part: request
+ key: Test
+ value: test value
+
+ - action: navigate
+ args:
+ url: "{{BaseURL}}/"
+
+ - action: waitload
+ matchers:
+ - type: word
+ words:
+ - "test value"
\ No newline at end of file
diff --git a/nuclei-jsonschema.json b/nuclei-jsonschema.json
index a2beba2c7..156090895 100755
--- a/nuclei-jsonschema.json
+++ b/nuclei-jsonschema.json
@@ -1123,6 +1123,11 @@
"type": "boolean",
"title": "mark requests as self-contained",
"description": "Mark Requests for the template as self-contained"
+ },
+ "stop-at-first-match": {
+ "type": "boolean",
+ "title": "stop at first match",
+ "description": "Stop at first match for the template"
}
},
"additionalProperties": false,
diff --git a/v2/cmd/integration-test/headless.go b/v2/cmd/integration-test/headless.go
new file mode 100644
index 000000000..6039fdb64
--- /dev/null
+++ b/v2/cmd/integration-test/headless.go
@@ -0,0 +1,81 @@
+package main
+
+import (
+ "net/http"
+ "net/http/httptest"
+
+ "github.com/julienschmidt/httprouter"
+
+ "github.com/projectdiscovery/nuclei/v2/pkg/testutils"
+)
+
+var headlessTestcases = map[string]testutils.TestCase{
+ "headless/headless-basic.yaml": &headlessBasic{},
+ "headless/headless-header-action.yaml": &headlessHeaderActions{},
+ "headless/headless-extract-values.yaml": &headlessExtractValues{},
+}
+
+type headlessBasic struct{}
+
+// Execute executes a test case and returns an error if occurred
+func (h *headlessBasic) Execute(filePath string) error {
+ router := httprouter.New()
+ router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ _, _ = w.Write([]byte(""))
+ })
+ ts := httptest.NewServer(router)
+ defer ts.Close()
+
+ results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-headless")
+ if err != nil {
+ return err
+ }
+ if len(results) != 1 {
+ return errIncorrectResultsCount(results)
+ }
+ return nil
+}
+
+type headlessHeaderActions struct{}
+
+// Execute executes a test case and returns an error if occurred
+func (h *headlessHeaderActions) Execute(filePath string) error {
+ router := httprouter.New()
+ router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ testValue := r.Header.Get("test")
+ if r.Header.Get("test") != "" {
+ _, _ = w.Write([]byte("" + testValue + ""))
+ }
+ })
+ ts := httptest.NewServer(router)
+ defer ts.Close()
+
+ results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-headless")
+ if err != nil {
+ return err
+ }
+ if len(results) != 1 {
+ return errIncorrectResultsCount(results)
+ }
+ return nil
+}
+
+type headlessExtractValues struct{}
+
+// Execute executes a test case and returns an error if occurred
+func (h *headlessExtractValues) Execute(filePath string) error {
+ router := httprouter.New()
+ router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ _, _ = w.Write([]byte("
test"))
+ })
+ ts := httptest.NewServer(router)
+ defer ts.Close()
+ results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-headless")
+ if err != nil {
+ return err
+ }
+ if len(results) != 3 {
+ return errIncorrectResultsCount(results)
+ }
+ return nil
+}
diff --git a/v2/cmd/integration-test/integration-test.go b/v2/cmd/integration-test/integration-test.go
index d55f74e0f..70bb193ac 100644
--- a/v2/cmd/integration-test/integration-test.go
+++ b/v2/cmd/integration-test/integration-test.go
@@ -29,6 +29,7 @@ func main() {
"workflow": workflowTestcases,
"loader": loaderTestcases,
"websocket": websocketTestCases,
+ "headless": headlessTestcases,
}
for proto, tests := range protocolTests {
if protocol == "" || protocol == proto {
diff --git a/v2/cmd/nuclei/main.go b/v2/cmd/nuclei/main.go
index 059a96167..bd015fede 100644
--- a/v2/cmd/nuclei/main.go
+++ b/v2/cmd/nuclei/main.go
@@ -134,7 +134,7 @@ on extensive configurability, massive extensibility and ease of use.`)
)
createGroup(flagSet, "headless", "Headless",
- flagSet.BoolVar(&options.Headless, "headless", false, "enable templates that require headless browser support"),
+ flagSet.BoolVar(&options.Headless, "headless", false, "enable templates that require headless browser support (root user on linux will disable sandbox)"),
flagSet.IntVar(&options.PageTimeout, "page-timeout", 20, "seconds to wait for each page in headless mode"),
flagSet.BoolVarP(&options.ShowBrowser, "show-browser", "sb", false, "show the browser on the screen when running templates with headless mode"),
flagSet.BoolVarP(&options.UseInstalledChrome, "system-chrome", "sc", false, "Use local installed chrome browser instead of nuclei installed"),
diff --git a/v2/go.mod b/v2/go.mod
index a48ceb5d2..77eda756f 100644
--- a/v2/go.mod
+++ b/v2/go.mod
@@ -67,6 +67,8 @@ require (
require github.com/aws/aws-sdk-go v1.42.3
+require github.com/projectdiscovery/folderutil v0.0.0-20211203091551-e81604e6940e
+
require (
git.mills.io/prologic/smtpd v0.0.0-20210710122116-a525b76c287a // indirect
github.com/PuerkitoBio/goquery v1.6.0 // indirect
@@ -79,7 +81,6 @@ require (
github.com/bits-and-blooms/bloom/v3 v3.0.1 // indirect
github.com/c4milo/unpackit v0.1.0 // indirect
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect
- github.com/dave/dst v0.26.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/dsnet/compress v0.0.1 // indirect
@@ -94,7 +95,6 @@ require (
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
- github.com/google/go-cmp v0.5.6 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gosuri/uilive v0.0.4 // indirect
@@ -129,14 +129,10 @@ require (
github.com/zclconf/go-cty v1.8.4 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
- golang.org/x/mod v0.4.2 // indirect
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
- golang.org/x/tools v0.1.3 // indirect
- golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/corvus-ch/zbase32.v1 v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
- mvdan.cc/gofumpt v0.1.1 // indirect
)
diff --git a/v2/go.sum b/v2/go.sum
index b3cb4a3ae..3ddd8b11d 100644
--- a/v2/go.sum
+++ b/v2/go.sum
@@ -165,7 +165,6 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/dave/dst v0.26.2 h1:lnxLAKI3tx7MgLNVDirFCsDTlTG9nKTk7GcptKcWSwY=
github.com/dave/dst v0.26.2/go.mod h1:UMDJuIRPfyUCC78eFuB+SV/WI8oDeyFDvM/JR6NI3IU=
github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ=
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
@@ -602,6 +601,10 @@ github.com/projectdiscovery/fileutil v0.0.0-20210914153648-31f843feaad4/go.mod h
github.com/projectdiscovery/fileutil v0.0.0-20210926202739-6050d0acf73c/go.mod h1:U+QCpQnX8o2N2w0VUGyAzjM3yBAe4BKedVElxiImsx0=
github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5 h1:2dbm7UhrAKnccZttr78CAmG768sSCd+MBn4ayLVDeqA=
github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5/go.mod h1:U+QCpQnX8o2N2w0VUGyAzjM3yBAe4BKedVElxiImsx0=
+github.com/projectdiscovery/folderutil v0.0.0-20210804143510-68474319fd84 h1:+VqGxv8ywpIHwGGSCOcGn/q5kkuB6F1AZtY42I8VnXc=
+github.com/projectdiscovery/folderutil v0.0.0-20210804143510-68474319fd84/go.mod h1:BMqXH4jNGByVdE2iLtKvc/6XStaiZRuCIaKv1vw9PnI=
+github.com/projectdiscovery/folderutil v0.0.0-20211203091551-e81604e6940e h1:ozfSeEc5j1f7NCEZAiAskP/KYfBD/TzPmFTIfh+CEwE=
+github.com/projectdiscovery/folderutil v0.0.0-20211203091551-e81604e6940e/go.mod h1:BMqXH4jNGByVdE2iLtKvc/6XStaiZRuCIaKv1vw9PnI=
github.com/projectdiscovery/goflags v0.0.7/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY=
github.com/projectdiscovery/goflags v0.0.8-0.20211028121123-edf02bc05b1a h1:EzwVm8i4zmzqZX55vrDtyfogwHh8AAZ3cWCJe4fEduk=
github.com/projectdiscovery/goflags v0.0.8-0.20211028121123-edf02bc05b1a/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY=
@@ -646,22 +649,7 @@ github.com/projectdiscovery/stringsutil v0.0.0-20210804142656-fd3c28dbaafe/go.mo
github.com/projectdiscovery/stringsutil v0.0.0-20210823090203-2f5f137e8e1d/go.mod h1:oTRc18WBv9t6BpaN9XBY+QmG28PUpsyDzRht56Qf49I=
github.com/projectdiscovery/stringsutil v0.0.0-20210830151154-f567170afdd9 h1:xbL1/7h0k6HE3RzPdYk9W/8pUxESrGWewTaZdIB5Pes=
github.com/projectdiscovery/stringsutil v0.0.0-20210830151154-f567170afdd9/go.mod h1:oTRc18WBv9t6BpaN9XBY+QmG28PUpsyDzRht56Qf49I=
-github.com/projectdiscovery/yamldoc-go v1.0.2 h1:SKb7PHgSOXm27Zci05ba0FxpyQiu6bGEiVMEcjCK1rQ=
github.com/projectdiscovery/yamldoc-go v1.0.2/go.mod h1:7uSxfMXaBmzvw8m5EhOEjB6nhz0rK/H9sUjq1ciZu24=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125104740-9b1096de655d h1:sXbcjsLPDgOrlGXgCKbT6MMyH/hTY3OJhhwsyM2bNlI=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125104740-9b1096de655d/go.mod h1:7uSxfMXaBmzvw8m5EhOEjB6nhz0rK/H9sUjq1ciZu24=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125105154-082a0a3cc326 h1:/fGqkG8GlfdvlTCfvAoSA/WgEAJrmCnW5qtdd7QXnwA=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125105154-082a0a3cc326/go.mod h1:7uSxfMXaBmzvw8m5EhOEjB6nhz0rK/H9sUjq1ciZu24=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125110259-585ff5584784 h1:Be2eD7oXNvCbFufVhvkiO5a0SRVN+Ri2V9pXKL2uJp8=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125110259-585ff5584784/go.mod h1:7uSxfMXaBmzvw8m5EhOEjB6nhz0rK/H9sUjq1ciZu24=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125112758-99d87632e49a h1:xGeZvil8Fe5LpGJbTGZGafnnbedNGtVcvLv3nYtmhXQ=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125112758-99d87632e49a/go.mod h1:7uSxfMXaBmzvw8m5EhOEjB6nhz0rK/H9sUjq1ciZu24=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125135034-67f4c31feb2b h1:oSBnxdyyDU/WpNKUAXrhydgx5+JtDT7KfJR+hOEaBXk=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125135034-67f4c31feb2b/go.mod h1:7uSxfMXaBmzvw8m5EhOEjB6nhz0rK/H9sUjq1ciZu24=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125135235-2e6dd74132d0 h1:mOptvTJ32yUuqQjjSfiPkPCelTWzqnts92uNOZBXZZo=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125135235-2e6dd74132d0/go.mod h1:7uSxfMXaBmzvw8m5EhOEjB6nhz0rK/H9sUjq1ciZu24=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125140040-b396ca47606e h1:0ZxOM0Q0/ESa24L/vq3fxs9YipxfHR4Y3jM/H2ReJ5E=
-github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211125140040-b396ca47606e/go.mod h1:7uSxfMXaBmzvw8m5EhOEjB6nhz0rK/H9sUjq1ciZu24=
github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211126104922-00d2c6bb43b6 h1:DvWRQpw7Ib2CRL3ogYm/BWM+X0UGPfz1n9Ix9YKgFM8=
github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211126104922-00d2c6bb43b6/go.mod h1:8OfZj8p/axkUM/TJoS/O9LDjj/S8u17rxRbqluE9CU4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -913,7 +901,6 @@ golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hM
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1132,7 +1119,6 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.3 h1:L69ShwSZEyCsLKoAxDKeMvLDZkumEe8gXUZAjab0tX8=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1274,7 +1260,6 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=
moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE=
-mvdan.cc/gofumpt v0.1.1 h1:bi/1aS/5W00E2ny5q65w9SnKpWEF/UIOqDYBILpo9rA=
mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
diff --git a/v2/internal/runner/runner.go b/v2/internal/runner/runner.go
index a5aea7e9e..b71587267 100644
--- a/v2/internal/runner/runner.go
+++ b/v2/internal/runner/runner.go
@@ -76,6 +76,9 @@ func New(options *types.Options) (*Runner, error) {
gologger.Warning().Msgf("Could not update templates: %s\n", err)
}
if options.Headless {
+ if engine.MustDisableSandbox() {
+ gologger.Warning().Msgf("The current platform and privileged user will run the browser without sandbox\n")
+ }
browser, err := engine.New(options)
if err != nil {
return nil, err
@@ -279,7 +282,7 @@ func (r *Runner) RunEnumeration() error {
if err := store.ValidateTemplates(r.options.Templates, r.options.Workflows); err != nil {
return err
}
- if stats.GetValue(parsers.SyntaxErrorStats) == 0 && stats.GetValue(parsers.SyntaxWarningStats) == 0 {
+ if stats.GetValue(parsers.SyntaxErrorStats) == 0 && stats.GetValue(parsers.SyntaxWarningStats) == 0 && stats.GetValue(parsers.RuntimeWarningsStats) == 0 {
gologger.Info().Msgf("All templates validated successfully\n")
} else {
return errors.New("encountered errors while performing template validation")
@@ -362,6 +365,7 @@ func (r *Runner) displayExecutionInfo(store *loader.Store) {
// Display stats for any loaded templates' syntax warnings or errors
stats.Display(parsers.SyntaxWarningStats)
stats.Display(parsers.SyntaxErrorStats)
+ stats.Display(parsers.RuntimeWarningsStats)
builder := &strings.Builder{}
if r.templatesConfig != nil && r.templatesConfig.NucleiLatestVersion != "" {
diff --git a/v2/pkg/catalog/config/config.go b/v2/pkg/catalog/config/config.go
index 87e6c672d..bb5cfb24f 100644
--- a/v2/pkg/catalog/config/config.go
+++ b/v2/pkg/catalog/config/config.go
@@ -26,7 +26,7 @@ type Config struct {
const nucleiConfigFilename = ".templates-config.json"
// Version is the current version of nuclei
-const Version = `2.5.4-dev`
+const Version = `2.5.4`
func getConfigDetails() (string, error) {
homeDir, err := os.UserHomeDir()
diff --git a/v2/pkg/catalog/loader/loader.go b/v2/pkg/catalog/loader/loader.go
index 12ddcfc39..23dfd980a 100644
--- a/v2/pkg/catalog/loader/loader.go
+++ b/v2/pkg/catalog/loader/loader.go
@@ -12,6 +12,7 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
templateTypes "github.com/projectdiscovery/nuclei/v2/pkg/templates/types"
"github.com/projectdiscovery/nuclei/v2/pkg/types"
+ "github.com/projectdiscovery/nuclei/v2/pkg/utils/stats"
)
// Config contains the configuration options for the loader
@@ -218,6 +219,7 @@ func (store *Store) LoadTemplates(templatesList []string) []*templates.Template
if loaded {
parsed, err := templates.Parse(templatePath, store.preprocessor, store.config.ExecutorOptions)
if err != nil {
+ stats.Increment(parsers.RuntimeWarningsStats)
gologger.Warning().Msgf("Could not parse template %s: %s\n", templatePath, err)
} else if parsed != nil {
loadedTemplates = append(loadedTemplates, parsed)
diff --git a/v2/pkg/parsers/parser.go b/v2/pkg/parsers/parser.go
index e26e7488e..c63922466 100644
--- a/v2/pkg/parsers/parser.go
+++ b/v2/pkg/parsers/parser.go
@@ -108,8 +108,9 @@ var (
)
const (
- SyntaxWarningStats = "syntax-warnings"
- SyntaxErrorStats = "syntax-errors"
+ SyntaxWarningStats = "syntax-warnings"
+ SyntaxErrorStats = "syntax-errors"
+ RuntimeWarningsStats = "runtime-warnings"
)
func init() {
@@ -118,6 +119,7 @@ func init() {
stats.NewEntry(SyntaxWarningStats, "Found %d templates with syntax warning (use -validate flag for further examination)")
stats.NewEntry(SyntaxErrorStats, "Found %d templates with syntax error (use -validate flag for further examination)")
+ stats.NewEntry(RuntimeWarningsStats, "Found %d templates with runtime error (use -validate flag for further examination)")
}
// ParseTemplate parses a template and returns a *templates.Template structure
diff --git a/v2/pkg/protocols/common/executer/executer.go b/v2/pkg/protocols/common/executer/executer.go
index fe650e945..16a3ede71 100644
--- a/v2/pkg/protocols/common/executer/executer.go
+++ b/v2/pkg/protocols/common/executer/executer.go
@@ -81,6 +81,10 @@ func (e *Executer) Execute(input string) (bool, error) {
}
gologger.Warning().Msgf("[%s] Could not execute request for %s: %s\n", e.options.TemplateID, input, err)
}
+ // If a match was found and stop at first match is set, break out of the loop and return
+ if results && (e.options.StopAtFirstMatch || e.options.Options.StopAtFirstMatch) {
+ break
+ }
}
return results, nil
}
@@ -89,6 +93,7 @@ func (e *Executer) Execute(input string) (bool, error) {
func (e *Executer) ExecuteWithResults(input string, callback protocols.OutputEventCallback) error {
dynamicValues := make(map[string]interface{})
previous := make(map[string]interface{})
+ var results bool
for _, req := range e.requests {
req := req
@@ -108,6 +113,7 @@ func (e *Executer) ExecuteWithResults(input string, callback protocols.OutputEve
if event.OperatorsResult == nil {
return
}
+ results = true
callback(event)
})
if err != nil {
@@ -118,6 +124,10 @@ func (e *Executer) ExecuteWithResults(input string, callback protocols.OutputEve
}
gologger.Warning().Msgf("[%s] Could not execute request for %s: %s\n", e.options.TemplateID, input, err)
}
+ // If a match was found and stop at first match is set, break out of the loop and return
+ if results && (e.options.StopAtFirstMatch || e.options.Options.StopAtFirstMatch) {
+ break
+ }
}
return nil
}
diff --git a/v2/pkg/protocols/common/generators/validate.go b/v2/pkg/protocols/common/generators/validate.go
index 305cae092..b04f8034c 100644
--- a/v2/pkg/protocols/common/generators/validate.go
+++ b/v2/pkg/protocols/common/generators/validate.go
@@ -4,9 +4,9 @@ import (
"errors"
"fmt"
"os"
- "path/filepath"
"strings"
+ "github.com/projectdiscovery/folderutil"
"github.com/projectdiscovery/nuclei/v2/pkg/types"
)
@@ -26,10 +26,16 @@ func (g *PayloadGenerator) validate(payloads map[string]interface{}, templatePat
}
changed := false
- pathTokens := strings.Split(templatePath, string(os.PathSeparator))
- for i := range pathTokens {
- payloadPath := filepath.Join(filepath.Join(pathTokens[:i]...), payloadType)
+ templatePathInfo, err := folderutil.NewPathInfo(templatePath)
+ if err != nil {
+ return err
+ }
+ payloadPathsToProbe, err := templatePathInfo.MeshWith(payloadType)
+ if err != nil {
+ return err
+ }
+ for _, payloadPath := range payloadPathsToProbe {
if fileExists(payloadPath) {
payloads[name] = payloadPath
changed = true
diff --git a/v2/pkg/protocols/dns/dns.go b/v2/pkg/protocols/dns/dns.go
index b3f3f7c1a..55be679a6 100644
--- a/v2/pkg/protocols/dns/dns.go
+++ b/v2/pkg/protocols/dns/dns.go
@@ -73,7 +73,7 @@ type Request struct {
// description: |
// Recursion determines if resolver should recurse all records to get fresh results.
- Recursion bool `yaml:"recursion,omitempty" jsonschema:"title=recurse all servers,description=Recursion determines if resolver should recurse all records to get fresh results"`
+ Recursion *bool `yaml:"recursion,omitempty" jsonschema:"title=recurse all servers,description=Recursion determines if resolver should recurse all records to get fresh results"`
// Resolvers to use for the dns requests
Resolvers []string `yaml:"resolvers,omitempty" jsonschema:"title=Resolvers,description=Define resolvers to use within the template"`
}
@@ -109,6 +109,13 @@ func (request *Request) GetID() string {
// Compile compiles the protocol request for further execution.
func (request *Request) Compile(options *protocols.ExecuterOptions) error {
+ if request.Retries == 0 {
+ request.Retries = 3
+ }
+ if request.Recursion == nil {
+ recursion := true
+ request.Recursion = &recursion
+ }
dnsClientOptions := &dnsclientpool.Configuration{
Retries: request.Retries,
}
@@ -172,7 +179,7 @@ func (request *Request) Make(domain string) (*dns.Msg, error) {
// Build a request on the specified URL
req := new(dns.Msg)
req.Id = dns.Id()
- req.RecursionDesired = request.Recursion
+ req.RecursionDesired = *request.Recursion
var q dns.Question
diff --git a/v2/pkg/protocols/dns/dns_test.go b/v2/pkg/protocols/dns/dns_test.go
index f44ba2205..0aae3ed20 100644
--- a/v2/pkg/protocols/dns/dns_test.go
+++ b/v2/pkg/protocols/dns/dns_test.go
@@ -24,6 +24,7 @@ func TestGenerateDNSVariables(t *testing.T) {
func TestDNSCompileMake(t *testing.T) {
options := testutils.DefaultOptions
+ recursion := false
testutils.Init(options)
const templateID = "testing-dns"
request := &Request{
@@ -31,7 +32,7 @@ func TestDNSCompileMake(t *testing.T) {
Class: "INET",
Retries: 5,
ID: templateID,
- Recursion: false,
+ Recursion: &recursion,
Name: "{{FQDN}}",
}
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
diff --git a/v2/pkg/protocols/dns/operators_test.go b/v2/pkg/protocols/dns/operators_test.go
index a472aa230..b8137bc26 100644
--- a/v2/pkg/protocols/dns/operators_test.go
+++ b/v2/pkg/protocols/dns/operators_test.go
@@ -20,6 +20,7 @@ import (
func TestResponseToDSLMap(t *testing.T) {
options := testutils.DefaultOptions
+ recursion := false
testutils.Init(options)
templateID := "testing-dns"
request := &Request{
@@ -27,7 +28,7 @@ func TestResponseToDSLMap(t *testing.T) {
Class: "INET",
Retries: 5,
ID: templateID,
- Recursion: false,
+ Recursion: &recursion,
Name: "{{FQDN}}",
}
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
@@ -52,6 +53,7 @@ func TestResponseToDSLMap(t *testing.T) {
func TestDNSOperatorMatch(t *testing.T) {
options := testutils.DefaultOptions
+ recursion := false
testutils.Init(options)
templateID := "testing-dns"
request := &Request{
@@ -59,7 +61,7 @@ func TestDNSOperatorMatch(t *testing.T) {
Class: "INET",
Retries: 5,
ID: templateID,
- Recursion: false,
+ Recursion: &recursion,
Name: "{{FQDN}}",
}
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
@@ -163,6 +165,7 @@ func TestDNSOperatorMatch(t *testing.T) {
func TestDNSOperatorExtract(t *testing.T) {
options := testutils.DefaultOptions
+ recursion := false
testutils.Init(options)
templateID := "testing-dns"
request := &Request{
@@ -170,7 +173,7 @@ func TestDNSOperatorExtract(t *testing.T) {
Class: "INET",
Retries: 5,
ID: templateID,
- Recursion: false,
+ Recursion: &recursion,
Name: "{{FQDN}}",
}
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
@@ -220,6 +223,7 @@ func TestDNSOperatorExtract(t *testing.T) {
func TestDNSMakeResult(t *testing.T) {
options := testutils.DefaultOptions
+ recursion := false
testutils.Init(options)
templateID := "testing-dns"
request := &Request{
@@ -227,7 +231,7 @@ func TestDNSMakeResult(t *testing.T) {
Class: "INET",
Retries: 5,
ID: templateID,
- Recursion: false,
+ Recursion: &recursion,
Name: "{{FQDN}}",
Operators: operators.Operators{
Matchers: []*matchers.Matcher{{
diff --git a/v2/pkg/protocols/dns/request_test.go b/v2/pkg/protocols/dns/request_test.go
index 46acc9937..5cf355450 100644
--- a/v2/pkg/protocols/dns/request_test.go
+++ b/v2/pkg/protocols/dns/request_test.go
@@ -17,6 +17,7 @@ import (
func TestDNSExecuteWithResults(t *testing.T) {
options := testutils.DefaultOptions
+ recursion := false
testutils.Init(options)
templateID := "testing-dns"
request := &Request{
@@ -24,7 +25,7 @@ func TestDNSExecuteWithResults(t *testing.T) {
Class: "INET",
Retries: 5,
ID: templateID,
- Recursion: false,
+ Recursion: &recursion,
Name: "{{FQDN}}",
Operators: operators.Operators{
Matchers: []*matchers.Matcher{{
diff --git a/v2/pkg/protocols/headless/engine/engine.go b/v2/pkg/protocols/headless/engine/engine.go
index d73e79044..6c1046b67 100644
--- a/v2/pkg/protocols/headless/engine/engine.go
+++ b/v2/pkg/protocols/headless/engine/engine.go
@@ -5,6 +5,7 @@ import (
"io/ioutil"
"net/http"
"os"
+ "runtime"
"strings"
"github.com/corpix/uarand"
@@ -44,12 +45,15 @@ func New(options *types.Options) (*Browser, error) {
Set("disable-notifications", "true").
Set("hide-scrollbars", "true").
Set("window-size", fmt.Sprintf("%d,%d", 1080, 1920)).
- Set("no-sandbox", "true").
Set("mute-audio", "true").
Set("incognito", "true").
Delete("use-mock-keychain").
UserDataDir(dataStore)
+ if MustDisableSandbox() {
+ chromeLauncher = chromeLauncher.NoSandbox(true)
+ }
+
if options.UseInstalledChrome {
if chromePath, hasChrome := launcher.LookPath(); hasChrome {
chromeLauncher.Bin(chromePath)
@@ -105,6 +109,13 @@ func New(options *types.Options) (*Browser, error) {
return engine, nil
}
+// MustDisableSandbox determines if the current os and user needs sandbox mode disabled
+func MustDisableSandbox() bool {
+ // linux with root user needs "--no-sandbox" option
+ // https://github.com/chromium/chromium/blob/c4d3c31083a2e1481253ff2d24298a1dfe19c754/chrome/test/chromedriver/client/chromedriver.py#L209
+ return runtime.GOOS == "linux" && os.Geteuid() == 0
+}
+
// Close closes the browser engine
func (b *Browser) Close() {
b.engine.Close()
diff --git a/v2/pkg/protocols/http/request.go b/v2/pkg/protocols/http/request.go
index 41c11077b..d416dfb34 100644
--- a/v2/pkg/protocols/http/request.go
+++ b/v2/pkg/protocols/http/request.go
@@ -291,8 +291,8 @@ func (request *Request) ExecuteWithResults(reqURL string, dynamicValues, previou
requestCount++
request.options.Progress.IncrementRequests()
- // If this was a match and we want to stop at first match, skip all further requests.
- if (generatedHttpRequest.original.options.Options.StopAtFirstMatch || request.StopAtFirstMatch) && gotOutput {
+ // If this was a match, and we want to stop at first match, skip all further requests.
+ if (generatedHttpRequest.original.options.Options.StopAtFirstMatch || generatedHttpRequest.original.options.StopAtFirstMatch || request.StopAtFirstMatch) && gotOutput {
return true, nil
}
return false, nil
diff --git a/v2/pkg/protocols/protocols.go b/v2/pkg/protocols/protocols.go
index f288b717f..fb7f04a1b 100644
--- a/v2/pkg/protocols/protocols.go
+++ b/v2/pkg/protocols/protocols.go
@@ -61,6 +61,8 @@ type ExecuterOptions struct {
Interactsh *interactsh.Client
// HostErrorsCache is an optional cache for handling host errors
HostErrorsCache *hosterrorscache.Cache
+ // Stop execution once first match is found
+ StopAtFirstMatch bool
Operators []*operators.Operators // only used by offlinehttp module
diff --git a/v2/pkg/templates/compile.go b/v2/pkg/templates/compile.go
index 092ec1d3e..4171d3760 100644
--- a/v2/pkg/templates/compile.go
+++ b/v2/pkg/templates/compile.go
@@ -69,6 +69,7 @@ func Parse(filePath string, preprocessor Preprocessor, options protocols.Execute
options.TemplateID = template.ID
options.TemplateInfo = template.Info
options.TemplatePath = filePath
+ options.StopAtFirstMatch = template.StopAtFirstMatch
// If no requests, and it is also not a workflow, return error.
if template.Requests() == 0 {
diff --git a/v2/pkg/templates/templates.go b/v2/pkg/templates/templates.go
index 7909bf8e9..586c898f4 100644
--- a/v2/pkg/templates/templates.go
+++ b/v2/pkg/templates/templates.go
@@ -74,6 +74,9 @@ type Template struct {
// description: |
// Self Contained marks Requests for the template as self-contained
SelfContained bool `yaml:"self-contained,omitempty" jsonschema:"title=mark requests as self-contained,description=Mark Requests for the template as self-contained"`
+ // description: |
+ // Stop execution once first match is found
+ StopAtFirstMatch bool `yaml:"stop-at-first-match,omitempty" jsonschema:"title=stop at first match,description=Stop at first match for the template"`
// description: |
// Signature is the request signature method
diff --git a/v2/pkg/templates/templates_doc.go b/v2/pkg/templates/templates_doc.go
index 6f21ce82c..e1b739e9f 100644
--- a/v2/pkg/templates/templates_doc.go
+++ b/v2/pkg/templates/templates_doc.go
@@ -16,14 +16,20 @@ var (
MODELClassificationDoc encoder.Doc
HTTPRequestDoc encoder.Doc
MATCHERSMatcherDoc encoder.Doc
+ MatcherTypeHolderDoc encoder.Doc
EXTRACTORSExtractorDoc encoder.Doc
+ ExtractorTypeHolderDoc encoder.Doc
GENERATORSAttackTypeHolderDoc encoder.Doc
+ HTTPMethodTypeHolderDoc encoder.Doc
DNSRequestDoc encoder.Doc
+ DNSRequestTypeHolderDoc encoder.Doc
FILERequestDoc encoder.Doc
NETWORKRequestDoc encoder.Doc
NETWORKInputDoc encoder.Doc
+ NetworkInputTypeHolderDoc encoder.Doc
HEADLESSRequestDoc encoder.Doc
ENGINEActionDoc encoder.Doc
+ ActionTypeHolderDoc encoder.Doc
SSLRequestDoc encoder.Doc
WEBSOCKETRequestDoc encoder.Doc
WEBSOCKETInputDoc encoder.Doc
@@ -35,7 +41,7 @@ func init() {
TemplateDoc.Type = "Template"
TemplateDoc.Comments[encoder.LineComment] = " Template is a YAML input file which defines all the requests and"
TemplateDoc.Description = "Template is a YAML input file which defines all the requests and\n other metadata for a template."
- TemplateDoc.Fields = make([]encoder.Doc, 11)
+ TemplateDoc.Fields = make([]encoder.Doc, 12)
TemplateDoc.Fields[0].Name = "id"
TemplateDoc.Fields[0].Type = "string"
TemplateDoc.Fields[0].Note = ""
@@ -103,6 +109,11 @@ func init() {
TemplateDoc.Fields[10].Note = ""
TemplateDoc.Fields[10].Description = "Self Contained marks Requests for the template as self-contained"
TemplateDoc.Fields[10].Comments[encoder.LineComment] = "Self Contained marks Requests for the template as self-contained"
+ TemplateDoc.Fields[11].Name = "stop-at-first-match"
+ TemplateDoc.Fields[11].Type = "bool"
+ TemplateDoc.Fields[11].Note = ""
+ TemplateDoc.Fields[11].Description = "Stop execution once first match is found"
+ TemplateDoc.Fields[11].Comments[encoder.LineComment] = "Stop execution once first match is found"
MODELInfoDoc.Type = "model.Info"
MODELInfoDoc.Comments[encoder.LineComment] = " Info contains metadata information about a template"
@@ -230,7 +241,20 @@ func init() {
FieldName: "severity",
},
}
- SEVERITYHolderDoc.Fields = make([]encoder.Doc, 0)
+ SEVERITYHolderDoc.Fields = make([]encoder.Doc, 1)
+ SEVERITYHolderDoc.Fields[0].Name = ""
+ SEVERITYHolderDoc.Fields[0].Type = "Severity"
+ SEVERITYHolderDoc.Fields[0].Note = ""
+ SEVERITYHolderDoc.Fields[0].Description = ""
+ SEVERITYHolderDoc.Fields[0].Comments[encoder.LineComment] = ""
+ SEVERITYHolderDoc.Fields[0].EnumFields = []string{
+ "undefined",
+ "info",
+ "low",
+ "medium",
+ "high",
+ "critical",
+ }
MODELClassificationDoc.Type = "model.Classification"
MODELClassificationDoc.Comments[encoder.LineComment] = ""
@@ -282,6 +306,72 @@ func init() {
FieldName: "requests",
},
}
+ HTTPRequestDoc.PartDefinitions = []encoder.KeyValue{
+ {
+ Key: "template-id",
+ Value: "ID of the template executed",
+ },
+ {
+ Key: "template-info",
+ Value: "Info Block of the template executed",
+ },
+ {
+ Key: "template-path",
+ Value: "Path of the template executed",
+ },
+ {
+ Key: "host",
+ Value: "Host is the input to the template",
+ },
+ {
+ Key: "matched",
+ Value: "Matched is the input which was matched upon",
+ },
+ {
+ Key: "type",
+ Value: "Type is the type of request made",
+ },
+ {
+ Key: "request",
+ Value: "HTTP request made from the client",
+ },
+ {
+ Key: "response",
+ Value: "HTTP response recieved from server",
+ },
+ {
+ Key: "status_code",
+ Value: "Status Code received from the Server",
+ },
+ {
+ Key: "body",
+ Value: "HTTP response body received from server (default)",
+ },
+ {
+ Key: "content_length",
+ Value: "HTTP Response content length",
+ },
+ {
+ Key: "header,all_headers",
+ Value: "HTTP response headers",
+ },
+ {
+ Key: "duration",
+ Value: "HTTP request time duration",
+ },
+ {
+ Key: "all",
+ Value: "HTTP response body + headers",
+ },
+ {
+ Key: "cookies_from_response",
+ Value: "HTTP response cookies in name:value format",
+ },
+ {
+ Key: "headers_from_response",
+ Value: "HTTP response headers in name:value format",
+ },
+ }
HTTPRequestDoc.Fields = make([]encoder.Doc, 27)
HTTPRequestDoc.Fields[0].Name = "matchers"
HTTPRequestDoc.Fields[0].Type = "[]matchers.Matcher"
@@ -585,6 +675,30 @@ func init() {
"true",
}
+ MatcherTypeHolderDoc.Type = "MatcherTypeHolder"
+ MatcherTypeHolderDoc.Comments[encoder.LineComment] = " MatcherTypeHolder is used to hold internal type of the matcher"
+ MatcherTypeHolderDoc.Description = "MatcherTypeHolder is used to hold internal type of the matcher"
+ MatcherTypeHolderDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "matchers.Matcher",
+ FieldName: "type",
+ },
+ }
+ MatcherTypeHolderDoc.Fields = make([]encoder.Doc, 1)
+ MatcherTypeHolderDoc.Fields[0].Name = ""
+ MatcherTypeHolderDoc.Fields[0].Type = "MatcherType"
+ MatcherTypeHolderDoc.Fields[0].Note = ""
+ MatcherTypeHolderDoc.Fields[0].Description = ""
+ MatcherTypeHolderDoc.Fields[0].Comments[encoder.LineComment] = ""
+ MatcherTypeHolderDoc.Fields[0].EnumFields = []string{
+ "word",
+ "regex",
+ "binary",
+ "status",
+ "size",
+ "dsl",
+ }
+
EXTRACTORSExtractorDoc.Type = "extractors.Extractor"
EXTRACTORSExtractorDoc.Comments[encoder.LineComment] = " Extractor is used to extract part of response using a regex."
EXTRACTORSExtractorDoc.Description = "Extractor is used to extract part of response using a regex."
@@ -699,6 +813,28 @@ func init() {
"true",
}
+ ExtractorTypeHolderDoc.Type = "ExtractorTypeHolder"
+ ExtractorTypeHolderDoc.Comments[encoder.LineComment] = " ExtractorTypeHolder is used to hold internal type of the extractor"
+ ExtractorTypeHolderDoc.Description = "ExtractorTypeHolder is used to hold internal type of the extractor"
+ ExtractorTypeHolderDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "extractors.Extractor",
+ FieldName: "type",
+ },
+ }
+ ExtractorTypeHolderDoc.Fields = make([]encoder.Doc, 1)
+ ExtractorTypeHolderDoc.Fields[0].Name = ""
+ ExtractorTypeHolderDoc.Fields[0].Type = "ExtractorType"
+ ExtractorTypeHolderDoc.Fields[0].Note = ""
+ ExtractorTypeHolderDoc.Fields[0].Description = ""
+ ExtractorTypeHolderDoc.Fields[0].Comments[encoder.LineComment] = ""
+ ExtractorTypeHolderDoc.Fields[0].EnumFields = []string{
+ "regex",
+ "kval",
+ "xpath",
+ "json",
+ }
+
GENERATORSAttackTypeHolderDoc.Type = "generators.AttackTypeHolder"
GENERATORSAttackTypeHolderDoc.Comments[encoder.LineComment] = " AttackTypeHolder is used to hold internal type of the protocol"
GENERATORSAttackTypeHolderDoc.Description = "AttackTypeHolder is used to hold internal type of the protocol"
@@ -716,7 +852,45 @@ func init() {
FieldName: "attack",
},
}
- GENERATORSAttackTypeHolderDoc.Fields = make([]encoder.Doc, 0)
+ GENERATORSAttackTypeHolderDoc.Fields = make([]encoder.Doc, 1)
+ GENERATORSAttackTypeHolderDoc.Fields[0].Name = ""
+ GENERATORSAttackTypeHolderDoc.Fields[0].Type = "AttackType"
+ GENERATORSAttackTypeHolderDoc.Fields[0].Note = ""
+ GENERATORSAttackTypeHolderDoc.Fields[0].Description = ""
+ GENERATORSAttackTypeHolderDoc.Fields[0].Comments[encoder.LineComment] = ""
+ GENERATORSAttackTypeHolderDoc.Fields[0].EnumFields = []string{
+ "batteringram",
+ "pitchfork",
+ "clusterbomb",
+ }
+
+ HTTPMethodTypeHolderDoc.Type = "HTTPMethodTypeHolder"
+ HTTPMethodTypeHolderDoc.Comments[encoder.LineComment] = " HTTPMethodTypeHolder is used to hold internal type of the HTTP Method"
+ HTTPMethodTypeHolderDoc.Description = "HTTPMethodTypeHolder is used to hold internal type of the HTTP Method"
+ HTTPMethodTypeHolderDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "http.Request",
+ FieldName: "method",
+ },
+ }
+ HTTPMethodTypeHolderDoc.Fields = make([]encoder.Doc, 1)
+ HTTPMethodTypeHolderDoc.Fields[0].Name = ""
+ HTTPMethodTypeHolderDoc.Fields[0].Type = "HTTPMethodType"
+ HTTPMethodTypeHolderDoc.Fields[0].Note = ""
+ HTTPMethodTypeHolderDoc.Fields[0].Description = ""
+ HTTPMethodTypeHolderDoc.Fields[0].Comments[encoder.LineComment] = ""
+ HTTPMethodTypeHolderDoc.Fields[0].EnumFields = []string{
+ "GET",
+ "GET",
+ "POST",
+ "PUT",
+ "DELETE",
+ "CONNECT",
+ "OPTIONS",
+ "TRACE",
+ "PATCH",
+ "PURGE",
+ }
DNSRequestDoc.Type = "dns.Request"
DNSRequestDoc.Comments[encoder.LineComment] = " Request contains a DNS protocol request to be made from a template"
@@ -729,6 +903,64 @@ func init() {
FieldName: "dns",
},
}
+ DNSRequestDoc.PartDefinitions = []encoder.KeyValue{
+ {
+ Key: "template-id",
+ Value: "ID of the template executed",
+ },
+ {
+ Key: "template-info",
+ Value: "Info Block of the template executed",
+ },
+ {
+ Key: "template-path",
+ Value: "Path of the template executed",
+ },
+ {
+ Key: "host",
+ Value: "Host is the input to the template",
+ },
+ {
+ Key: "matched",
+ Value: "Matched is the input which was matched upon",
+ },
+ {
+ Key: "request",
+ Value: "Request contains the DNS request in text format",
+ },
+ {
+ Key: "type",
+ Value: "Type is the type of request made",
+ },
+ {
+ Key: "rcode",
+ Value: "Rcode field returned for the DNS request",
+ },
+ {
+ Key: "question",
+ Value: "Question contains the DNS question field",
+ },
+ {
+ Key: "extra",
+ Value: "Extra contains the DNS response extra field",
+ },
+ {
+ Key: "answer",
+ Value: "Answer contains the DNS response answer field",
+ },
+ {
+ Key: "ns",
+ Value: "NS contains the DNS response NS field",
+ },
+ {
+ Key: "raw,body,all",
+ Value: "Raw contains the raw DNS response (default)",
+ },
+ {
+ Key: "trace",
+ Value: "Trace contains trace data for DNS request if enabled",
+ },
+ }
DNSRequestDoc.Fields = make([]encoder.Doc, 12)
DNSRequestDoc.Fields[0].Name = "matchers"
DNSRequestDoc.Fields[0].Type = "[]matchers.Matcher"
@@ -799,7 +1031,7 @@ func init() {
DNSRequestDoc.Fields[9].AddExample("Use a retry of 100 to 150 generally", 100)
DNSRequestDoc.Fields[10].Name = "recursion"
- DNSRequestDoc.Fields[10].Type = "bool"
+ DNSRequestDoc.Fields[10].Type = "dns.bool"
DNSRequestDoc.Fields[10].Note = ""
DNSRequestDoc.Fields[10].Description = "Recursion determines if resolver should recurse all records to get fresh results."
DNSRequestDoc.Fields[10].Comments[encoder.LineComment] = "Recursion determines if resolver should recurse all records to get fresh results."
@@ -809,6 +1041,33 @@ func init() {
DNSRequestDoc.Fields[11].Description = "Resolvers to use for the dns requests"
DNSRequestDoc.Fields[11].Comments[encoder.LineComment] = " Resolvers to use for the dns requests"
+ DNSRequestTypeHolderDoc.Type = "DNSRequestTypeHolder"
+ DNSRequestTypeHolderDoc.Comments[encoder.LineComment] = " DNSRequestTypeHolder is used to hold internal type of the DNS type"
+ DNSRequestTypeHolderDoc.Description = "DNSRequestTypeHolder is used to hold internal type of the DNS type"
+ DNSRequestTypeHolderDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "dns.Request",
+ FieldName: "type",
+ },
+ }
+ DNSRequestTypeHolderDoc.Fields = make([]encoder.Doc, 1)
+ DNSRequestTypeHolderDoc.Fields[0].Name = ""
+ DNSRequestTypeHolderDoc.Fields[0].Type = "DNSRequestType"
+ DNSRequestTypeHolderDoc.Fields[0].Note = ""
+ DNSRequestTypeHolderDoc.Fields[0].Description = ""
+ DNSRequestTypeHolderDoc.Fields[0].Comments[encoder.LineComment] = ""
+ DNSRequestTypeHolderDoc.Fields[0].EnumFields = []string{
+ "A",
+ "NS",
+ "DS",
+ "CNAME",
+ "SOA",
+ "PTR",
+ "MX",
+ "TXT",
+ "AAAA",
+ }
+
FILERequestDoc.Type = "file.Request"
FILERequestDoc.Comments[encoder.LineComment] = " Request contains a File matching mechanism for local disk operations."
FILERequestDoc.Description = "Request contains a File matching mechanism for local disk operations."
@@ -820,6 +1079,36 @@ func init() {
FieldName: "file",
},
}
+ FILERequestDoc.PartDefinitions = []encoder.KeyValue{
+ {
+ Key: "template-id",
+ Value: "ID of the template executed",
+ },
+ {
+ Key: "template-info",
+ Value: "Info Block of the template executed",
+ },
+ {
+ Key: "template-path",
+ Value: "Path of the template executed",
+ },
+ {
+ Key: "matched",
+ Value: "Matched is the input which was matched upon",
+ },
+ {
+ Key: "path",
+ Value: "Path is the path of file on local filesystem",
+ },
+ {
+ Key: "type",
+ Value: "Type is the type of request made",
+ },
+ {
+ Key: "raw,body,all,data",
+ Value: "Raw contains the raw file contents",
+ },
+ }
FILERequestDoc.Fields = make([]encoder.Doc, 8)
FILERequestDoc.Fields[0].Name = "matchers"
FILERequestDoc.Fields[0].Type = "[]matchers.Matcher"
@@ -883,6 +1172,44 @@ func init() {
FieldName: "network",
},
}
+ NETWORKRequestDoc.PartDefinitions = []encoder.KeyValue{
+ {
+ Key: "template-id",
+ Value: "ID of the template executed",
+ },
+ {
+ Key: "template-info",
+ Value: "Info Block of the template executed",
+ },
+ {
+ Key: "template-path",
+ Value: "Path of the template executed",
+ },
+ {
+ Key: "host",
+ Value: "Host is the input to the template",
+ },
+ {
+ Key: "matched",
+ Value: "Matched is the input which was matched upon",
+ },
+ {
+ Key: "type",
+ Value: "Type is the type of request made",
+ },
+ {
+ Key: "request",
+ Value: "Network request made from the client",
+ },
+ {
+ Key: "body,all,data",
+ Value: "Network response recieved from server (default)",
+ },
+ {
+ Key: "raw",
+ Value: "Full Network protocol data",
+ },
+ }
NETWORKRequestDoc.Fields = make([]encoder.Doc, 10)
NETWORKRequestDoc.Fields[0].Name = "id"
NETWORKRequestDoc.Fields[0].Type = "string"
@@ -988,6 +1315,26 @@ func init() {
NETWORKInputDoc.Fields[3].AddExample("", "prefix")
+ NetworkInputTypeHolderDoc.Type = "NetworkInputTypeHolder"
+ NetworkInputTypeHolderDoc.Comments[encoder.LineComment] = " NetworkInputTypeHolder is used to hold internal type of the Network type"
+ NetworkInputTypeHolderDoc.Description = "NetworkInputTypeHolder is used to hold internal type of the Network type"
+ NetworkInputTypeHolderDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "network.Input",
+ FieldName: "type",
+ },
+ }
+ NetworkInputTypeHolderDoc.Fields = make([]encoder.Doc, 1)
+ NetworkInputTypeHolderDoc.Fields[0].Name = ""
+ NetworkInputTypeHolderDoc.Fields[0].Type = "NetworkInputType"
+ NetworkInputTypeHolderDoc.Fields[0].Note = ""
+ NetworkInputTypeHolderDoc.Fields[0].Description = ""
+ NetworkInputTypeHolderDoc.Fields[0].Comments[encoder.LineComment] = ""
+ NetworkInputTypeHolderDoc.Fields[0].EnumFields = []string{
+ "hex",
+ "text",
+ }
+
HEADLESSRequestDoc.Type = "headless.Request"
HEADLESSRequestDoc.Comments[encoder.LineComment] = " Request contains a Headless protocol request to be made from a template"
HEADLESSRequestDoc.Description = "Request contains a Headless protocol request to be made from a template"
@@ -997,6 +1344,40 @@ func init() {
FieldName: "headless",
},
}
+ HEADLESSRequestDoc.PartDefinitions = []encoder.KeyValue{
+ {
+ Key: "template-id",
+ Value: "ID of the template executed",
+ },
+ {
+ Key: "template-info",
+ Value: "Info Block of the template executed",
+ },
+ {
+ Key: "template-path",
+ Value: "Path of the template executed",
+ },
+ {
+ Key: "host",
+ Value: "Host is the input to the template",
+ },
+ {
+ Key: "matched",
+ Value: "Matched is the input which was matched upon",
+ },
+ {
+ Key: "type",
+ Value: "Type is the type of request made",
+ },
+ {
+ Key: "req",
+ Value: "Headless request made from the client",
+ },
+ {
+ Key: "resp,body,data",
+ Value: "Headless response recieved from client (default)",
+ },
+ }
HEADLESSRequestDoc.Fields = make([]encoder.Doc, 5)
HEADLESSRequestDoc.Fields[0].Name = "id"
HEADLESSRequestDoc.Fields[0].Type = "string"
@@ -1059,6 +1440,46 @@ func init() {
ENGINEActionDoc.Fields[3].Description = "Action is the type of the action to perform."
ENGINEActionDoc.Fields[3].Comments[encoder.LineComment] = "Action is the type of the action to perform."
+ ActionTypeHolderDoc.Type = "ActionTypeHolder"
+ ActionTypeHolderDoc.Comments[encoder.LineComment] = " ActionTypeHolder is used to hold internal type of the action"
+ ActionTypeHolderDoc.Description = "ActionTypeHolder is used to hold internal type of the action"
+ ActionTypeHolderDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "engine.Action",
+ FieldName: "action",
+ },
+ }
+ ActionTypeHolderDoc.Fields = make([]encoder.Doc, 1)
+ ActionTypeHolderDoc.Fields[0].Name = ""
+ ActionTypeHolderDoc.Fields[0].Type = "ActionType"
+ ActionTypeHolderDoc.Fields[0].Note = ""
+ ActionTypeHolderDoc.Fields[0].Description = ""
+ ActionTypeHolderDoc.Fields[0].Comments[encoder.LineComment] = ""
+ ActionTypeHolderDoc.Fields[0].EnumFields = []string{
+ "navigate",
+ "script",
+ "click",
+ "rightclick",
+ "text",
+ "screenshot",
+ "time",
+ "select",
+ "files",
+ "waitload",
+ "getresource",
+ "extract",
+ "setmethod",
+ "addheader",
+ "setheader",
+ "deleteheader",
+ "setbody",
+ "waitevent",
+ "keyboard",
+ "debug",
+ "sleep",
+ "waitvisible",
+ }
+
SSLRequestDoc.Type = "ssl.Request"
SSLRequestDoc.Comments[encoder.LineComment] = " Request is a request for the SSL protocol"
SSLRequestDoc.Description = "Request is a request for the SSL protocol"
@@ -1068,6 +1489,28 @@ func init() {
FieldName: "ssl",
},
}
+ SSLRequestDoc.PartDefinitions = []encoder.KeyValue{
+ {
+ Key: "type",
+ Value: "Type is the type of request made",
+ },
+ {
+ Key: "response",
+ Value: "JSON SSL protocol handshake details",
+ },
+ {
+ Key: "not_after",
+ Value: "Timestamp after which the remote cert expires",
+ },
+ {
+ Key: "host",
+ Value: "Host is the input to the template",
+ },
+ {
+ Key: "matched",
+ Value: "Matched is the input which was matched upon",
+ },
+ }
SSLRequestDoc.Fields = make([]encoder.Doc, 4)
SSLRequestDoc.Fields[0].Name = "matchers"
SSLRequestDoc.Fields[0].Type = "[]matchers.Matcher"
@@ -1103,6 +1546,32 @@ func init() {
FieldName: "websocket",
},
}
+ WEBSOCKETRequestDoc.PartDefinitions = []encoder.KeyValue{
+ {
+ Key: "type",
+ Value: "Type is the type of request made",
+ },
+ {
+ Key: "success",
+ Value: "Success specifies whether websocket connection was successful",
+ },
+ {
+ Key: "request",
+ Value: "Websocket request made to the server",
+ },
+ {
+ Key: "response",
+ Value: "Websocket response recieved from the server",
+ },
+ {
+ Key: "host",
+ Value: "Host is the input to the template",
+ },
+ {
+ Key: "matched",
+ Value: "Matched is the input which was matched upon",
+ },
+ }
WEBSOCKETRequestDoc.Fields = make([]encoder.Doc, 8)
WEBSOCKETRequestDoc.Fields[0].Name = "matchers"
WEBSOCKETRequestDoc.Fields[0].Type = "[]matchers.Matcher"
@@ -1254,14 +1723,20 @@ func GetTemplateDoc() *encoder.FileDoc {
&MODELClassificationDoc,
&HTTPRequestDoc,
&MATCHERSMatcherDoc,
+ &MatcherTypeHolderDoc,
&EXTRACTORSExtractorDoc,
+ &ExtractorTypeHolderDoc,
&GENERATORSAttackTypeHolderDoc,
+ &HTTPMethodTypeHolderDoc,
&DNSRequestDoc,
+ &DNSRequestTypeHolderDoc,
&FILERequestDoc,
&NETWORKRequestDoc,
&NETWORKInputDoc,
+ &NetworkInputTypeHolderDoc,
&HEADLESSRequestDoc,
&ENGINEActionDoc,
+ &ActionTypeHolderDoc,
&SSLRequestDoc,
&WEBSOCKETRequestDoc,
&WEBSOCKETInputDoc,
diff --git a/v2/pkg/templates/templates_doc_examples.go b/v2/pkg/templates/templates_doc_examples.go
index bb51dd154..c43b479ab 100644
--- a/v2/pkg/templates/templates_doc_examples.go
+++ b/v2/pkg/templates/templates_doc_examples.go
@@ -36,12 +36,13 @@ var (
}
_ = exampleNormalHTTPRequest
+ recursion = false
exampleNormalDNSRequest = &dns.Request{
Name: "{{FQDN}}",
RequestType: dns.DNSRequestTypeHolder{DNSRequestType: dns.CNAME},
Class: "inet",
Retries: 2,
- Recursion: true,
+ Recursion: &recursion,
Operators: operators.Operators{
Extractors: []*extractors.Extractor{
{Type: extractors.ExtractorTypeHolder{ExtractorType: extractors.RegexExtractor}, Regex: []string{"ec2-[-\\d]+\\.compute[-\\d]*\\.amazonaws\\.com", "ec2-[-\\d]+\\.[\\w\\d\\-]+\\.compute[-\\d]*\\.amazonaws\\.com"}},