Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/cmd/integration-test/http.go
2848 views
1
package main
2
3
import (
4
"errors"
5
"fmt"
6
"net/http"
7
"net/http/httptest"
8
"net/http/httputil"
9
"os"
10
"reflect"
11
"regexp"
12
"strconv"
13
"strings"
14
"sync"
15
"time"
16
17
"github.com/julienschmidt/httprouter"
18
"gopkg.in/yaml.v2"
19
20
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
21
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
22
"github.com/projectdiscovery/retryablehttp-go"
23
"github.com/projectdiscovery/utils/errkit"
24
logutil "github.com/projectdiscovery/utils/log"
25
sliceutil "github.com/projectdiscovery/utils/slice"
26
stringsutil "github.com/projectdiscovery/utils/strings"
27
unitutils "github.com/projectdiscovery/utils/unit"
28
)
29
30
var httpTestcases = []TestCaseInfo{
31
// TODO: excluded due to parsing errors with console
32
// "http/raw-unsafe-request.yaml": &httpRawUnsafeRequest{},
33
{Path: "protocols/http/get-headers.yaml", TestCase: &httpGetHeaders{}},
34
{Path: "protocols/http/get-query-string.yaml", TestCase: &httpGetQueryString{}},
35
{Path: "protocols/http/get-redirects.yaml", TestCase: &httpGetRedirects{}},
36
{Path: "protocols/http/get-host-redirects.yaml", TestCase: &httpGetHostRedirects{}},
37
{Path: "protocols/http/disable-redirects.yaml", TestCase: &httpDisableRedirects{}},
38
{Path: "protocols/http/get.yaml", TestCase: &httpGet{}},
39
{Path: "protocols/http/post-body.yaml", TestCase: &httpPostBody{}},
40
{Path: "protocols/http/post-json-body.yaml", TestCase: &httpPostJSONBody{}},
41
{Path: "protocols/http/post-multipart-body.yaml", TestCase: &httpPostMultipartBody{}},
42
{Path: "protocols/http/raw-cookie-reuse.yaml", TestCase: &httpRawCookieReuse{}},
43
{Path: "protocols/http/raw-dynamic-extractor.yaml", TestCase: &httpRawDynamicExtractor{}},
44
{Path: "protocols/http/raw-get-query.yaml", TestCase: &httpRawGetQuery{}},
45
{Path: "protocols/http/raw-get.yaml", TestCase: &httpRawGet{}},
46
{Path: "protocols/http/raw-with-params.yaml", TestCase: &httpRawWithParams{}},
47
{Path: "protocols/http/raw-unsafe-with-params.yaml", TestCase: &httpRawWithParams{}}, // Not a typo, functionality is same as above
48
{Path: "protocols/http/raw-path-trailing-slash.yaml", TestCase: &httpRawPathTrailingSlash{}},
49
{Path: "protocols/http/raw-payload.yaml", TestCase: &httpRawPayload{}},
50
{Path: "protocols/http/raw-post-body.yaml", TestCase: &httpRawPostBody{}},
51
{Path: "protocols/http/raw-unsafe-path.yaml", TestCase: &httpRawUnsafePath{}},
52
{Path: "protocols/http/http-paths.yaml", TestCase: &httpPaths{}},
53
{Path: "protocols/http/request-condition.yaml", TestCase: &httpRequestCondition{}},
54
{Path: "protocols/http/request-condition-new.yaml", TestCase: &httpRequestCondition{}},
55
{Path: "protocols/http/self-contained.yaml", TestCase: &httpRequestSelfContained{}},
56
{Path: "protocols/http/self-contained-with-path.yaml", TestCase: &httpRequestSelfContained{}}, // Not a typo, functionality is same as above
57
{Path: "protocols/http/self-contained-with-params.yaml", TestCase: &httpRequestSelfContainedWithParams{}},
58
{Path: "protocols/http/self-contained-file-input.yaml", TestCase: &httpRequestSelfContainedFileInput{}},
59
{Path: "protocols/http/get-case-insensitive.yaml", TestCase: &httpGetCaseInsensitive{}},
60
{Path: "protocols/http/get.yaml,protocols/http/get-case-insensitive.yaml", TestCase: &httpGetCaseInsensitiveCluster{}},
61
{Path: "protocols/http/get-redirects-chain-headers.yaml", TestCase: &httpGetRedirectsChainHeaders{}},
62
{Path: "protocols/http/dsl-matcher-variable.yaml", TestCase: &httpDSLVariable{}},
63
{Path: "protocols/http/dsl-functions.yaml", TestCase: &httpDSLFunctions{}},
64
{Path: "protocols/http/race-simple.yaml", TestCase: &httpRaceSimple{}},
65
{Path: "protocols/http/race-multiple.yaml", TestCase: &httpRaceMultiple{}},
66
{Path: "protocols/http/race-condition-with-delay.yaml", TestCase: &httpRaceWithDelay{}},
67
{Path: "protocols/http/race-with-variables.yaml", TestCase: &httpRaceWithVariables{}},
68
{Path: "protocols/http/stop-at-first-match.yaml", TestCase: &httpStopAtFirstMatch{}},
69
{Path: "protocols/http/stop-at-first-match-with-extractors.yaml", TestCase: &httpStopAtFirstMatchWithExtractors{}},
70
{Path: "protocols/http/variables.yaml", TestCase: &httpVariables{}},
71
{Path: "protocols/http/variables-threads-previous.yaml", TestCase: &httpVariablesThreadsPrevious{}},
72
{Path: "protocols/http/variable-dsl-function.yaml", TestCase: &httpVariableDSLFunction{}},
73
{Path: "protocols/http/get-override-sni.yaml", TestCase: &httpSniAnnotation{}},
74
{Path: "protocols/http/get-sni.yaml", TestCase: &customCLISNI{}},
75
{Path: "protocols/http/redirect-match-url.yaml", TestCase: &httpRedirectMatchURL{}},
76
{Path: "protocols/http/get-sni-unsafe.yaml", TestCase: &customCLISNIUnsafe{}},
77
{Path: "protocols/http/annotation-timeout.yaml", TestCase: &annotationTimeout{}},
78
{Path: "protocols/http/custom-attack-type.yaml", TestCase: &customAttackType{}},
79
{Path: "protocols/http/get-all-ips.yaml", TestCase: &scanAllIPS{}},
80
{Path: "protocols/http/get-without-scheme.yaml", TestCase: &httpGetWithoutScheme{}},
81
{Path: "protocols/http/cl-body-without-header.yaml", TestCase: &httpCLBodyWithoutHeader{}},
82
{Path: "protocols/http/cl-body-with-header.yaml", TestCase: &httpCLBodyWithHeader{}},
83
{Path: "protocols/http/cli-with-constants.yaml", TestCase: &ConstantWithCliVar{}},
84
{Path: "protocols/http/constants-with-threads.yaml", TestCase: &constantsWithThreads{}},
85
{Path: "protocols/http/matcher-status.yaml", TestCase: &matcherStatusTest{}},
86
{Path: "protocols/http/disable-path-automerge.yaml", TestCase: &httpDisablePathAutomerge{}},
87
{Path: "protocols/http/http-preprocessor.yaml", TestCase: &httpPreprocessor{}},
88
{Path: "protocols/http/multi-request.yaml", TestCase: &httpMultiRequest{}},
89
{Path: "protocols/http/http-matcher-extractor-dy-extractor.yaml", TestCase: &httpMatcherExtractorDynamicExtractor{}},
90
{Path: "protocols/http/multi-http-var-sharing.yaml", TestCase: &httpMultiVarSharing{}},
91
{Path: "protocols/http/raw-path-single-slash.yaml", TestCase: &httpRawPathSingleSlash{}},
92
{Path: "protocols/http/raw-unsafe-path-single-slash.yaml", TestCase: &httpRawUnsafePathSingleSlash{}},
93
}
94
95
type httpMultiVarSharing struct{}
96
97
func (h *httpMultiVarSharing) Execute(filePath string) error {
98
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "https://scanme.sh", debug)
99
if err != nil {
100
return err
101
}
102
return expectResultsCount(results, 1)
103
}
104
105
type httpMatcherExtractorDynamicExtractor struct{}
106
107
func (h *httpMatcherExtractorDynamicExtractor) Execute(filePath string) error {
108
router := httprouter.New()
109
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
110
html := `<!DOCTYPE html>
111
<html lang="en">
112
<body>
113
<a href="/domains">Domains</a>
114
</body>
115
</html>`
116
_, _ = fmt.Fprint(w, html)
117
})
118
router.GET("/domains", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
119
html := `<!DOCTYPE html>
120
<html lang="en">
121
<head>
122
<title>Dynamic Extractor Test</title>
123
</head>
124
<body>
125
<!-- The content of the title tag matches the regex pattern for both the extractor and matcher for 'title' -->
126
</body>
127
</html>
128
`
129
_, _ = fmt.Fprint(w, html)
130
})
131
ts := httptest.NewServer(router)
132
defer ts.Close()
133
134
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
135
if err != nil {
136
return err
137
}
138
139
return expectResultsCount(results, 1)
140
}
141
142
type httpInteractshRequest struct{}
143
144
// Execute executes a test case and returns an error if occurred
145
func (h *httpInteractshRequest) Execute(filePath string) error {
146
router := httprouter.New()
147
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
148
value := r.Header.Get("url")
149
if value != "" {
150
if resp, _ := retryablehttp.DefaultClient().Get(value); resp != nil {
151
_ = resp.Body.Close()
152
}
153
}
154
})
155
ts := httptest.NewServer(router)
156
defer ts.Close()
157
158
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
159
if err != nil {
160
return err
161
}
162
163
return expectResultsCount(results, 1, 2)
164
}
165
166
type httpInteractshWithPayloadsRequest struct{}
167
168
// Execute executes a test case and returns an error if occurred
169
func (h *httpInteractshWithPayloadsRequest) Execute(filePath string) error {
170
router := httprouter.New()
171
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
172
value := r.Header.Get("url")
173
if value != "" {
174
if resp, _ := retryablehttp.DefaultClient().Get(value); resp != nil {
175
_ = resp.Body.Close()
176
}
177
}
178
})
179
ts := httptest.NewServer(router)
180
defer ts.Close()
181
182
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
183
if err != nil {
184
return err
185
}
186
187
return expectResultsCount(results, 1, 3)
188
}
189
190
type httpDefaultMatcherCondition struct{}
191
192
// Execute executes a test case and returns an error if occurred
193
func (d *httpDefaultMatcherCondition) Execute(filePath string) error {
194
// to simulate matcher-condition `or`
195
// - template should be run twice and vulnerable server should send response that fits for that specific run
196
router := httprouter.New()
197
var routerErr error
198
// Server endpoint where only interactsh matcher is successful and status code is not 200
199
router.GET("/interactsh/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
200
value := r.URL.Query().Get("url")
201
if value != "" {
202
if _, err := retryablehttp.DefaultClient().Get("https://" + value); err != nil {
203
routerErr = err
204
}
205
}
206
w.WriteHeader(http.StatusNotFound)
207
})
208
// Server endpoint where url is not probed but sends a 200 status code
209
router.GET("/status/", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
210
w.WriteHeader(http.StatusOK)
211
})
212
ts := httptest.NewServer(router)
213
defer ts.Close()
214
215
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/status", debug)
216
if err != nil {
217
return err
218
}
219
if err := expectResultsCount(results, 1); err != nil {
220
return err
221
}
222
223
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/interactsh", debug)
224
if err != nil {
225
return err
226
}
227
if routerErr != nil {
228
return errkit.Wrap(routerErr, "failed to send http request to interactsh server")
229
}
230
if err := expectResultsCount(results, 1); err != nil {
231
return err
232
}
233
return nil
234
}
235
236
type httpInteractshStopAtFirstMatchRequest struct{}
237
238
// Execute executes a test case and returns an error if occurred
239
func (h *httpInteractshStopAtFirstMatchRequest) Execute(filePath string) error {
240
router := httprouter.New()
241
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
242
value := r.Header.Get("url")
243
if value != "" {
244
if resp, _ := retryablehttp.DefaultClient().Get(value); resp != nil {
245
_ = resp.Body.Close()
246
}
247
}
248
})
249
ts := httptest.NewServer(router)
250
defer ts.Close()
251
252
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
253
if err != nil {
254
return err
255
}
256
// polling is asynchronous, so the interactions may be retrieved after the first request
257
return expectResultsCount(results, 1)
258
}
259
260
type httpGetHeaders struct{}
261
262
// Execute executes a test case and returns an error if occurred
263
func (h *httpGetHeaders) Execute(filePath string) error {
264
router := httprouter.New()
265
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
266
if strings.EqualFold(r.Header.Get("test"), "nuclei") {
267
_, _ = fmt.Fprintf(w, "This is test headers matcher text")
268
}
269
})
270
ts := httptest.NewServer(router)
271
defer ts.Close()
272
273
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
274
if err != nil {
275
return err
276
}
277
278
return expectResultsCount(results, 1)
279
}
280
281
type httpGetQueryString struct{}
282
283
// Execute executes a test case and returns an error if occurred
284
func (h *httpGetQueryString) Execute(filePath string) error {
285
router := httprouter.New()
286
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
287
if strings.EqualFold(r.URL.Query().Get("test"), "nuclei") {
288
_, _ = fmt.Fprintf(w, "This is test querystring matcher text")
289
}
290
})
291
ts := httptest.NewServer(router)
292
defer ts.Close()
293
294
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
295
if err != nil {
296
return err
297
}
298
299
return expectResultsCount(results, 1)
300
}
301
302
type httpGetRedirects struct{}
303
304
// Execute executes a test case and returns an error if occurred
305
func (h *httpGetRedirects) Execute(filePath string) error {
306
router := httprouter.New()
307
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
308
http.Redirect(w, r, "/redirected", http.StatusFound)
309
})
310
router.GET("/redirected", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
311
_, _ = fmt.Fprintf(w, "This is test redirects matcher text")
312
})
313
ts := httptest.NewServer(router)
314
defer ts.Close()
315
316
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
317
if err != nil {
318
return err
319
}
320
321
return expectResultsCount(results, 1)
322
}
323
324
type httpGetHostRedirects struct{}
325
326
// Execute executes a test case and returns an error if occurred
327
func (h *httpGetHostRedirects) Execute(filePath string) error {
328
router := httprouter.New()
329
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
330
http.Redirect(w, r, "/redirected1", http.StatusFound)
331
})
332
router.GET("/redirected1", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
333
http.Redirect(w, r, "redirected2", http.StatusFound)
334
})
335
router.GET("/redirected2", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
336
http.Redirect(w, r, "/redirected3", http.StatusFound)
337
})
338
router.GET("/redirected3", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
339
http.Redirect(w, r, "https://scanme.sh", http.StatusTemporaryRedirect)
340
})
341
ts := httptest.NewServer(router)
342
defer ts.Close()
343
344
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
345
if err != nil {
346
return err
347
}
348
349
return expectResultsCount(results, 1)
350
}
351
352
type httpDisableRedirects struct{}
353
354
// Execute executes a test case and returns an error if occurred
355
func (h *httpDisableRedirects) Execute(filePath string) error {
356
router := httprouter.New()
357
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
358
http.Redirect(w, r, "/redirected", http.StatusMovedPermanently)
359
})
360
router.GET("/redirected", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
361
_, _ = fmt.Fprintf(w, "This is test redirects matcher text")
362
})
363
ts := httptest.NewServer(router)
364
defer ts.Close()
365
366
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-dr")
367
if err != nil {
368
return err
369
}
370
371
return expectResultsCount(results, 0)
372
}
373
374
type httpGet struct{}
375
376
// Execute executes a test case and returns an error if occurred
377
func (h *httpGet) Execute(filePath string) error {
378
router := httprouter.New()
379
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
380
_, _ = fmt.Fprintf(w, "This is test matcher text")
381
})
382
ts := httptest.NewServer(router)
383
defer ts.Close()
384
385
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
386
if err != nil {
387
return err
388
}
389
390
return expectResultsCount(results, 1)
391
}
392
393
type httpDSLVariable struct{}
394
395
// Execute executes a test case and returns an error if occurred
396
func (h *httpDSLVariable) Execute(filePath string) error {
397
router := httprouter.New()
398
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
399
_, _ = fmt.Fprintf(w, "This is test matcher text")
400
})
401
ts := httptest.NewServer(router)
402
defer ts.Close()
403
404
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
405
if err != nil {
406
return err
407
}
408
409
return expectResultsCount(results, 5)
410
}
411
412
type httpDSLFunctions struct{}
413
414
func (h *httpDSLFunctions) Execute(filePath string) error {
415
router := httprouter.New()
416
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
417
request, err := httputil.DumpRequest(r, true)
418
if err != nil {
419
_, _ = fmt.Fprint(w, err.Error())
420
} else {
421
_, _ = fmt.Fprint(w, string(request))
422
}
423
})
424
ts := httptest.NewServer(router)
425
defer ts.Close()
426
427
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-nc")
428
if err != nil {
429
return err
430
}
431
432
if err := expectResultsCount(results, 1); err != nil {
433
return err
434
}
435
436
// get result part
437
resultPart, err := stringsutil.After(results[0], ts.URL)
438
if err != nil {
439
return err
440
}
441
442
// remove additional characters till the first valid result and ignore last ] which doesn't alter the total count
443
resultPart = stringsutil.TrimPrefixAny(resultPart, "/", " ", "[")
444
445
extracted := strings.Split(resultPart, ",")
446
numberOfDslFunctions := 88
447
if len(extracted) != numberOfDslFunctions {
448
return errors.New("incorrect number of results")
449
}
450
451
for _, header := range extracted {
452
header = strings.Trim(header, `"`)
453
parts := strings.Split(header, ": ")
454
index, err := strconv.Atoi(parts[0])
455
if err != nil {
456
return err
457
}
458
if index < 0 || index > numberOfDslFunctions {
459
return fmt.Errorf("incorrect header index found: %d", index)
460
}
461
if strings.TrimSpace(parts[1]) == "" {
462
return fmt.Errorf("the DSL expression with index %d was not evaluated correctly", index)
463
}
464
}
465
466
return nil
467
}
468
469
type httpPostBody struct{}
470
471
// Execute executes a test case and returns an error if occurred
472
func (h *httpPostBody) Execute(filePath string) error {
473
router := httprouter.New()
474
var routerErr error
475
476
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
477
if err := r.ParseForm(); err != nil {
478
routerErr = err
479
return
480
}
481
if strings.EqualFold(r.Form.Get("username"), "test") && strings.EqualFold(r.Form.Get("password"), "nuclei") {
482
_, _ = fmt.Fprintf(w, "This is test post-body matcher text")
483
}
484
})
485
ts := httptest.NewServer(router)
486
defer ts.Close()
487
488
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
489
if err != nil {
490
return err
491
}
492
if routerErr != nil {
493
return routerErr
494
}
495
496
return expectResultsCount(results, 1)
497
}
498
499
type httpPostJSONBody struct{}
500
501
// Execute executes a test case and returns an error if occurred
502
func (h *httpPostJSONBody) Execute(filePath string) error {
503
router := httprouter.New()
504
var routerErr error
505
506
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
507
type doc struct {
508
Username string `json:"username"`
509
Password string `json:"password"`
510
}
511
obj := &doc{}
512
if err := json.NewDecoder(r.Body).Decode(obj); err != nil {
513
routerErr = err
514
return
515
}
516
if strings.EqualFold(obj.Username, "test") && strings.EqualFold(obj.Password, "nuclei") {
517
_, _ = fmt.Fprintf(w, "This is test post-json-body matcher text")
518
}
519
})
520
ts := httptest.NewServer(router)
521
defer ts.Close()
522
523
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
524
if err != nil {
525
return err
526
}
527
if routerErr != nil {
528
return routerErr
529
}
530
531
return expectResultsCount(results, 1)
532
}
533
534
type httpPostMultipartBody struct{}
535
536
// Execute executes a test case and returns an error if occurred
537
func (h *httpPostMultipartBody) Execute(filePath string) error {
538
router := httprouter.New()
539
var routerErr error
540
541
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
542
if err := r.ParseMultipartForm(unitutils.Mega); err != nil {
543
routerErr = err
544
return
545
}
546
password, ok := r.MultipartForm.Value["password"]
547
if !ok || len(password) != 1 {
548
routerErr = errors.New("no password in request")
549
return
550
}
551
file := r.MultipartForm.File["username"]
552
if len(file) != 1 {
553
routerErr = errors.New("no file in request")
554
return
555
}
556
if strings.EqualFold(password[0], "nuclei") && strings.EqualFold(file[0].Filename, "username") {
557
_, _ = fmt.Fprintf(w, "This is test post-multipart matcher text")
558
}
559
})
560
ts := httptest.NewServer(router)
561
defer ts.Close()
562
563
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
564
if err != nil {
565
return err
566
}
567
if routerErr != nil {
568
return routerErr
569
}
570
571
return expectResultsCount(results, 1)
572
}
573
574
type httpRawDynamicExtractor struct{}
575
576
// Execute executes a test case and returns an error if occurred
577
func (h *httpRawDynamicExtractor) Execute(filePath string) error {
578
router := httprouter.New()
579
var routerErr error
580
581
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
582
if err := r.ParseForm(); err != nil {
583
routerErr = err
584
return
585
}
586
if strings.EqualFold(r.Form.Get("testing"), "parameter") {
587
_, _ = fmt.Fprintf(w, "Token: 'nuclei'")
588
}
589
})
590
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
591
if strings.EqualFold(r.URL.Query().Get("username"), "nuclei") {
592
_, _ = fmt.Fprintf(w, "Test is test-dynamic-extractor-raw matcher text")
593
}
594
})
595
ts := httptest.NewServer(router)
596
defer ts.Close()
597
598
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
599
if err != nil {
600
return err
601
}
602
if routerErr != nil {
603
return routerErr
604
}
605
606
return expectResultsCount(results, 1)
607
}
608
609
type httpRawGetQuery struct{}
610
611
// Execute executes a test case and returns an error if occurred
612
func (h *httpRawGetQuery) Execute(filePath string) error {
613
router := httprouter.New()
614
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
615
if strings.EqualFold(r.URL.Query().Get("test"), "nuclei") {
616
_, _ = fmt.Fprintf(w, "Test is test raw-get-query-matcher text")
617
}
618
})
619
ts := httptest.NewServer(router)
620
defer ts.Close()
621
622
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
623
if err != nil {
624
return err
625
}
626
627
return expectResultsCount(results, 1)
628
}
629
630
type httpRawGet struct{}
631
632
// Execute executes a test case and returns an error if occurred
633
func (h *httpRawGet) Execute(filePath string) error {
634
router := httprouter.New()
635
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
636
_, _ = fmt.Fprintf(w, "Test is test raw-get-matcher text")
637
})
638
ts := httptest.NewServer(router)
639
defer ts.Close()
640
641
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
642
if err != nil {
643
return err
644
}
645
646
return expectResultsCount(results, 1)
647
}
648
649
type httpRawWithParams struct{}
650
651
// Execute executes a test case and returns an error if occurred
652
func (h *httpRawWithParams) Execute(filePath string) error {
653
router := httprouter.New()
654
var errx error
655
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
656
params := r.URL.Query()
657
// we intentionally use params["test"] instead of params.Get("test") to test the case where
658
// there are multiple parameters with the same name
659
if !reflect.DeepEqual(params["key1"], []string{"value1"}) {
660
errx = errkit.Append(errx, errkit.New("key1 not found in params", "expected", []string{"value1"}, "got", params["key1"]))
661
}
662
if !reflect.DeepEqual(params["key2"], []string{"value2"}) {
663
errx = errkit.Append(errx, errkit.New("key2 not found in params", "expected", []string{"value2"}, "got", params["key2"]))
664
}
665
_, _ = fmt.Fprintf(w, "Test is test raw-params-matcher text")
666
})
667
ts := httptest.NewServer(router)
668
defer ts.Close()
669
670
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/?key1=value1", debug)
671
if err != nil {
672
return err
673
}
674
if errx != nil {
675
return err
676
}
677
return expectResultsCount(results, 1)
678
}
679
680
type httpRawPathTrailingSlash struct{}
681
682
func (h *httpRawPathTrailingSlash) Execute(filepath string) error {
683
router := httprouter.New()
684
var routerErr error
685
686
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
687
if r.RequestURI != "/test/..;/..;/" {
688
routerErr = fmt.Errorf("expected path /test/..;/..;/ but got %v", r.RequestURI)
689
return
690
}
691
})
692
ts := httptest.NewServer(router)
693
defer ts.Close()
694
695
_, err := testutils.RunNucleiTemplateAndGetResults(filepath, ts.URL, debug)
696
if err != nil {
697
return err
698
}
699
if routerErr != nil {
700
return routerErr
701
}
702
return nil
703
}
704
705
type httpRawPayload struct{}
706
707
// Execute executes a test case and returns an error if occurred
708
func (h *httpRawPayload) Execute(filePath string) error {
709
router := httprouter.New()
710
var routerErr error
711
712
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
713
if err := r.ParseForm(); err != nil {
714
routerErr = err
715
return
716
}
717
if !strings.EqualFold(r.Header.Get("another_header"), "bnVjbGVp") && !strings.EqualFold(r.Header.Get("another_header"), "Z3Vlc3Q=") {
718
return
719
}
720
if strings.EqualFold(r.Form.Get("username"), "test") && (strings.EqualFold(r.Form.Get("password"), "nuclei") || strings.EqualFold(r.Form.Get("password"), "guest")) {
721
_, _ = fmt.Fprintf(w, "Test is raw-payload matcher text")
722
}
723
})
724
ts := httptest.NewServer(router)
725
defer ts.Close()
726
727
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
728
if err != nil {
729
return err
730
}
731
if routerErr != nil {
732
return routerErr
733
}
734
735
return expectResultsCount(results, 2)
736
}
737
738
type httpRawPostBody struct{}
739
740
// Execute executes a test case and returns an error if occurred
741
func (h *httpRawPostBody) Execute(filePath string) error {
742
router := httprouter.New()
743
var routerErr error
744
745
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
746
if err := r.ParseForm(); err != nil {
747
routerErr = err
748
return
749
}
750
if strings.EqualFold(r.Form.Get("username"), "test") && strings.EqualFold(r.Form.Get("password"), "nuclei") {
751
_, _ = fmt.Fprintf(w, "Test is test raw-post-body-matcher text")
752
}
753
})
754
ts := httptest.NewServer(router)
755
defer ts.Close()
756
757
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
758
if err != nil {
759
return err
760
}
761
if routerErr != nil {
762
return routerErr
763
}
764
765
return expectResultsCount(results, 1)
766
}
767
768
type httpRawUnsafePath struct{}
769
770
func (h *httpRawUnsafePath) Execute(filepath string) error {
771
// testing unsafe paths using router feedback is not possible cause they are `unsafe urls`
772
// hence it is done by parsing and matching paths from nuclei output with `-debug-req` flag
773
// read template files
774
bin, err := os.ReadFile(filepath)
775
if err != nil {
776
return err
777
}
778
779
// Instead of storing expected `paths` in code it is stored in
780
// `reference` section of template
781
type template struct {
782
Info struct {
783
Reference []string `yaml:"reference"`
784
}
785
}
786
var tpl template
787
if err = yaml.Unmarshal(bin, &tpl); err != nil {
788
return err
789
}
790
// expected relative paths
791
expected := []string{}
792
expected = append(expected, tpl.Info.Reference...)
793
if len(expected) == 0 {
794
return fmt.Errorf("something went wrong with %v template", filepath)
795
}
796
797
results, err := testutils.RunNucleiBinaryAndGetCombinedOutput(debug, []string{"-t", filepath, "-u", "scanme.sh", "-debug-req"})
798
if err != nil {
799
return err
800
}
801
802
actual := []string{}
803
for _, v := range strings.Split(results, "\n") {
804
if strings.Contains(v, "GET") {
805
parts := strings.Fields(v)
806
if len(parts) == 3 {
807
actual = append(actual, parts[1])
808
}
809
}
810
}
811
812
if !reflect.DeepEqual(expected, actual) {
813
return fmt.Errorf("%8v: %v\n%-8v: %v", "expected", expected, "actual", actual)
814
}
815
return nil
816
}
817
818
type httpPaths struct{}
819
820
func (h *httpPaths) Execute(filepath string) error {
821
// covers testcases similar to httpRawUnsafePath but when `unsafe:false`
822
bin, err := os.ReadFile(filepath)
823
if err != nil {
824
return err
825
}
826
827
// Instead of storing expected `paths` in code it is stored in
828
// `reference` section of template
829
type template struct {
830
Info struct {
831
Reference []string `yaml:"reference"`
832
}
833
}
834
var tpl template
835
if err = yaml.Unmarshal(bin, &tpl); err != nil {
836
return err
837
}
838
// expected relative paths
839
expected := []string{}
840
expected = append(expected, tpl.Info.Reference...)
841
if len(expected) == 0 {
842
return fmt.Errorf("something went wrong with %v template", filepath)
843
}
844
845
results, err := testutils.RunNucleiBinaryAndGetCombinedOutput(debug, []string{"-t", filepath, "-u", "scanme.sh", "-debug-req"})
846
if err != nil {
847
return err
848
}
849
850
actual := []string{}
851
for _, v := range strings.Split(results, "\n") {
852
if strings.Contains(v, "GET") {
853
parts := strings.Fields(v)
854
if len(parts) == 3 {
855
actual = append(actual, parts[1])
856
}
857
}
858
}
859
860
if len(expected) > len(actual) {
861
actualValuesIndex := max(len(actual)-1, 0)
862
return fmt.Errorf("missing values : %v", expected[actualValuesIndex:])
863
} else if len(expected) < len(actual) {
864
return fmt.Errorf("unexpected values : %v", actual[len(expected)-1:])
865
} else {
866
if !reflect.DeepEqual(expected, actual) {
867
return fmt.Errorf("expected: %v\n\nactual: %v", expected, actual)
868
}
869
}
870
return nil
871
}
872
873
type httpRawCookieReuse struct{}
874
875
// Execute executes a test case and returns an error if occurred
876
func (h *httpRawCookieReuse) Execute(filePath string) error {
877
router := httprouter.New()
878
var routerErr error
879
880
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
881
if err := r.ParseForm(); err != nil {
882
routerErr = err
883
return
884
}
885
if strings.EqualFold(r.Form.Get("testing"), "parameter") {
886
http.SetCookie(w, &http.Cookie{Name: "nuclei", Value: "test"})
887
}
888
})
889
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
890
if err := r.ParseForm(); err != nil {
891
routerErr = err
892
return
893
}
894
cookie, err := r.Cookie("nuclei")
895
if err != nil {
896
routerErr = err
897
return
898
}
899
900
if strings.EqualFold(cookie.Value, "test") {
901
_, _ = fmt.Fprintf(w, "Test is test-cookie-reuse matcher text")
902
}
903
})
904
ts := httptest.NewServer(router)
905
defer ts.Close()
906
907
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
908
if err != nil {
909
return err
910
}
911
if routerErr != nil {
912
return routerErr
913
}
914
915
return expectResultsCount(results, 1)
916
}
917
918
// TODO: excluded due to parsing errors with console
919
// type httpRawUnsafeRequest struct{
920
// Execute executes a test case and returns an error if occurred
921
// func (h *httpRawUnsafeRequest) Execute(filePath string) error {
922
// var routerErr error
923
//
924
// ts := testutils.NewTCPServer(nil, defaultStaticPort, func(conn net.Conn) {
925
// defer conn.Close()
926
// _, _ = conn.Write([]byte("protocols/http/1.1 200 OK\r\nContent-Length: 36\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nThis is test raw-unsafe-matcher test"))
927
// })
928
// defer ts.Close()
929
//
930
// results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "http://"+ts.URL, debug)
931
// if err != nil {
932
// return err
933
// }
934
// if routerErr != nil {
935
// return routerErr
936
// }
937
//
938
// return expectResultsCount(results, 1)
939
// }
940
941
type httpRequestCondition struct{}
942
943
// Execute executes a test case and returns an error if occurred
944
func (h *httpRequestCondition) Execute(filePath string) error {
945
router := httprouter.New()
946
947
router.GET("/200", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
948
w.WriteHeader(http.StatusOK)
949
})
950
router.GET("/400", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
951
w.WriteHeader(http.StatusBadRequest)
952
})
953
ts := httptest.NewServer(router)
954
defer ts.Close()
955
956
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
957
if err != nil {
958
return err
959
}
960
961
return expectResultsCount(results, 1)
962
}
963
964
type httpRequestSelfContained struct{}
965
966
// Execute executes a test case and returns an error if occurred
967
func (h *httpRequestSelfContained) Execute(filePath string) error {
968
router := httprouter.New()
969
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
970
_, _ = w.Write([]byte("This is self-contained response"))
971
})
972
server := &http.Server{
973
Addr: fmt.Sprintf("localhost:%d", defaultStaticPort),
974
Handler: router,
975
}
976
go func() {
977
_ = server.ListenAndServe()
978
}()
979
defer func() {
980
_ = server.Close()
981
}()
982
983
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-esc")
984
if err != nil {
985
return err
986
}
987
988
return expectResultsCount(results, 1)
989
}
990
991
// testcase to check duplicated values in params
992
type httpRequestSelfContainedWithParams struct{}
993
994
// Execute executes a test case and returns an error if occurred
995
func (h *httpRequestSelfContainedWithParams) Execute(filePath string) error {
996
router := httprouter.New()
997
var errx error
998
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
999
params := r.URL.Query()
1000
// we intentionally use params["test"] instead of params.Get("test") to test the case where
1001
// there are multiple parameters with the same name
1002
if !reflect.DeepEqual(params["something"], []string{"here"}) {
1003
errx = errkit.Append(errx, errkit.New("something not found in params", "expected", []string{"here"}, "got", params["something"]))
1004
}
1005
if !reflect.DeepEqual(params["key"], []string{"value"}) {
1006
errx = errkit.Append(errx, errkit.New("key not found in params", "expected", []string{"value"}, "got", params["key"]))
1007
}
1008
_, _ = w.Write([]byte("This is self-contained response"))
1009
})
1010
server := &http.Server{
1011
Addr: fmt.Sprintf("localhost:%d", defaultStaticPort),
1012
Handler: router,
1013
}
1014
go func() {
1015
_ = server.ListenAndServe()
1016
}()
1017
defer func() {
1018
_ = server.Close()
1019
}()
1020
1021
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-esc")
1022
if err != nil {
1023
return err
1024
}
1025
if errx != nil {
1026
return errx
1027
}
1028
1029
return expectResultsCount(results, 1)
1030
}
1031
1032
type httpRequestSelfContainedFileInput struct{}
1033
1034
func (h *httpRequestSelfContainedFileInput) Execute(filePath string) error {
1035
router := httprouter.New()
1036
gotReqToEndpoints := []string{}
1037
router.GET("/one", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1038
gotReqToEndpoints = append(gotReqToEndpoints, "/one")
1039
_, _ = w.Write([]byte("This is self-contained response"))
1040
})
1041
router.GET("/two", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1042
gotReqToEndpoints = append(gotReqToEndpoints, "/two")
1043
_, _ = w.Write([]byte("This is self-contained response"))
1044
})
1045
server := &http.Server{
1046
Addr: fmt.Sprintf("localhost:%d", defaultStaticPort),
1047
Handler: router,
1048
}
1049
go func() {
1050
_ = server.ListenAndServe()
1051
}()
1052
defer func() {
1053
_ = server.Close()
1054
}()
1055
1056
// create temp file
1057
FileLoc, err := os.CreateTemp("", "self-contained-payload-*.txt")
1058
if err != nil {
1059
return errkit.Wrap(err, "failed to create temp file")
1060
}
1061
if _, err := FileLoc.Write([]byte("one\ntwo\n")); err != nil {
1062
return errkit.Wrap(err, "failed to write payload to temp file")
1063
}
1064
defer func() {
1065
_ = FileLoc.Close()
1066
}()
1067
1068
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-V", "test="+FileLoc.Name(), "-esc")
1069
if err != nil {
1070
return err
1071
}
1072
1073
if err := expectResultsCount(results, 4); err != nil {
1074
return err
1075
}
1076
1077
if !sliceutil.ElementsMatch(gotReqToEndpoints, []string{"/one", "/two", "/one", "/two"}) {
1078
return errkit.New("expected requests to be sent to `/one` and `/two` endpoints but were sent to `%v`", gotReqToEndpoints, "filePath", filePath)
1079
}
1080
return nil
1081
}
1082
1083
type httpGetCaseInsensitive struct{}
1084
1085
// Execute executes a test case and returns an error if occurred
1086
func (h *httpGetCaseInsensitive) Execute(filePath string) error {
1087
router := httprouter.New()
1088
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1089
_, _ = fmt.Fprintf(w, "THIS IS TEST MATCHER TEXT")
1090
})
1091
ts := httptest.NewServer(router)
1092
defer ts.Close()
1093
1094
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1095
if err != nil {
1096
return err
1097
}
1098
1099
return expectResultsCount(results, 1)
1100
}
1101
1102
type httpGetCaseInsensitiveCluster struct{}
1103
1104
// Execute executes a test case and returns an error if occurred
1105
func (h *httpGetCaseInsensitiveCluster) Execute(filesPath string) error {
1106
router := httprouter.New()
1107
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1108
_, _ = fmt.Fprintf(w, "This is test matcher text")
1109
})
1110
ts := httptest.NewServer(router)
1111
defer ts.Close()
1112
1113
files := strings.Split(filesPath, ",")
1114
1115
results, err := testutils.RunNucleiTemplateAndGetResults(files[0], ts.URL, debug, "-t", files[1])
1116
if err != nil {
1117
return err
1118
}
1119
1120
return expectResultsCount(results, 2)
1121
}
1122
1123
type httpGetRedirectsChainHeaders struct{}
1124
1125
// Execute executes a test case and returns an error if occurred
1126
func (h *httpGetRedirectsChainHeaders) Execute(filePath string) error {
1127
router := httprouter.New()
1128
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1129
http.Redirect(w, r, "/redirected", http.StatusFound)
1130
})
1131
router.GET("/redirected", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1132
w.Header().Set("Secret", "TestRedirectHeaderMatch")
1133
http.Redirect(w, r, "/final", http.StatusFound)
1134
})
1135
router.GET("/final", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1136
_, _ = w.Write([]byte("ok"))
1137
})
1138
ts := httptest.NewServer(router)
1139
defer ts.Close()
1140
1141
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1142
if err != nil {
1143
return err
1144
}
1145
1146
return expectResultsCount(results, 1)
1147
}
1148
1149
type httpRaceSimple struct{}
1150
1151
// Execute executes a test case and returns an error if occurred
1152
func (h *httpRaceSimple) Execute(filePath string) error {
1153
router := httprouter.New()
1154
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1155
w.WriteHeader(http.StatusOK)
1156
})
1157
ts := httptest.NewServer(router)
1158
defer ts.Close()
1159
1160
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1161
if err != nil {
1162
return err
1163
}
1164
return expectResultsCount(results, 10)
1165
}
1166
1167
type httpRaceMultiple struct{}
1168
1169
// Execute executes a test case and returns an error if occurred
1170
func (h *httpRaceMultiple) Execute(filePath string) error {
1171
router := httprouter.New()
1172
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1173
w.WriteHeader(http.StatusOK)
1174
})
1175
ts := httptest.NewServer(router)
1176
defer ts.Close()
1177
1178
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1179
if err != nil {
1180
return err
1181
}
1182
return expectResultsCount(results, 5)
1183
}
1184
1185
type httpRaceWithDelay struct{}
1186
1187
// Execute executes a test case and returns an error if occurred
1188
func (h *httpRaceWithDelay) Execute(filePath string) error {
1189
var requestTimes []time.Time
1190
var mu sync.Mutex
1191
1192
router := httprouter.New()
1193
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1194
mu.Lock()
1195
requestTimes = append(requestTimes, time.Now())
1196
mu.Unlock()
1197
time.Sleep(2 * time.Second)
1198
w.WriteHeader(http.StatusOK)
1199
})
1200
ts := httptest.NewServer(router)
1201
defer ts.Close()
1202
1203
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1204
if err != nil {
1205
return err
1206
}
1207
if err := expectResultsCount(results, 3); err != nil {
1208
return err
1209
}
1210
1211
mu.Lock()
1212
defer mu.Unlock()
1213
if len(requestTimes) != 3 {
1214
return fmt.Errorf("expected 3 requests, got %d", len(requestTimes))
1215
}
1216
1217
// Check concurrency of first two requests (should be very close)
1218
if diff := requestTimes[1].Sub(requestTimes[0]); diff > 500*time.Millisecond {
1219
return fmt.Errorf("expected first 2 requests to be concurrent, diff: %v", diff)
1220
}
1221
1222
// Check delay of third request (should be after ~2s)
1223
if diff := requestTimes[2].Sub(requestTimes[0]); diff < 1500*time.Millisecond {
1224
return fmt.Errorf("expected 3rd request to be delayed, diff: %v", diff)
1225
}
1226
1227
return nil
1228
}
1229
1230
type httpRaceWithVariables struct{}
1231
1232
// Execute tests that variables and constants are properly resolved in race mode.
1233
func (h *httpRaceWithVariables) Execute(filePath string) error {
1234
router := httprouter.New()
1235
router.GET("/race", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1236
// Echo back the API key header so we can match on it
1237
_, _ = fmt.Fprint(w, r.Header.Get("X-API-Key"))
1238
})
1239
ts := httptest.NewServer(router)
1240
defer ts.Close()
1241
1242
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1243
if err != nil {
1244
return err
1245
}
1246
1247
return expectResultsCount(results, 3)
1248
}
1249
1250
type httpStopAtFirstMatch struct{}
1251
1252
// Execute executes a test case and returns an error if occurred
1253
func (h *httpStopAtFirstMatch) Execute(filePath string) error {
1254
router := httprouter.New()
1255
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1256
_, _ = fmt.Fprintf(w, "This is test")
1257
})
1258
ts := httptest.NewServer(router)
1259
defer ts.Close()
1260
1261
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1262
if err != nil {
1263
return err
1264
}
1265
1266
return expectResultsCount(results, 1)
1267
}
1268
1269
type httpStopAtFirstMatchWithExtractors struct{}
1270
1271
// Execute executes a test case and returns an error if occurred
1272
func (h *httpStopAtFirstMatchWithExtractors) Execute(filePath string) error {
1273
router := httprouter.New()
1274
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1275
_, _ = fmt.Fprintf(w, "This is test")
1276
})
1277
ts := httptest.NewServer(router)
1278
defer ts.Close()
1279
1280
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1281
if err != nil {
1282
return err
1283
}
1284
1285
return expectResultsCount(results, 2)
1286
}
1287
1288
type httpVariables struct{}
1289
1290
// Execute executes a test case and returns an error if occurred
1291
func (h *httpVariables) Execute(filePath string) error {
1292
router := httprouter.New()
1293
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1294
_, _ = fmt.Fprintf(w, "%s\n%s\n%s", r.Header.Get("Test"), r.Header.Get("Another"), r.Header.Get("Email"))
1295
})
1296
ts := httptest.NewServer(router)
1297
defer ts.Close()
1298
1299
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1300
if err != nil {
1301
return err
1302
}
1303
if err := expectResultsCount(results, 1); err != nil {
1304
return err
1305
}
1306
1307
// variable override that does not have any match
1308
// to make sure the variable override is working
1309
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-var", "a1=failed")
1310
if err != nil {
1311
return err
1312
}
1313
1314
return expectResultsCount(results, 0)
1315
}
1316
1317
type httpVariablesThreadsPrevious struct{}
1318
1319
// Execute tests that variables can reference data extracted from previous requests
1320
// when using threads mode (parallel execution).
1321
func (h *httpVariablesThreadsPrevious) Execute(filePath string) error {
1322
router := httprouter.New()
1323
router.GET("/login", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1324
_, _ = fmt.Fprint(w, "token=secret123")
1325
})
1326
router.GET("/api", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1327
// Echo back the Authorization header so we can match on it
1328
_, _ = fmt.Fprint(w, r.Header.Get("Authorization"))
1329
})
1330
ts := httptest.NewServer(router)
1331
defer ts.Close()
1332
1333
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1334
if err != nil {
1335
return err
1336
}
1337
1338
return expectResultsCount(results, 1)
1339
}
1340
1341
type httpVariableDSLFunction struct{}
1342
1343
// Execute executes a test case and returns an error if occurred
1344
func (h *httpVariableDSLFunction) Execute(filePath string) error {
1345
results, err := testutils.RunNucleiBinaryAndGetCombinedOutput(debug, []string{"-t", filePath, "-u", "https://scanme.sh", "-debug-req"})
1346
if err != nil {
1347
return err
1348
}
1349
1350
actual := []string{}
1351
for _, v := range strings.Split(results, "\n") {
1352
if strings.Contains(v, "GET") {
1353
parts := strings.Fields(v)
1354
if len(parts) == 3 {
1355
actual = append(actual, parts[1])
1356
}
1357
}
1358
}
1359
if len(actual) == 2 && actual[0] == actual[1] {
1360
return nil
1361
}
1362
1363
return fmt.Errorf("expected 2 requests with same URL, got %v", actual)
1364
}
1365
1366
type customCLISNI struct{}
1367
1368
// Execute executes a test case and returns an error if occurred
1369
func (h *customCLISNI) Execute(filePath string) error {
1370
router := httprouter.New()
1371
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1372
if r.TLS.ServerName == "test" {
1373
_, _ = w.Write([]byte("test-ok"))
1374
} else {
1375
_, _ = w.Write([]byte("test-ko"))
1376
}
1377
})
1378
ts := httptest.NewTLSServer(router)
1379
defer ts.Close()
1380
1381
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-sni", "test")
1382
if err != nil {
1383
return err
1384
}
1385
return expectResultsCount(results, 1)
1386
}
1387
1388
type httpSniAnnotation struct{}
1389
1390
// Execute executes a test case and returns an error if occurred
1391
func (h *httpSniAnnotation) Execute(filePath string) error {
1392
router := httprouter.New()
1393
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1394
if r.TLS.ServerName == "test" {
1395
_, _ = w.Write([]byte("test-ok"))
1396
} else {
1397
_, _ = w.Write([]byte("test-ko"))
1398
}
1399
})
1400
ts := httptest.NewTLSServer(router)
1401
defer ts.Close()
1402
1403
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1404
if err != nil {
1405
return err
1406
}
1407
return expectResultsCount(results, 1)
1408
}
1409
1410
type httpRedirectMatchURL struct{}
1411
1412
// Execute executes a test case and returns an error if occurred
1413
func (h *httpRedirectMatchURL) Execute(filePath string) error {
1414
router := httprouter.New()
1415
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1416
http.Redirect(w, r, "/redirected", http.StatusFound)
1417
_, _ = w.Write([]byte("This is test redirects matcher text"))
1418
})
1419
router.GET("/redirected", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1420
_, _ = fmt.Fprintf(w, "This is test redirects matcher text")
1421
})
1422
ts := httptest.NewServer(router)
1423
defer ts.Close()
1424
1425
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-no-meta")
1426
if err != nil {
1427
return err
1428
}
1429
1430
if err := expectResultsCount(results, 1); err != nil {
1431
return err
1432
}
1433
if results[0] != fmt.Sprintf("%s/redirected", ts.URL) {
1434
return fmt.Errorf("mismatched url found: %s", results[0])
1435
}
1436
return nil
1437
}
1438
1439
type customCLISNIUnsafe struct{}
1440
1441
// Execute executes a test case and returns an error if occurred
1442
func (h *customCLISNIUnsafe) Execute(filePath string) error {
1443
router := httprouter.New()
1444
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1445
if r.TLS.ServerName == "test" {
1446
_, _ = w.Write([]byte("test-ok"))
1447
} else {
1448
_, _ = w.Write([]byte("test-ko"))
1449
}
1450
})
1451
ts := httptest.NewTLSServer(router)
1452
defer ts.Close()
1453
1454
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-sni", "test")
1455
if err != nil {
1456
return err
1457
}
1458
return expectResultsCount(results, 1)
1459
}
1460
1461
type annotationTimeout struct{}
1462
1463
// Execute executes a test case and returns an error if occurred
1464
func (h *annotationTimeout) Execute(filePath string) error {
1465
router := httprouter.New()
1466
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1467
time.Sleep(4 * time.Second)
1468
_, _ = fmt.Fprintf(w, "This is test matcher text")
1469
})
1470
ts := httptest.NewTLSServer(router)
1471
defer ts.Close()
1472
1473
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-timeout", "1")
1474
if err != nil {
1475
return err
1476
}
1477
return expectResultsCount(results, 1)
1478
}
1479
1480
type customAttackType struct{}
1481
1482
// Execute executes a test case and returns an error if occurred
1483
func (h *customAttackType) Execute(filePath string) error {
1484
router := httprouter.New()
1485
got := []string{}
1486
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1487
got = append(got, r.URL.RawQuery)
1488
_, _ = fmt.Fprintf(w, "This is test custom payload")
1489
})
1490
ts := httptest.NewTLSServer(router)
1491
defer ts.Close()
1492
1493
_, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-attack-type", "clusterbomb")
1494
if err != nil {
1495
return err
1496
}
1497
return expectResultsCount(got, 4)
1498
}
1499
1500
// Disabled as GH doesn't support ipv6
1501
type scanAllIPS struct{}
1502
1503
// Execute executes a test case and returns an error if occurred
1504
func (h *scanAllIPS) Execute(filePath string) error {
1505
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, "https://scanme.sh", debug, "-scan-all-ips", "-iv", "4")
1506
if err != nil {
1507
return err
1508
}
1509
// limiting test to ipv4 (GH doesn't support ipv6)
1510
return expectResultsCount(got, 1)
1511
}
1512
1513
// ensure that ip|host are handled without http|https scheme
1514
type httpGetWithoutScheme struct{}
1515
1516
// Execute executes a test case and returns an error if occurred
1517
func (h *httpGetWithoutScheme) Execute(filePath string) error {
1518
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, "scanme.sh", debug)
1519
if err != nil {
1520
return err
1521
}
1522
return expectResultsCount(got, 1)
1523
}
1524
1525
// content-length in case the response has no header but has a body
1526
type httpCLBodyWithoutHeader struct{}
1527
1528
// Execute executes a test case and returns an error if occurred
1529
func (h *httpCLBodyWithoutHeader) Execute(filePath string) error {
1530
logutil.DisableDefaultLogger()
1531
defer logutil.EnableDefaultLogger()
1532
1533
router := httprouter.New()
1534
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1535
w.Header()["Content-Length"] = []string{"-1"}
1536
_, _ = fmt.Fprintf(w, "this is a test")
1537
})
1538
ts := httptest.NewTLSServer(router)
1539
defer ts.Close()
1540
1541
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1542
if err != nil {
1543
return err
1544
}
1545
return expectResultsCount(got, 1)
1546
}
1547
1548
// content-length in case the response has content-length header and a body
1549
type httpCLBodyWithHeader struct{}
1550
1551
// Execute executes a test case and returns an error if occurred
1552
func (h *httpCLBodyWithHeader) Execute(filePath string) error {
1553
router := httprouter.New()
1554
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1555
w.Header()["Content-Length"] = []string{"50000"}
1556
_, _ = fmt.Fprintf(w, "this is a test")
1557
})
1558
ts := httptest.NewTLSServer(router)
1559
defer ts.Close()
1560
1561
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1562
if err != nil {
1563
return err
1564
}
1565
return expectResultsCount(got, 1)
1566
}
1567
1568
// constant shouldn't be overwritten by cli var with same name
1569
type ConstantWithCliVar struct{}
1570
1571
// Execute executes a test case and returns an error if occurred
1572
func (h *ConstantWithCliVar) Execute(filePath string) error {
1573
router := httprouter.New()
1574
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1575
_, _ = fmt.Fprint(w, r.URL.Query().Get("p"))
1576
})
1577
ts := httptest.NewTLSServer(router)
1578
defer ts.Close()
1579
1580
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-V", "test=fromcli")
1581
if err != nil {
1582
return err
1583
}
1584
return expectResultsCount(got, 1)
1585
}
1586
1587
type constantsWithThreads struct{}
1588
1589
// Execute tests that constants are properly resolved when using threads mode.
1590
func (h *constantsWithThreads) Execute(filePath string) error {
1591
router := httprouter.New()
1592
router.GET("/api/:version", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
1593
// Echo back the API key header and version so we can match on them
1594
_, _ = fmt.Fprintf(w, "%s %s", r.Header.Get("X-API-Key"), p.ByName("version"))
1595
})
1596
ts := httptest.NewServer(router)
1597
defer ts.Close()
1598
1599
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1600
if err != nil {
1601
return err
1602
}
1603
1604
return expectResultsCount(results, 1)
1605
}
1606
1607
type matcherStatusTest struct{}
1608
1609
// Execute executes a test case and returns an error if occurred
1610
func (h *matcherStatusTest) Execute(filePath string) error {
1611
router := httprouter.New()
1612
router.GET("/200", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1613
w.WriteHeader(http.StatusOK)
1614
})
1615
ts := httptest.NewServer(router)
1616
defer ts.Close()
1617
1618
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-ms")
1619
if err != nil {
1620
return err
1621
}
1622
return expectResultsCount(results, 1)
1623
}
1624
1625
// disable path automerge in raw request
1626
type httpDisablePathAutomerge struct{}
1627
1628
// Execute executes a test case and returns an error if occurred
1629
func (h *httpDisablePathAutomerge) Execute(filePath string) error {
1630
router := httprouter.New()
1631
router.GET("/api/v1/test", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1632
_, _ = fmt.Fprint(w, r.URL.Query().Get("id"))
1633
})
1634
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1635
_, _ = fmt.Fprint(w, "empty path in raw request")
1636
})
1637
1638
ts := httptest.NewServer(router)
1639
defer ts.Close()
1640
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/api/v1/user", debug)
1641
if err != nil {
1642
return err
1643
}
1644
return expectResultsCount(got, 2)
1645
}
1646
1647
type httpInteractshRequestsWithMCAnd struct{}
1648
1649
func (h *httpInteractshRequestsWithMCAnd) Execute(filePath string) error {
1650
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, "honey.scanme.sh", debug)
1651
if err != nil {
1652
return err
1653
}
1654
return expectResultsCount(got, 1)
1655
}
1656
1657
// integration test to check if preprocessor i.e {{randstr}}
1658
// is working correctly
1659
type httpPreprocessor struct{}
1660
1661
// Execute executes a test case and returns an error if occurred
1662
func (h *httpPreprocessor) Execute(filePath string) error {
1663
router := httprouter.New()
1664
re := regexp.MustCompile(`[A-Za-z0-9]{25,}`)
1665
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1666
value := r.URL.RequestURI()
1667
if re.MatchString(value) {
1668
w.WriteHeader(http.StatusOK)
1669
_, _ = fmt.Fprint(w, "ok")
1670
} else {
1671
w.WriteHeader(http.StatusBadRequest)
1672
_, _ = fmt.Fprint(w, "not ok")
1673
}
1674
})
1675
ts := httptest.NewServer(router)
1676
defer ts.Close()
1677
1678
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1679
if err != nil {
1680
return err
1681
}
1682
1683
return expectResultsCount(results, 1)
1684
}
1685
1686
type httpMultiRequest struct{}
1687
1688
// Execute executes a test case and returns an error if occurred
1689
func (h *httpMultiRequest) Execute(filePath string) error {
1690
router := httprouter.New()
1691
router.GET("/ping", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1692
w.WriteHeader(http.StatusOK)
1693
_, _ = fmt.Fprint(w, "ping")
1694
})
1695
router.GET("/pong", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
1696
w.WriteHeader(http.StatusOK)
1697
_, _ = fmt.Fprint(w, "pong")
1698
})
1699
ts := httptest.NewServer(router)
1700
defer ts.Close()
1701
1702
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
1703
if err != nil {
1704
return err
1705
}
1706
1707
return expectResultsCount(results, 1)
1708
}
1709
1710
type httpRawPathSingleSlash struct{}
1711
1712
func (h *httpRawPathSingleSlash) Execute(filepath string) error {
1713
expectedPath := "/index.php"
1714
results, err := testutils.RunNucleiBinaryAndGetCombinedOutput(debug, []string{"-t", filepath, "-u", "scanme.sh/index.php", "-debug-req"})
1715
if err != nil {
1716
return err
1717
}
1718
1719
var actual string
1720
for _, v := range strings.Split(results, "\n") {
1721
if strings.Contains(v, "GET") {
1722
parts := strings.Fields(v)
1723
if len(parts) == 3 {
1724
actual = parts[1]
1725
}
1726
}
1727
}
1728
1729
if actual != expectedPath {
1730
return fmt.Errorf("expected: %v\n\nactual: %v", expectedPath, actual)
1731
}
1732
return nil
1733
}
1734
1735
type httpRawUnsafePathSingleSlash struct{}
1736
1737
func (h *httpRawUnsafePathSingleSlash) Execute(filepath string) error {
1738
expectedPath := "/index.php"
1739
results, err := testutils.RunNucleiBinaryAndGetCombinedOutput(debug, []string{"-t", filepath, "-u", "scanme.sh/index.php", "-debug-req"})
1740
if err != nil {
1741
return err
1742
}
1743
1744
var actual string
1745
for _, v := range strings.Split(results, "\n") {
1746
if strings.Contains(v, "GET") {
1747
parts := strings.Fields(v)
1748
if len(parts) == 3 {
1749
actual = parts[1]
1750
}
1751
}
1752
}
1753
1754
if actual != expectedPath {
1755
return fmt.Errorf("expected: %v\n\nactual: %v", expectedPath, actual)
1756
}
1757
return nil
1758
}
1759
1760