mirror of https://github.com/daffainfo/nuclei.git
Start of work on testing infrastructure
parent
162afc6c7b
commit
b33bc83b0b
|
@ -0,0 +1,17 @@
|
|||
id: basic-get-headers
|
||||
|
||||
info:
|
||||
name: Basic GET Headers Request
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- method: GET
|
||||
path:
|
||||
- "{{BaseURL}}"
|
||||
headers:
|
||||
test: nuclei
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "This is test headers matcher text"
|
|
@ -0,0 +1,15 @@
|
|||
id: basic-get-querystring
|
||||
|
||||
info:
|
||||
name: Basic GET QueryString Request
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- method: GET
|
||||
path:
|
||||
- "{{BaseURL}}?test=nuclei"
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "This is test querystring matcher text"
|
|
@ -0,0 +1,17 @@
|
|||
id: basic-get-redirects
|
||||
|
||||
info:
|
||||
name: Basic GET Redirects Request
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- method: GET
|
||||
path:
|
||||
- "{{BaseURL}}"
|
||||
redirects: true
|
||||
max-redirects: 3
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "This is test redirects matcher text"
|
|
@ -0,0 +1,15 @@
|
|||
id: basic-get
|
||||
|
||||
info:
|
||||
name: Basic GET Request
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- method: GET
|
||||
path:
|
||||
- "{{BaseURL}}"
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "This is test matcher text"
|
|
@ -0,0 +1,19 @@
|
|||
id: basic-post-body
|
||||
|
||||
info:
|
||||
name: Basic POST Body Request
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- method: POST
|
||||
path:
|
||||
- "{{BaseURL}}"
|
||||
headers:
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Content-Length: 1 # as long as there is a value, nuclei will auto-recalculate it.
|
||||
body: username=test&password=nuclei
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "This is test post-body matcher text"
|
|
@ -0,0 +1,19 @@
|
|||
id: basic-post-json-body
|
||||
|
||||
info:
|
||||
name: Basic POST JSON Body Request
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- method: POST
|
||||
path:
|
||||
- "{{BaseURL}}"
|
||||
headers:
|
||||
Content-Type: application/json
|
||||
Content-Length: 1
|
||||
body: '{"username":"test","password":"nuclei"}'
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "This is test post-json-body matcher text"
|
|
@ -0,0 +1,29 @@
|
|||
id: basic-post-multipart-body
|
||||
|
||||
info:
|
||||
name: Basic POST Multipart Request
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- method: POST
|
||||
path:
|
||||
- "{{BaseURL}}"
|
||||
headers:
|
||||
Content-Type: multipart/form-data; boundary=d64a5c6be2120f494d87b096fff6efe6d3248474d4de2debb1d387b3d8e8
|
||||
Content-Length: 1
|
||||
body: |
|
||||
--d64a5c6be2120f494d87b096fff6efe6d3248474d4de2debb1d387b3d8e8
|
||||
Content-Disposition: form-data; name="username"; filename="username"
|
||||
Content-Type: application/octet-stream
|
||||
|
||||
test
|
||||
--d64a5c6be2120f494d87b096fff6efe6d3248474d4de2debb1d387b3d8e8
|
||||
Content-Disposition: form-data; name="password"
|
||||
|
||||
nuclei
|
||||
--d64a5c6be2120f494d87b096fff6efe6d3248474d4de2debb1d387b3d8e8--
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "This is test post-multipart matcher text"
|
|
@ -0,0 +1,34 @@
|
|||
id: cookiereuse-raw-example
|
||||
info:
|
||||
name: Test CookieReuse RAW Template
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- raw:
|
||||
- |
|
||||
POST / HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Content-Length: 1
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
|
||||
testing=parameter
|
||||
- |
|
||||
GET / HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
|
||||
cookie-reuse: true
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test-cookie-reuse matcher text"
|
|
@ -0,0 +1,43 @@
|
|||
id: dynamic-extractor-raw-example
|
||||
|
||||
info:
|
||||
name: Test Dynamic Extractor RAW Template
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- raw:
|
||||
- |
|
||||
POST / HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Content-Length: 1
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
|
||||
testing=parameter
|
||||
- |
|
||||
GET /?username={{randkey}} HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
extractors:
|
||||
- type: regex
|
||||
name: randkey
|
||||
part: body
|
||||
group: 1
|
||||
internal: true
|
||||
regex:
|
||||
- "Token: '([A-Za-z0-9]+)'"
|
||||
|
||||
cookie-reuse: true
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test-dynamic-extractor-raw matcher text"
|
|
@ -0,0 +1,18 @@
|
|||
id: basic-raw-query-example
|
||||
|
||||
info:
|
||||
name: Test RAW GET Query Template
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- raw:
|
||||
- |
|
||||
GET ?test=nuclei HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test raw-get-query-matcher text"
|
|
@ -0,0 +1,18 @@
|
|||
id: basic-raw-http-example
|
||||
|
||||
info:
|
||||
name: Test RAW GET Template
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- raw:
|
||||
- |
|
||||
GET / HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test raw-get-matcher text"
|
|
@ -0,0 +1,29 @@
|
|||
id: payload-raw-example
|
||||
info:
|
||||
name: Test RAW With Payload Template
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- payloads:
|
||||
username:
|
||||
- test
|
||||
password:
|
||||
- nuclei
|
||||
- guest
|
||||
attack: clusterbomb
|
||||
raw:
|
||||
- |
|
||||
POST / HTTP/1.1
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5)
|
||||
Host: {{Hostname}}
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Content-Length: 1
|
||||
another_header: {{base64('§password§')}}
|
||||
Accept: */*
|
||||
|
||||
username=§username§&password={{password}}
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is raw-payload matcher text"
|
|
@ -0,0 +1,21 @@
|
|||
id: basic-raw-http-body-example
|
||||
|
||||
info:
|
||||
name: Test RAW POST Template
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- raw:
|
||||
- |
|
||||
POST / HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Content-Length: 1
|
||||
|
||||
username=test&password=nuclei
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test raw-post-body-matcher text"
|
|
@ -0,0 +1,20 @@
|
|||
id: basic-raw-unsafe-request-example
|
||||
|
||||
info:
|
||||
name: Test RAW Unsafe Request Template
|
||||
author: pd-team
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- raw:
|
||||
- |
|
||||
GET / HTTP/1.1
|
||||
Host:
|
||||
Content-Length: 4
|
||||
|
||||
unsafe: true
|
||||
matchers-condition: and
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "This is test-raw-unsafe request matcher."
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd ../v2/cmd/nuclei
|
||||
go build
|
||||
cp nuclei ../../../integration_tests/nuclei
|
||||
cd ../integration-test
|
||||
go build
|
||||
cp integration-test ../../../integration_tests/integration-test
|
||||
cd ../../../integration_tests
|
||||
./integration-test
|
||||
# Build and run nuclei.
|
|
@ -0,0 +1,491 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/projectdiscovery/nuclei/v2/internal/testutils"
|
||||
)
|
||||
|
||||
var httpTestcases = map[string]testutils.TestCase{
|
||||
"http/get-headers.yaml": &httpGetHeaders{},
|
||||
"http/get-query-string.yaml": &httpGetQueryString{},
|
||||
"http/get-redirects.yaml": &httpGetRedirects{},
|
||||
"http/get.yaml": &httpGet{},
|
||||
"http/post-body.yaml": &httpPostBody{},
|
||||
"http/post-json-body.yaml": &httpPostJSONBody{},
|
||||
"http/post-multipart-body.yaml": &httpPostMultipartBody{},
|
||||
"http/raw-cookie-reuse.yaml": &httpRawCookieReuse{},
|
||||
"http/raw-dynamic-extractor.yaml": &httpRawDynamicExtractor{},
|
||||
"http/raw-get-query.yaml": &httpRawGetQuery{},
|
||||
"http/raw-get.yaml": &httpRawGet{},
|
||||
"http/raw-payload.yaml": &httpRawPayload{},
|
||||
"http/raw-post-body.yaml": &httpRawPostBody{},
|
||||
"http/raw-unsafe-request.yaml": &httpRawUnsafeRequest{},
|
||||
}
|
||||
|
||||
func httpDebugRequestDump(r *http.Request) {
|
||||
if debug {
|
||||
if dump, err := httputil.DumpRequest(r, true); err == nil {
|
||||
fmt.Printf("\nRequest dump: \n%s\n\n", string(dump))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type httpGetHeaders struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpGetHeaders) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
if strings.EqualFold(r.Header.Get("test"), "nuclei") {
|
||||
fmt.Fprintf(w, "This is test headers matcher text")
|
||||
}
|
||||
}))
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpGetQueryString struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpGetQueryString) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
if strings.EqualFold(r.URL.Query().Get("test"), "nuclei") {
|
||||
fmt.Fprintf(w, "This is test querystring matcher text")
|
||||
}
|
||||
}))
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpGetRedirects struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpGetRedirects) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
http.Redirect(w, r, "/redirected", 302)
|
||||
}))
|
||||
router.GET("/redirected", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
fmt.Fprintf(w, "This is test redirects matcher text")
|
||||
}))
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpGet struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpGet) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
fmt.Fprintf(w, "This is test matcher text")
|
||||
}))
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpPostBody struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpPostBody) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
var routerErr error
|
||||
|
||||
router.POST("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
if err := r.ParseForm(); err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(r.Form.Get("username"), "test") && strings.EqualFold(r.Form.Get("password"), "nuclei") {
|
||||
fmt.Fprintf(w, "This is test post-body matcher text")
|
||||
}
|
||||
}))
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return routerErr
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpPostJSONBody struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpPostJSONBody) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
var routerErr error
|
||||
|
||||
router.POST("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
|
||||
type doc struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
obj := &doc{}
|
||||
if err := json.NewDecoder(r.Body).Decode(obj); err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(obj.Username, "test") && strings.EqualFold(obj.Password, "nuclei") {
|
||||
fmt.Fprintf(w, "This is test post-json-body matcher text")
|
||||
}
|
||||
}))
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return routerErr
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpPostMultipartBody struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpPostMultipartBody) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
var routerErr error
|
||||
|
||||
router.POST("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
if err := r.ParseMultipartForm(1 * 1024); err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
password, ok := r.MultipartForm.Value["password"]
|
||||
if !ok || len(password) != 1 {
|
||||
routerErr = errors.New("no password in request")
|
||||
return
|
||||
}
|
||||
file := r.MultipartForm.File["username"]
|
||||
if len(file) != 1 {
|
||||
routerErr = errors.New("no file in request")
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(password[0], "nuclei") && strings.EqualFold(file[0].Filename, "username") {
|
||||
fmt.Fprintf(w, "This is test post-multipart matcher text")
|
||||
}
|
||||
}))
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return routerErr
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpRawDynamicExtractor struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpRawDynamicExtractor) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
var routerErr error
|
||||
|
||||
router.POST("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
if err := r.ParseForm(); err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(r.Form.Get("testing"), "parameter") {
|
||||
fmt.Fprintf(w, "Token: 'nuclei'")
|
||||
}
|
||||
}))
|
||||
router.GET("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
if strings.EqualFold(r.URL.Query().Get("username"), "nuclei") {
|
||||
fmt.Fprintf(w, "Test is test-dynamic-extractor-raw matcher text")
|
||||
}
|
||||
}))
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return routerErr
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpRawGetQuery struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpRawGetQuery) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
var routerErr error
|
||||
|
||||
router.GET("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
if strings.EqualFold(r.URL.Query().Get("test"), "nuclei") {
|
||||
fmt.Fprintf(w, "Test is test raw-get-query-matcher text")
|
||||
}
|
||||
}))
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return routerErr
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpRawGet struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpRawGet) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
var routerErr error
|
||||
|
||||
router.GET("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
|
||||
fmt.Fprintf(w, "Test is test raw-get-matcher text")
|
||||
}))
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return routerErr
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpRawPayload struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpRawPayload) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
var routerErr error
|
||||
|
||||
router.POST("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
if err := r.ParseForm(); err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
if !(strings.EqualFold(r.Header.Get("another_header"), "bnVjbGVp") || strings.EqualFold(r.Header.Get("another_header"), "Z3Vlc3Q=")) {
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(r.Form.Get("username"), "test") && (strings.EqualFold(r.Form.Get("password"), "nuclei") || strings.EqualFold(r.Form.Get("password"), "guest")) {
|
||||
fmt.Fprintf(w, "Test is raw-payload matcher text")
|
||||
}
|
||||
}))
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return routerErr
|
||||
}
|
||||
if len(results) != 2 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpRawPostBody struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpRawPostBody) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
var routerErr error
|
||||
|
||||
router.POST("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
if err := r.ParseForm(); err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(r.Form.Get("username"), "test") && strings.EqualFold(r.Form.Get("password"), "nuclei") {
|
||||
fmt.Fprintf(w, "Test is test raw-post-body-matcher text")
|
||||
}
|
||||
}))
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return routerErr
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpRawCookieReuse struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpRawCookieReuse) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
var routerErr error
|
||||
|
||||
router.POST("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
if err := r.ParseForm(); err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(r.Form.Get("testing"), "parameter") {
|
||||
http.SetCookie(w, &http.Cookie{Name: "nuclei", Value: "test"})
|
||||
}
|
||||
}))
|
||||
router.GET("/", httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
httpDebugRequestDump(r)
|
||||
if err := r.ParseForm(); err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
cookie, err := r.Cookie("nuclei")
|
||||
if err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
|
||||
if strings.EqualFold(cookie.Value, "test") {
|
||||
fmt.Fprintf(w, "Test is test-cookie-reuse matcher text")
|
||||
}
|
||||
}))
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return routerErr
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpRawUnsafeRequest struct{}
|
||||
|
||||
// Executes executes a test case and returns an error if occured
|
||||
func (h *httpRawUnsafeRequest) Execute(filePath string) error {
|
||||
var routerErr error
|
||||
|
||||
ts := testutils.NewTCPServer(func(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
conn.Write([]byte("HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 40\r\nContent-Type: text/plain; charset=utf-8\r\nDate: Thu, 25 Feb 2021 17:17:28 GMT\r\n\r\nThis is test-raw-unsafe request matcher.\r\n"))
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiAndGetResults(filePath, "http://"+ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return routerErr
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errIncorrectResultsCount(results)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
)
|
||||
|
||||
var (
|
||||
debug = os.Getenv("DEBUG") == "true"
|
||||
customTest = os.Getenv("TEST")
|
||||
)
|
||||
|
||||
func main() {
|
||||
success := aurora.Green("[✓]").String()
|
||||
failed := aurora.Red("[✘]").String()
|
||||
|
||||
for file, test := range httpTestcases {
|
||||
if customTest != "" && !strings.Contains(file, customTest) {
|
||||
continue // only run tests user asked
|
||||
}
|
||||
err := test.Execute(file)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s Test \"%s\" failed: %s\n", failed, file, err)
|
||||
} else {
|
||||
fmt.Printf("%s Test \"%s\" passed!\n", success, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func errIncorrectResultsCount(results []string) error {
|
||||
return fmt.Errorf("incorrect number of results %s", strings.Join(results, "\n\t"))
|
||||
}
|
|
@ -16,6 +16,7 @@ require (
|
|||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.6.8 // indirect
|
||||
github.com/json-iterator/go v1.1.10
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
github.com/karrick/godirwalk v1.16.1
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/mattn/go-runewidth v0.0.10 // indirect
|
||||
|
|
|
@ -154,6 +154,8 @@ github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr
|
|||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
|
||||
github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
package testutils
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RunNucleiAndGetResults returns a list of results for a template
|
||||
func RunNucleiAndGetResults(template string, URL string, debug bool) ([]string, error) {
|
||||
cmd := exec.Command("./nuclei", "-t", template, "-target", URL)
|
||||
if debug {
|
||||
cmd = exec.Command("./nuclei", "-t", template, "-target", URL, "-debug")
|
||||
cmd.Stderr = os.Stderr
|
||||
}
|
||||
data, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parts := []string{}
|
||||
items := strings.Split(string(data), "\n")
|
||||
for _, i := range items {
|
||||
if i != "" {
|
||||
parts = append(parts, i)
|
||||
}
|
||||
}
|
||||
return parts, nil
|
||||
}
|
||||
|
||||
// TestCase is a single integration test case
|
||||
type TestCase interface {
|
||||
// Execute executes a test case and returns any errors if occured
|
||||
Execute(filePath string) error
|
||||
}
|
||||
|
||||
// TCPServer creates a new tcp server that returns a response
|
||||
type TCPServer struct {
|
||||
URL string
|
||||
listener net.Listener
|
||||
}
|
||||
|
||||
// NewTCPServer creates a new TCP server from a handler
|
||||
func NewTCPServer(handler func(conn net.Conn)) *TCPServer {
|
||||
server := &TCPServer{}
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
server.URL = l.Addr().String()
|
||||
server.listener = l
|
||||
|
||||
go func() {
|
||||
for {
|
||||
// Listen for an incoming connection.
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// Handle connections in a new goroutine.
|
||||
go handler(conn)
|
||||
}
|
||||
}()
|
||||
return server
|
||||
}
|
||||
|
||||
// Close closes the TCP server
|
||||
func (s *TCPServer) Close() {
|
||||
s.listener.Close()
|
||||
}
|
Loading…
Reference in New Issue