diff --git a/cmd/integration-test/http.go b/cmd/integration-test/http.go
index e1fddc04..41e1910a 100644
--- a/cmd/integration-test/http.go
+++ b/cmd/integration-test/http.go
@@ -80,6 +80,55 @@ var httpTestcases = []TestCaseInfo{
{Path: "protocols/http/disable-path-automerge.yaml", TestCase: &httpDisablePathAutomerge{}},
{Path: "protocols/http/http-preprocessor.yaml", TestCase: &httpPreprocessor{}},
{Path: "protocols/http/multi-request.yaml", TestCase: &httpMultiRequest{}},
+ {Path: "protocols/http/http-matcher-extractor-dy-extractor.yaml", TestCase: &httpMatcherExtractorDynamicExtractor{}},
+ {Path: "protocols/http/multi-http-var-sharing.yaml", TestCase: &httpMultiVarSharing{}},
+}
+
+type httpMultiVarSharing struct{}
+
+func (h *httpMultiVarSharing) Execute(filePath string) error {
+ results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "https://scanme.sh", debug)
+ if err != nil {
+ return err
+ }
+ return expectResultsCount(results, 1)
+}
+
+type httpMatcherExtractorDynamicExtractor struct{}
+
+func (h *httpMatcherExtractorDynamicExtractor) Execute(filePath string) error {
+ router := httprouter.New()
+ router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ html := `
+
+
+ Domains
+
+`
+ fmt.Fprint(w, html)
+ })
+ router.GET("/domains", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ html := `
+
+
+ Dynamic Extractor Test
+
+
+
+
+
+ `
+ fmt.Fprint(w, html)
+ })
+ ts := httptest.NewServer(router)
+ defer ts.Close()
+
+ results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
+ if err != nil {
+ return err
+ }
+
+ return expectResultsCount(results, 1)
}
type httpInteractshRequest struct{}
@@ -776,8 +825,18 @@ func (h *httpPaths) Execute(filepath string) error {
}
}
- if !reflect.DeepEqual(expected, actual) {
- return fmt.Errorf("%8v: %v\n%-8v: %v", "expected", expected, "actual", actual)
+ if len(expected) > len(actual) {
+ actualValuesIndex := len(actual) - 1
+ if actualValuesIndex < 0 {
+ actualValuesIndex = 0
+ }
+ return fmt.Errorf("missing values : %v", expected[actualValuesIndex:])
+ } else if len(expected) < len(actual) {
+ return fmt.Errorf("unexpected values : %v", actual[len(expected)-1:])
+ } else {
+ if !reflect.DeepEqual(expected, actual) {
+ return fmt.Errorf("expected: %v\n\nactual: %v", expected, actual)
+ }
}
return nil
}
diff --git a/cmd/nuclei/main.go b/cmd/nuclei/main.go
index d16078db..684ea618 100644
--- a/cmd/nuclei/main.go
+++ b/cmd/nuclei/main.go
@@ -322,6 +322,7 @@ on extensive configurability, massive extensibility and ease of use.`)
flagSet.IntVarP(&options.HeadlessBulkSize, "headless-bulk-size", "hbs", 10, "maximum number of headless hosts to be analyzed in parallel per template"),
flagSet.IntVarP(&options.HeadlessTemplateThreads, "headless-concurrency", "headc", 10, "maximum number of headless templates to be executed in parallel"),
flagSet.IntVarP(&options.JsConcurrency, "js-concurrency", "jsc", 120, "maximum number of javascript runtimes to be executed in parallel"),
+ flagSet.IntVarP(&options.PayloadConcurrency, "payload-concurrency", "pc", 25, "max payload concurrency for each template"),
)
flagSet.CreateGroup("optimization", "Optimizations",
flagSet.IntVar(&options.Timeout, "timeout", 10, "time to wait in seconds before timeout"),
diff --git a/go.mod b/go.mod
index ca89a0eb..1d4d558d 100644
--- a/go.mod
+++ b/go.mod
@@ -20,12 +20,12 @@ require (
github.com/olekukonko/tablewriter v0.0.5
github.com/pkg/errors v0.9.1
github.com/projectdiscovery/clistats v0.0.20
- github.com/projectdiscovery/fastdialer v0.0.61
+ github.com/projectdiscovery/fastdialer v0.0.62
github.com/projectdiscovery/hmap v0.0.41
github.com/projectdiscovery/interactsh v1.1.9
github.com/projectdiscovery/rawhttp v0.1.40
github.com/projectdiscovery/retryabledns v1.0.58
- github.com/projectdiscovery/retryablehttp-go v1.0.50
+ github.com/projectdiscovery/retryablehttp-go v1.0.51
github.com/projectdiscovery/yamldoc-go v1.0.4
github.com/remeh/sizedwaitgroup v1.0.0
github.com/rs/xid v1.5.0
@@ -81,7 +81,7 @@ require (
github.com/projectdiscovery/goflags v0.1.42
github.com/projectdiscovery/gologger v1.1.12
github.com/projectdiscovery/gostruct v0.0.2
- github.com/projectdiscovery/gozero v0.0.2-0.20240305085154-99aa5ddb9f98
+ github.com/projectdiscovery/gozero v0.0.2
github.com/projectdiscovery/httpx v1.6.0
github.com/projectdiscovery/mapcidr v1.1.16
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5
@@ -91,7 +91,7 @@ require (
github.com/projectdiscovery/tlsx v1.1.6
github.com/projectdiscovery/uncover v1.0.7
github.com/projectdiscovery/useragent v0.0.40
- github.com/projectdiscovery/utils v0.0.82
+ github.com/projectdiscovery/utils v0.0.84-0.20240312214300-d3ba70dbb9ca
github.com/projectdiscovery/wappalyzergo v0.0.112
github.com/redis/go-redis/v9 v9.1.0
github.com/sashabaranov/go-openai v1.15.3
@@ -274,7 +274,6 @@ require (
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/trivago/tgo v1.0.7
github.com/ulikunitz/xz v0.5.11 // indirect
- github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/yl2chen/cidranger v1.0.2 // indirect
github.com/ysmood/goob v0.4.0 // indirect
diff --git a/go.sum b/go.sum
index 5c896ca7..952f4f93 100644
--- a/go.sum
+++ b/go.sum
@@ -812,8 +812,8 @@ github.com/projectdiscovery/clistats v0.0.20 h1:5jO5SLiRJ7f0nDV0ndBNmBeesbROouPo
github.com/projectdiscovery/clistats v0.0.20/go.mod h1:GJ2av0KnOvK0AISQnP8hyDclYIji1LVkx2l0pwnzAu4=
github.com/projectdiscovery/dsl v0.0.46 h1:zBNNzSBA1aakGY44w6KhnjJQx/zO+oW2Wx7TR8xZm/A=
github.com/projectdiscovery/dsl v0.0.46/go.mod h1:eoZEJFCIT5emI00xj8HTQwHz4YwwGAD+grL3G7CDlfs=
-github.com/projectdiscovery/fastdialer v0.0.61 h1:z5OzP9lRbn6fSIezgReKC3hkzRh+YX41ST9OgkVEm/s=
-github.com/projectdiscovery/fastdialer v0.0.61/go.mod h1:FyxJ0m1MwB69nLmdXYqK32f3a0Pf+5YpC8wBY73baiE=
+github.com/projectdiscovery/fastdialer v0.0.62 h1:Wyba2hD6ZF3S04MgCn380mC+1RXJ+dq14Yq8u2yk7ps=
+github.com/projectdiscovery/fastdialer v0.0.62/go.mod h1:2baj2TRXTw+hHbKTW9IZR4dhpxCGJkq5AKL1ge5gis8=
github.com/projectdiscovery/fasttemplate v0.0.2 h1:h2cISk5xDhlJEinlBQS6RRx0vOlOirB2y3Yu4PJzpiA=
github.com/projectdiscovery/fasttemplate v0.0.2/go.mod h1:XYWWVMxnItd+r0GbjA1GCsUopMw1/XusuQxdyAIHMCw=
github.com/projectdiscovery/freeport v0.0.5 h1:jnd3Oqsl4S8n0KuFkE5Hm8WGDP24ITBvmyw5pFTHS8Q=
@@ -828,6 +828,8 @@ github.com/projectdiscovery/gostruct v0.0.2 h1:s8gP8ApugGM4go1pA+sVlPDXaWqNP5BBD
github.com/projectdiscovery/gostruct v0.0.2/go.mod h1:H86peL4HKwMXcQQtEa6lmC8FuD9XFt6gkNR0B/Mu5PE=
github.com/projectdiscovery/gozero v0.0.2-0.20240305085154-99aa5ddb9f98 h1:KKS26wFrlcfPxKDmop+2NmI8HbGn8pgotHJBTh+3R4k=
github.com/projectdiscovery/gozero v0.0.2-0.20240305085154-99aa5ddb9f98/go.mod h1:/dHwbly+1lhOX9UreVure4lEe7K4hIHeu/c/wZGNTDo=
+github.com/projectdiscovery/gozero v0.0.2 h1:8fJeaCjxL9tpm33uG/RsCQs6HGM/NE6eA3cjkilRQ+E=
+github.com/projectdiscovery/gozero v0.0.2/go.mod h1:d8bZvDWW07LWNYWrwjZ4OO1I0cpkfqaysyDfSs9ibK8=
github.com/projectdiscovery/hmap v0.0.41 h1:8IgTyDce3/2JzcfPVA4H+XpBRFfETULx8td3BMdSYVE=
github.com/projectdiscovery/hmap v0.0.41/go.mod h1:bCrai6x5Eijqm2U+jtcH0wZX5ZcaZhcvzoMGTZgLAf0=
github.com/projectdiscovery/httpx v1.6.0 h1:6g4UoSQpsOyZgaK+SMLLnZIAU0eYyTxBUwVl+jtm0JQ=
@@ -852,8 +854,8 @@ github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917 h1:m03X4gB
github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917/go.mod h1:JxXtZC9e195awe7EynrcnBJmFoad/BNDzW9mzFkK8Sg=
github.com/projectdiscovery/retryabledns v1.0.58 h1:ut1FSB9+GZ6zQIlKJFLqIz2RZs81EmkbsHTuIrWfYLE=
github.com/projectdiscovery/retryabledns v1.0.58/go.mod h1:RobmKoNBgngAVE4H9REQtaLP1pa4TCyypHy1MWHT1mY=
-github.com/projectdiscovery/retryablehttp-go v1.0.50 h1:QiVqNzpDFoTZm2z1oO68FEtl2VTk2kFscPetxmCK2gc=
-github.com/projectdiscovery/retryablehttp-go v1.0.50/go.mod h1:yY/go76Lx/MgJmR+dSg/Xa8wOiXNjmuEdBDVDODKeDk=
+github.com/projectdiscovery/retryablehttp-go v1.0.51 h1:8XMrNC8JrwvySESe2d+XWF9bq4unWqD4PUPEC4Cai8s=
+github.com/projectdiscovery/retryablehttp-go v1.0.51/go.mod h1:6cdh/acYHpeYWg7+Iblh4xBRb87bC118L4G4mpvCMuA=
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=
@@ -864,8 +866,8 @@ github.com/projectdiscovery/uncover v1.0.7 h1:ut+2lTuvmftmveqF5RTjMWAgyLj8ltPQC7
github.com/projectdiscovery/uncover v1.0.7/go.mod h1:HFXgm1sRPuoN0D4oATljPIdmbo/EEh1wVuxQqo/dwFE=
github.com/projectdiscovery/useragent v0.0.40 h1:1LUhReSGPkhqsM5n40OOC9dIoNqMGs1dyGFJcOmg2Fo=
github.com/projectdiscovery/useragent v0.0.40/go.mod h1:EvK1x3s948Gtqb/XOahXcauyejCL/rSgy5d1IAvsKT4=
-github.com/projectdiscovery/utils v0.0.82 h1:U//02floCSFxJluN7MP+rJSwI4Px7o454JL7ukERArI=
-github.com/projectdiscovery/utils v0.0.82/go.mod h1:AbmIvy0TTlsfXxPDEMaNPVrxmqDmYiCnbGqh0TTthE4=
+github.com/projectdiscovery/utils v0.0.84-0.20240312214300-d3ba70dbb9ca h1:GY9lUYDlENXPSFPJH01Bm1BfhrUF2jpnUBR+K4VPJIs=
+github.com/projectdiscovery/utils v0.0.84-0.20240312214300-d3ba70dbb9ca/go.mod h1:wzMfHBq2I9oy+DEiMfUYV86g1D7eXKaQsgWnqFpmMtI=
github.com/projectdiscovery/wappalyzergo v0.0.112 h1:QPpp5jmj1lqLd5mFdFKQ9VvcYhQNqyU9Mr+IB0US2zA=
github.com/projectdiscovery/wappalyzergo v0.0.112/go.mod h1:hc/o+fgM8KtdpFesjfBTmHTwsR+yBd+4kYZW/DGy/x8=
github.com/projectdiscovery/yamldoc-go v1.0.4 h1:eZoESapnMw6WAHiVgRwNqvbJEfNHEH148uthhFbG5jE=
@@ -1034,8 +1036,6 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
-github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6 h1:TtyC78WMafNW8QFfv3TeP3yWNDG+uxNkk9vOrnDu6JA=
-github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6/go.mod h1:h8272+G2omSmi30fBXiZDMkmHuOgonplfKIKjQWzlfs=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
diff --git a/integration_tests/protocols/code/py-env-var.yaml b/integration_tests/protocols/code/py-env-var.yaml
index c4b9e6c5..4ccf3648 100644
--- a/integration_tests/protocols/code/py-env-var.yaml
+++ b/integration_tests/protocols/code/py-env-var.yaml
@@ -20,4 +20,4 @@ code:
- type: word
words:
- "hello from input baz"
-# digest: 4a0a00473045022100b290a0c40f27573f0de9a950be13457a9bf59ade1ff2f497bf01a3b526e5db750220761942acffd6d27e2714ddaa1c73c699ccd7de48839f08cff1d6a9456bc8ff1f:4a3eb6b4988d95847d4203be25ed1d46
\ No newline at end of file
+# digest: 4a0a0047304502207e3a5eda5f3207c3c01c820562243281926c1215224a7c80ed7528559b9f52cb022100f6ef99bb45843f481705778630f2cfd8f4d1cc3acb96392ff016f22e06aa91af:4a3eb6b4988d95847d4203be25ed1d46
\ No newline at end of file
diff --git a/integration_tests/protocols/code/py-file.yaml b/integration_tests/protocols/code/py-file.yaml
index 74203e8d..9e0b041b 100644
--- a/integration_tests/protocols/code/py-file.yaml
+++ b/integration_tests/protocols/code/py-file.yaml
@@ -18,4 +18,4 @@ code:
- type: word
words:
- "hello from input"
-# digest: 490a004630440220335663a6a4db720ee6276ab7179a87a6be0b4030771ec5ee82ecf6982342113602200a2570db7eb9721f6ceb1a89543fc436ee62b30d1b720c75ea3834ed3d2b64f3:4a3eb6b4988d95847d4203be25ed1d46
\ No newline at end of file
+# digest: 4a0a004730450220069673af9bd6d6677f9529d06f5d8bd46d543089a4731ed18ee806761d75fd60022100913a3e27b0a5809baf710ba9585bf9fe729634c0e19e3e13eef70a6bd100df34:4a3eb6b4988d95847d4203be25ed1d46
\ No newline at end of file
diff --git a/integration_tests/protocols/code/py-interactsh.yaml b/integration_tests/protocols/code/py-interactsh.yaml
index eac53c3b..24e4b062 100644
--- a/integration_tests/protocols/code/py-interactsh.yaml
+++ b/integration_tests/protocols/code/py-interactsh.yaml
@@ -26,4 +26,4 @@ code:
part: interactsh_protocol
words:
- "http"
-# digest: 490a004630440220400892730a62fa1bbb1064e4d88eea760dbf8f01c6b630ff0f5b126fd1952839022025a6d52e730c1f1cfcbd440e6269f93489db3a77cb2a27d0f47522c0819dc8d3:4a3eb6b4988d95847d4203be25ed1d46
\ No newline at end of file
+# digest: 490a00463044022003b8d069e3c84412729c43e33013a52ee04eabcf096d511979691d71d8e905f60220011f4475899abed4f86b4bd5e6c2423750759135206e4729826afe1ed8a44f4d:4a3eb6b4988d95847d4203be25ed1d46
\ No newline at end of file
diff --git a/integration_tests/protocols/code/py-snippet.yaml b/integration_tests/protocols/code/py-snippet.yaml
index f8a30b4f..287ca2c6 100644
--- a/integration_tests/protocols/code/py-snippet.yaml
+++ b/integration_tests/protocols/code/py-snippet.yaml
@@ -21,4 +21,4 @@ code:
- type: word
words:
- "hello from input"
-# digest: 490a0046304402206b14abdc0d5fc13466f5c292da9fb2a19d1b2c5e683cc052037fe367b372f82b02202c00b9acbd8106a769eb411794c567d3019433671397bf909e16b286105ed69e:4a3eb6b4988d95847d4203be25ed1d46
\ No newline at end of file
+# digest: 4a0a00473045022100c291615cf2a8005450c17a6554e81a9cdab14743b299f0679c644247929198b502206fdacc8ab173bde2b4015340012637916bf2659f66f320fcc06b97ac639072a1:4a3eb6b4988d95847d4203be25ed1d46
\ No newline at end of file
diff --git a/integration_tests/protocols/http/http-matcher-extractor-dy-extractor.yaml b/integration_tests/protocols/http/http-matcher-extractor-dy-extractor.yaml
new file mode 100644
index 00000000..eb26d50b
--- /dev/null
+++ b/integration_tests/protocols/http/http-matcher-extractor-dy-extractor.yaml
@@ -0,0 +1,36 @@
+id: http-matcher-extractor-dy-extractor
+info:
+ name: HTTP matcher and extractor & dynamic extractor
+ description: >
+ Edgecase to test for a combination of matchers , extractors and dynamic extractors
+ author: pdteam
+ severity: info
+
+http:
+ - raw:
+ - |
+ GET {{BaseURL}} HTTP/1.1
+ - |
+ GET {{absolutePath}} HTTP/1.1
+
+ req-condition: true
+ extractors:
+ - type: regex
+ internal: true
+ part: body_1
+ name: absolutePath
+ regex:
+ - ''
+ group: 1
+ - type: regex
+ internal: false
+ part: body_2
+ name: title
+ regex:
+ - ']*>([^<]+)'
+ group: 1
+ matchers:
+ - type: regex
+ part: body_2
+ regex:
+ - ']*>([^<]+)'
\ No newline at end of file
diff --git a/integration_tests/protocols/http/http-paths.yaml b/integration_tests/protocols/http/http-paths.yaml
index ae389a37..ba5be2d5 100644
--- a/integration_tests/protocols/http/http-paths.yaml
+++ b/integration_tests/protocols/http/http-paths.yaml
@@ -28,7 +28,7 @@ info:
- "//CFIDE/wizards/common/utils.cfc"
# Test all templates with FullURLs
-requests:
+http:
- raw:
# relative path without leading slash with param
# If relative path does not have `/` prefix it is autocorrected
diff --git a/integration_tests/protocols/http/multi-http-var-sharing.yaml b/integration_tests/protocols/http/multi-http-var-sharing.yaml
new file mode 100644
index 00000000..606a0365
--- /dev/null
+++ b/integration_tests/protocols/http/multi-http-var-sharing.yaml
@@ -0,0 +1,36 @@
+id: multi-http-var-sharing
+
+info:
+ name: Multi HTTP var sharing
+ author: pdteam
+ severity: info
+ description: |
+ A template which has multiple HTTP requests block and variables are shared between them
+
+http:
+ - method: GET
+ path:
+ - "{{BaseURL}}"
+
+ matchers:
+ - type: word
+ words:
+ - "This is test matcher text"
+ negative: true
+ internal: true
+
+ extractors:
+ - type: dsl
+ name: ffff
+ dsl:
+ - status_code
+ internal: true
+
+ - method: GET
+ path:
+ - "{{BaseURL}}/{{ffff}}"
+
+ matchers:
+ - type: status
+ status:
+ - 200
\ No newline at end of file
diff --git a/lib/config.go b/lib/config.go
index 0c48f373..65f3fc8c 100644
--- a/lib/config.go
+++ b/lib/config.go
@@ -107,10 +107,12 @@ func WithInteractshOptions(opts InteractshOpts) NucleiSDKOptions {
// Concurrency options
type Concurrency struct {
- TemplateConcurrency int // number of templates to run concurrently (per host in host-spray mode)
- HostConcurrency int // number of hosts to scan concurrently (per template in template-spray mode)
- HeadlessHostConcurrency int // number of hosts to scan concurrently for headless templates (per template in template-spray mode)
- HeadlessTemplateConcurrency int // number of templates to run concurrently for headless templates (per host in host-spray mode)
+ TemplateConcurrency int // number of templates to run concurrently (per host in host-spray mode)
+ HostConcurrency int // number of hosts to scan concurrently (per template in template-spray mode)
+ HeadlessHostConcurrency int // number of hosts to scan concurrently for headless templates (per template in template-spray mode)
+ HeadlessTemplateConcurrency int // number of templates to run concurrently for headless templates (per host in host-spray mode)
+ JavascriptTemplateConcurrency int // number of templates to run concurrently for javascript templates (per host in host-spray mode)
+ TemplatePayloadConcurrency int // max concurrent payloads to run for a template (a good default is 25)
}
// WithConcurrency sets concurrency options
@@ -120,6 +122,8 @@ func WithConcurrency(opts Concurrency) NucleiSDKOptions {
e.opts.BulkSize = opts.HostConcurrency
e.opts.HeadlessBulkSize = opts.HeadlessHostConcurrency
e.opts.HeadlessTemplateThreads = opts.HeadlessTemplateConcurrency
+ e.opts.JsConcurrency = opts.JavascriptTemplateConcurrency
+ e.opts.PayloadConcurrency = opts.TemplatePayloadConcurrency
return nil
}
}
diff --git a/pkg/operators/operators.go b/pkg/operators/operators.go
index a3b4fc56..1d6103fe 100644
--- a/pkg/operators/operators.go
+++ b/pkg/operators/operators.go
@@ -299,7 +299,7 @@ func (operators *Operators) Execute(data map[string]interface{}, match MatchFunc
if len(result.DynamicValues) > 0 {
return result, true
}
- return nil, false
+ return result, false
}
}
@@ -313,6 +313,10 @@ func (operators *Operators) Execute(data map[string]interface{}, match MatchFunc
// Don't print if we have matchers, and they have not matched, regardless of extractor
if len(operators.Matchers) > 0 && !matches {
+ // if dynamic values are present then it is not a failure
+ if len(result.DynamicValues) > 0 {
+ return result, true
+ }
return nil, false
}
// Write a final string of output if matcher type is
@@ -320,6 +324,10 @@ func (operators *Operators) Execute(data map[string]interface{}, match MatchFunc
if len(result.Extracts) > 0 || len(result.OutputExtracts) > 0 || matches {
return result, true
}
+ // if dynamic values are present then it is not a failure
+ if len(result.DynamicValues) > 0 {
+ return result, true
+ }
return nil, false
}
diff --git a/pkg/protocols/code/code.go b/pkg/protocols/code/code.go
index 9423d200..57eb9b88 100644
--- a/pkg/protocols/code/code.go
+++ b/pkg/protocols/code/code.go
@@ -192,6 +192,10 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
dataOutputString := fmtStdout(gOutput.Stdout.String())
data := make(output.InternalEvent)
+ // also include all request variables in result event
+ for _, value := range metaSrc.Variables {
+ data[value.Name] = value.Value
+ }
data["type"] = request.Type().String()
data["response"] = dataOutputString // response contains filtered output (eg without trailing \n)
diff --git a/pkg/protocols/http/build_request.go b/pkg/protocols/http/build_request.go
index b526d9d2..d8ebb397 100644
--- a/pkg/protocols/http/build_request.go
+++ b/pkg/protocols/http/build_request.go
@@ -97,7 +97,7 @@ func (r *requestGenerator) Make(ctx context.Context, input *contextargs.Context,
}
// Parse target url
- parsed, err := urlutil.Parse(input.MetaInput.Input)
+ parsed, err := urlutil.ParseAbsoluteURL(input.MetaInput.Input, false)
if err != nil {
return nil, err
}
@@ -153,7 +153,7 @@ func (r *requestGenerator) Make(ctx context.Context, input *contextargs.Context,
return r.generateRawRequest(ctx, reqData, parsed, finalVars, payloads)
}
- reqURL, err := urlutil.ParseURL(reqData, true)
+ reqURL, err := urlutil.ParseAbsoluteURL(reqData, true)
if err != nil {
return nil, errorutil.NewWithTag("http", "failed to parse url %v while creating http request", reqData)
}
@@ -291,8 +291,7 @@ func (r *requestGenerator) generateRawRequest(ctx context.Context, rawRequest st
unsafeReq := &generatedRequest{rawRequest: rawRequestData, meta: generatorValues, original: r.request, interactshURLs: r.interactshURLs}
return unsafeReq, nil
}
-
- urlx, err := urlutil.ParseURL(rawRequestData.FullURL, true)
+ urlx, err := urlutil.ParseAbsoluteURL(rawRequestData.FullURL, true)
if err != nil {
return nil, errorutil.NewWithErr(err).Msgf("failed to create request with url %v got %v", rawRequestData.FullURL, err).WithTag("raw")
}
diff --git a/pkg/protocols/http/request.go b/pkg/protocols/http/request.go
index 9bacd6d9..09b6c43f 100644
--- a/pkg/protocols/http/request.go
+++ b/pkg/protocols/http/request.go
@@ -38,6 +38,7 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/types"
"github.com/projectdiscovery/rawhttp"
convUtil "github.com/projectdiscovery/utils/conversion"
+ errorutil "github.com/projectdiscovery/utils/errors"
httpUtils "github.com/projectdiscovery/utils/http"
"github.com/projectdiscovery/utils/reader"
sliceutil "github.com/projectdiscovery/utils/slice"
@@ -453,7 +454,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
}
// verify if parallel elaboration was requested
- if request.Threads > 0 {
+ if request.Threads > 0 && len(request.Payloads) > 0 {
return request.executeParallelHTTP(input, dynamicValues, callback)
}
@@ -494,7 +495,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
return true, nil
}
var gotMatches bool
- err = request.executeRequest(input, generatedHttpRequest, previous, hasInteractMatchers, func(event *output.InternalWrappedEvent) {
+ execReqErr := request.executeRequest(input, generatedHttpRequest, previous, hasInteractMatchers, func(event *output.InternalWrappedEvent) {
// a special case where operators has interactsh matchers and multiple request are made
// ex: status_code_2 , interactsh_protocol (from 1st request) etc
needsRequestEvent := interactsh.HasMatchers(request.CompiledOperators) && request.NeedsRequestCondition()
@@ -525,14 +526,14 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
}, generator.currentIndex)
// If a variable is unresolved, skip all further requests
- if errors.Is(err, errStopExecution) {
+ if errors.Is(execReqErr, errStopExecution) {
return true, nil
}
- if err != nil {
+ if execReqErr != nil {
if request.options.HostErrorsCache != nil {
request.options.HostErrorsCache.MarkFailed(input.MetaInput.ID(), err)
}
- requestErr = err
+ requestErr = errorutil.NewWithErr(execReqErr).Msgf("got err while executing %v", generatedHttpRequest.URL())
}
request.options.Progress.IncrementRequests()
diff --git a/pkg/protocols/http/utils.go b/pkg/protocols/http/utils.go
index fe893ebe..875faaf2 100644
--- a/pkg/protocols/http/utils.go
+++ b/pkg/protocols/http/utils.go
@@ -6,13 +6,22 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
"github.com/projectdiscovery/rawhttp"
+ errorutil "github.com/projectdiscovery/utils/errors"
)
// dump creates a dump of the http request in form of a byte slice
func dump(req *generatedRequest, reqURL string) ([]byte, error) {
if req.request != nil {
- return req.request.Dump()
+ bin, err := req.request.Dump()
+ if err != nil {
+ return nil, errorutil.NewWithErr(err).WithTag("http").Msgf("could not dump request: %v", req.request.URL.String())
+ }
+ return bin, nil
}
rawHttpOptions := &rawhttp.Options{CustomHeaders: req.rawRequest.UnsafeHeaders, CustomRawBytes: req.rawRequest.UnsafeRawBytes}
- return rawhttp.DumpRequestRaw(req.rawRequest.Method, reqURL, req.rawRequest.Path, generators.ExpandMapValues(req.rawRequest.Headers), io.NopCloser(strings.NewReader(req.rawRequest.Data)), rawHttpOptions)
+ bin, err := rawhttp.DumpRequestRaw(req.rawRequest.Method, reqURL, req.rawRequest.Path, generators.ExpandMapValues(req.rawRequest.Headers), io.NopCloser(strings.NewReader(req.rawRequest.Data)), rawHttpOptions)
+ if err != nil {
+ return nil, errorutil.NewWithErr(err).WithTag("http").Msgf("could not dump request: %v", reqURL)
+ }
+ return bin, nil
}
diff --git a/pkg/protocols/protocols.go b/pkg/protocols/protocols.go
index 44849798..75918fee 100644
--- a/pkg/protocols/protocols.go
+++ b/pkg/protocols/protocols.go
@@ -131,7 +131,7 @@ func (e *ExecutorOptions) GetThreadsForNPayloadRequests(totalRequests int, curre
if currentThreads > 0 {
return currentThreads
} else {
- return e.Options.TemplateThreads
+ return e.Options.PayloadConcurrency
}
}
diff --git a/pkg/tmplexec/exec.go b/pkg/tmplexec/exec.go
index 5035a1ff..3e0e8fdd 100644
--- a/pkg/tmplexec/exec.go
+++ b/pkg/tmplexec/exec.go
@@ -3,6 +3,7 @@ package tmplexec
import (
"errors"
"fmt"
+ "runtime/debug"
"strings"
"sync/atomic"
@@ -34,16 +35,6 @@ var _ protocols.Executer = &TemplateExecuter{}
// NewTemplateExecuter creates a new request TemplateExecuter for list of requests
func NewTemplateExecuter(requests []protocols.Request, options *protocols.ExecutorOptions) (*TemplateExecuter, error) {
- isMultiProto := false
- lastProto := ""
- for _, request := range requests {
- if request.Type().String() != lastProto && lastProto != "" {
- isMultiProto = true
- break
- }
- lastProto = request.Type().String()
- }
-
e := &TemplateExecuter{requests: requests, options: options, results: &atomic.Bool{}}
if options.Flow != "" {
// we use a dummy input here because goal of flow executor at this point is to just check
@@ -55,13 +46,11 @@ func NewTemplateExecuter(requests []protocols.Request, options *protocols.Execut
}
e.program = p
} else {
- // Review:
- // multiproto engine is only used if there is more than one protocol in template
- // else we use generic engine (should we use multiproto engine for single protocol with multiple requests as well ?)
- if isMultiProto {
- e.engine = multiproto.NewMultiProtocol(requests, options, e.results)
- } else {
+ // only use generic if there is only 1 protocol with only 1 section
+ if len(requests) == 1 {
e.engine = generic.NewGenericEngine(requests, options, e.results)
+ } else {
+ e.engine = multiproto.NewMultiProtocol(requests, options, e.results)
}
}
return e, nil
@@ -113,8 +102,9 @@ func (e *TemplateExecuter) Execute(ctx *scan.ScanContext) (bool, error) {
defer func() {
// try catching unknown panics
if r := recover(); r != nil {
- ctx.LogError(fmt.Errorf("panic: %v", r))
- gologger.Verbose().Msgf("panic: %v", r)
+ stacktrace := debug.Stack()
+ ctx.LogError(fmt.Errorf("panic: %v\n%s", r, stacktrace))
+ gologger.Verbose().Msgf("panic: %v\n%s", r, stacktrace)
}
}()
diff --git a/pkg/tmplexec/multiproto/multi.go b/pkg/tmplexec/multiproto/multi.go
index 021c8353..d164c03a 100644
--- a/pkg/tmplexec/multiproto/multi.go
+++ b/pkg/tmplexec/multiproto/multi.go
@@ -46,12 +46,13 @@ func (m *MultiProtocol) Compile() error {
func (m *MultiProtocol) ExecuteWithResults(ctx *scan.ScanContext) error {
// put all readonly args into template context
m.options.GetTemplateCtx(ctx.Input.MetaInput).Merge(m.readOnlyArgs)
- var finalProtoEvent *output.InternalWrappedEvent
// callback to process results from all protocols
multiProtoCallback := func(event *output.InternalWrappedEvent) {
- if event != nil {
- finalProtoEvent = event
+ if event == nil {
+ return
}
+ // log event and generate result for the event
+ ctx.LogEvent(event)
// export dynamic values from operators (i.e internal:true)
if event.OperatorsResult != nil && len(event.OperatorsResult.DynamicValues) > 0 {
for k, v := range event.OperatorsResult.DynamicValues {
@@ -97,12 +98,6 @@ func (m *MultiProtocol) ExecuteWithResults(ctx *scan.ScanContext) error {
return err
}
}
- // Review: how to handle events of multiple protocols in a single template
- // currently the outer callback is only executed once (for the last protocol in queue)
- // due to workflow logic at https://github.com/projectdiscovery/nuclei/blob/main/pkg/protocols/common/executer/executem.go#L150
- // this causes addition of duplicated / unncessary variables with prefix template_id_all_variables
- ctx.LogEvent(finalProtoEvent)
-
return nil
}
diff --git a/pkg/types/types.go b/pkg/types/types.go
index e8419475..fb42ea6f 100644
--- a/pkg/types/types.go
+++ b/pkg/types/types.go
@@ -370,6 +370,8 @@ type Options struct {
ScanID string
// JsConcurrency is the number of concurrent js routines to run
JsConcurrency int
+ // PayloadConcurrency is the number of concurrent payloads to run per template
+ PayloadConcurrency int
}
// ShouldLoadResume resume file