package main
import (
"errors"
"fmt"
"net/http"
"net/http/httptest"
"net/http/httputil"
"os"
"reflect"
"regexp"
"strconv"
"strings"
"time"
"github.com/julienschmidt/httprouter"
"gopkg.in/yaml.v2"
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
"github.com/projectdiscovery/retryablehttp-go"
"github.com/projectdiscovery/utils/errkit"
logutil "github.com/projectdiscovery/utils/log"
sliceutil "github.com/projectdiscovery/utils/slice"
stringsutil "github.com/projectdiscovery/utils/strings"
unitutils "github.com/projectdiscovery/utils/unit"
)
var httpTestcases = []TestCaseInfo{
{Path: "protocols/http/get-headers.yaml", TestCase: &httpGetHeaders{}},
{Path: "protocols/http/get-query-string.yaml", TestCase: &httpGetQueryString{}},
{Path: "protocols/http/get-redirects.yaml", TestCase: &httpGetRedirects{}},
{Path: "protocols/http/get-host-redirects.yaml", TestCase: &httpGetHostRedirects{}},
{Path: "protocols/http/disable-redirects.yaml", TestCase: &httpDisableRedirects{}},
{Path: "protocols/http/get.yaml", TestCase: &httpGet{}},
{Path: "protocols/http/post-body.yaml", TestCase: &httpPostBody{}},
{Path: "protocols/http/post-json-body.yaml", TestCase: &httpPostJSONBody{}},
{Path: "protocols/http/post-multipart-body.yaml", TestCase: &httpPostMultipartBody{}},
{Path: "protocols/http/raw-cookie-reuse.yaml", TestCase: &httpRawCookieReuse{}},
{Path: "protocols/http/raw-dynamic-extractor.yaml", TestCase: &httpRawDynamicExtractor{}},
{Path: "protocols/http/raw-get-query.yaml", TestCase: &httpRawGetQuery{}},
{Path: "protocols/http/raw-get.yaml", TestCase: &httpRawGet{}},
{Path: "protocols/http/raw-with-params.yaml", TestCase: &httpRawWithParams{}},
{Path: "protocols/http/raw-unsafe-with-params.yaml", TestCase: &httpRawWithParams{}},
{Path: "protocols/http/raw-path-trailing-slash.yaml", TestCase: &httpRawPathTrailingSlash{}},
{Path: "protocols/http/raw-payload.yaml", TestCase: &httpRawPayload{}},
{Path: "protocols/http/raw-post-body.yaml", TestCase: &httpRawPostBody{}},
{Path: "protocols/http/raw-unsafe-path.yaml", TestCase: &httpRawUnsafePath{}},
{Path: "protocols/http/http-paths.yaml", TestCase: &httpPaths{}},
{Path: "protocols/http/request-condition.yaml", TestCase: &httpRequestCondition{}},
{Path: "protocols/http/request-condition-new.yaml", TestCase: &httpRequestCondition{}},
{Path: "protocols/http/self-contained.yaml", TestCase: &httpRequestSelfContained{}},
{Path: "protocols/http/self-contained-with-path.yaml", TestCase: &httpRequestSelfContained{}},
{Path: "protocols/http/self-contained-with-params.yaml", TestCase: &httpRequestSelfContainedWithParams{}},
{Path: "protocols/http/self-contained-file-input.yaml", TestCase: &httpRequestSelfContainedFileInput{}},
{Path: "protocols/http/get-case-insensitive.yaml", TestCase: &httpGetCaseInsensitive{}},
{Path: "protocols/http/get.yaml,protocols/http/get-case-insensitive.yaml", TestCase: &httpGetCaseInsensitiveCluster{}},
{Path: "protocols/http/get-redirects-chain-headers.yaml", TestCase: &httpGetRedirectsChainHeaders{}},
{Path: "protocols/http/dsl-matcher-variable.yaml", TestCase: &httpDSLVariable{}},
{Path: "protocols/http/dsl-functions.yaml", TestCase: &httpDSLFunctions{}},
{Path: "protocols/http/race-simple.yaml", TestCase: &httpRaceSimple{}},
{Path: "protocols/http/race-multiple.yaml", TestCase: &httpRaceMultiple{}},
{Path: "protocols/http/stop-at-first-match.yaml", TestCase: &httpStopAtFirstMatch{}},
{Path: "protocols/http/stop-at-first-match-with-extractors.yaml", TestCase: &httpStopAtFirstMatchWithExtractors{}},
{Path: "protocols/http/variables.yaml", TestCase: &httpVariables{}},
{Path: "protocols/http/variable-dsl-function.yaml", TestCase: &httpVariableDSLFunction{}},
{Path: "protocols/http/get-override-sni.yaml", TestCase: &httpSniAnnotation{}},
{Path: "protocols/http/get-sni.yaml", TestCase: &customCLISNI{}},
{Path: "protocols/http/redirect-match-url.yaml", TestCase: &httpRedirectMatchURL{}},
{Path: "protocols/http/get-sni-unsafe.yaml", TestCase: &customCLISNIUnsafe{}},
{Path: "protocols/http/annotation-timeout.yaml", TestCase: &annotationTimeout{}},
{Path: "protocols/http/custom-attack-type.yaml", TestCase: &customAttackType{}},
{Path: "protocols/http/get-all-ips.yaml", TestCase: &scanAllIPS{}},
{Path: "protocols/http/get-without-scheme.yaml", TestCase: &httpGetWithoutScheme{}},
{Path: "protocols/http/cl-body-without-header.yaml", TestCase: &httpCLBodyWithoutHeader{}},
{Path: "protocols/http/cl-body-with-header.yaml", TestCase: &httpCLBodyWithHeader{}},
{Path: "protocols/http/cli-with-constants.yaml", TestCase: &ConstantWithCliVar{}},
{Path: "protocols/http/matcher-status.yaml", TestCase: &matcherStatusTest{}},
{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{}},
{Path: "protocols/http/raw-path-single-slash.yaml", TestCase: &httpRawPathSingleSlash{}},
{Path: "protocols/http/raw-unsafe-path-single-slash.yaml", TestCase: &httpRawUnsafePathSingleSlash{}},
}
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 := `<!DOCTYPE html>
<html lang="en">
<body>
<a href="/domains">Domains</a>
</body>
</html>`
_, _ = fmt.Fprint(w, html)
})
router.GET("/domains", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
html := `<!DOCTYPE html>
<html lang="en">
<head>
<title>Dynamic Extractor Test</title>
</head>
<body>
<!-- The content of the title tag matches the regex pattern for both the extractor and matcher for 'title' -->
</body>
</html>
`
_, _ = 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{}
func (h *httpInteractshRequest) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
value := r.Header.Get("url")
if value != "" {
if resp, _ := retryablehttp.DefaultClient().Get(value); resp != nil {
_ = resp.Body.Close()
}
}
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
return expectResultsCount(results, 1, 2)
}
type httpDefaultMatcherCondition struct{}
func (d *httpDefaultMatcherCondition) Execute(filePath string) error {
router := httprouter.New()
var routerErr error
router.GET("/interactsh/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
value := r.URL.Query().Get("url")
if value != "" {
if _, err := retryablehttp.DefaultClient().Get("https://" + value); err != nil {
routerErr = err
}
}
w.WriteHeader(http.StatusNotFound)
})
router.GET("/status/", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
w.WriteHeader(http.StatusOK)
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/status", debug)
if err != nil {
return err
}
if err := expectResultsCount(results, 1); err != nil {
return err
}
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/interactsh", debug)
if err != nil {
return err
}
if routerErr != nil {
return errkit.Wrap(routerErr, "failed to send http request to interactsh server")
}
if err := expectResultsCount(results, 1); err != nil {
return err
}
return nil
}
type httpInteractshStopAtFirstMatchRequest struct{}
func (h *httpInteractshStopAtFirstMatchRequest) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
value := r.Header.Get("url")
if value != "" {
if resp, _ := retryablehttp.DefaultClient().Get(value); resp != nil {
_ = resp.Body.Close()
}
}
})
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 httpGetHeaders struct{}
func (h *httpGetHeaders) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
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.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
type httpGetQueryString struct{}
func (h *httpGetQueryString) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
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.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
type httpGetRedirects struct{}
func (h *httpGetRedirects) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
http.Redirect(w, r, "/redirected", http.StatusFound)
})
router.GET("/redirected", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprintf(w, "This is test redirects matcher text")
})
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 httpGetHostRedirects struct{}
func (h *httpGetHostRedirects) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
http.Redirect(w, r, "/redirected1", http.StatusFound)
})
router.GET("/redirected1", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
http.Redirect(w, r, "redirected2", http.StatusFound)
})
router.GET("/redirected2", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
http.Redirect(w, r, "/redirected3", http.StatusFound)
})
router.GET("/redirected3", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
http.Redirect(w, r, "https://scanme.sh", http.StatusTemporaryRedirect)
})
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 httpDisableRedirects struct{}
func (h *httpDisableRedirects) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
http.Redirect(w, r, "/redirected", http.StatusMovedPermanently)
})
router.GET("/redirected", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprintf(w, "This is test redirects matcher text")
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-dr")
if err != nil {
return err
}
return expectResultsCount(results, 0)
}
type httpGet struct{}
func (h *httpGet) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprintf(w, "This is test matcher text")
})
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 httpDSLVariable struct{}
func (h *httpDSLVariable) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprintf(w, "This is test matcher text")
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
return expectResultsCount(results, 5)
}
type httpDSLFunctions struct{}
func (h *httpDSLFunctions) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
request, err := httputil.DumpRequest(r, true)
if err != nil {
_, _ = fmt.Fprint(w, err.Error())
} else {
_, _ = fmt.Fprint(w, string(request))
}
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-nc")
if err != nil {
return err
}
if err := expectResultsCount(results, 1); err != nil {
return err
}
resultPart, err := stringsutil.After(results[0], ts.URL)
if err != nil {
return err
}
resultPart = stringsutil.TrimPrefixAny(resultPart, "/", " ", "[")
extracted := strings.Split(resultPart, ",")
numberOfDslFunctions := 88
if len(extracted) != numberOfDslFunctions {
return errors.New("incorrect number of results")
}
for _, header := range extracted {
header = strings.Trim(header, `"`)
parts := strings.Split(header, ": ")
index, err := strconv.Atoi(parts[0])
if err != nil {
return err
}
if index < 0 || index > numberOfDslFunctions {
return fmt.Errorf("incorrect header index found: %d", index)
}
if strings.TrimSpace(parts[1]) == "" {
return fmt.Errorf("the DSL expression with index %d was not evaluated correctly", index)
}
}
return nil
}
type httpPostBody struct{}
func (h *httpPostBody) Execute(filePath string) error {
router := httprouter.New()
var routerErr error
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
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.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
if routerErr != nil {
return routerErr
}
return expectResultsCount(results, 1)
}
type httpPostJSONBody struct{}
func (h *httpPostJSONBody) Execute(filePath string) error {
router := httprouter.New()
var routerErr error
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
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.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
if routerErr != nil {
return routerErr
}
return expectResultsCount(results, 1)
}
type httpPostMultipartBody struct{}
func (h *httpPostMultipartBody) Execute(filePath string) error {
router := httprouter.New()
var routerErr error
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
if err := r.ParseMultipartForm(unitutils.Mega); 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.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
if routerErr != nil {
return routerErr
}
return expectResultsCount(results, 1)
}
type httpRawDynamicExtractor struct{}
func (h *httpRawDynamicExtractor) Execute(filePath string) error {
router := httprouter.New()
var routerErr error
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
if err := r.ParseForm(); err != nil {
routerErr = err
return
}
if strings.EqualFold(r.Form.Get("testing"), "parameter") {
_, _ = fmt.Fprintf(w, "Token: 'nuclei'")
}
})
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
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.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
if routerErr != nil {
return routerErr
}
return expectResultsCount(results, 1)
}
type httpRawGetQuery struct{}
func (h *httpRawGetQuery) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
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.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
type httpRawGet struct{}
func (h *httpRawGet) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprintf(w, "Test is test raw-get-matcher text")
})
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 httpRawWithParams struct{}
func (h *httpRawWithParams) Execute(filePath string) error {
router := httprouter.New()
var errx error
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
params := r.URL.Query()
if !reflect.DeepEqual(params["key1"], []string{"value1"}) {
errx = errkit.Append(errx, errkit.New("key1 not found in params", "expected", []string{"value1"}, "got", params["key1"]))
}
if !reflect.DeepEqual(params["key2"], []string{"value2"}) {
errx = errkit.Append(errx, errkit.New("key2 not found in params", "expected", []string{"value2"}, "got", params["key2"]))
}
_, _ = fmt.Fprintf(w, "Test is test raw-params-matcher text")
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/?key1=value1", debug)
if err != nil {
return err
}
if errx != nil {
return err
}
return expectResultsCount(results, 1)
}
type httpRawPathTrailingSlash struct{}
func (h *httpRawPathTrailingSlash) Execute(filepath string) error {
router := httprouter.New()
var routerErr error
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
if r.RequestURI != "/test/..;/..;/" {
routerErr = fmt.Errorf("expected path /test/..;/..;/ but got %v", r.RequestURI)
return
}
})
ts := httptest.NewServer(router)
defer ts.Close()
_, err := testutils.RunNucleiTemplateAndGetResults(filepath, ts.URL, debug)
if err != nil {
return err
}
if routerErr != nil {
return routerErr
}
return nil
}
type httpRawPayload struct{}
func (h *httpRawPayload) Execute(filePath string) error {
router := httprouter.New()
var routerErr error
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
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.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
if routerErr != nil {
return routerErr
}
return expectResultsCount(results, 2)
}
type httpRawPostBody struct{}
func (h *httpRawPostBody) Execute(filePath string) error {
router := httprouter.New()
var routerErr error
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
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.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
if routerErr != nil {
return routerErr
}
return expectResultsCount(results, 1)
}
type httpRawUnsafePath struct{}
func (h *httpRawUnsafePath) Execute(filepath string) error {
bin, err := os.ReadFile(filepath)
if err != nil {
return err
}
type template struct {
Info struct {
Reference []string `yaml:"reference"`
}
}
var tpl template
if err = yaml.Unmarshal(bin, &tpl); err != nil {
return err
}
expected := []string{}
expected = append(expected, tpl.Info.Reference...)
if len(expected) == 0 {
return fmt.Errorf("something went wrong with %v template", filepath)
}
results, err := testutils.RunNucleiBinaryAndGetCombinedOutput(debug, []string{"-t", filepath, "-u", "scanme.sh", "-debug-req"})
if err != nil {
return err
}
actual := []string{}
for _, v := range strings.Split(results, "\n") {
if strings.Contains(v, "GET") {
parts := strings.Fields(v)
if len(parts) == 3 {
actual = append(actual, parts[1])
}
}
}
if !reflect.DeepEqual(expected, actual) {
return fmt.Errorf("%8v: %v\n%-8v: %v", "expected", expected, "actual", actual)
}
return nil
}
type httpPaths struct{}
func (h *httpPaths) Execute(filepath string) error {
bin, err := os.ReadFile(filepath)
if err != nil {
return err
}
type template struct {
Info struct {
Reference []string `yaml:"reference"`
}
}
var tpl template
if err = yaml.Unmarshal(bin, &tpl); err != nil {
return err
}
expected := []string{}
expected = append(expected, tpl.Info.Reference...)
if len(expected) == 0 {
return fmt.Errorf("something went wrong with %v template", filepath)
}
results, err := testutils.RunNucleiBinaryAndGetCombinedOutput(debug, []string{"-t", filepath, "-u", "scanme.sh", "-debug-req"})
if err != nil {
return err
}
actual := []string{}
for _, v := range strings.Split(results, "\n") {
if strings.Contains(v, "GET") {
parts := strings.Fields(v)
if len(parts) == 3 {
actual = append(actual, parts[1])
}
}
}
if len(expected) > len(actual) {
actualValuesIndex := max(len(actual)-1, 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
}
type httpRawCookieReuse struct{}
func (h *httpRawCookieReuse) Execute(filePath string) error {
router := httprouter.New()
var routerErr error
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
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("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
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.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
if routerErr != nil {
return routerErr
}
return expectResultsCount(results, 1)
}
type httpRequestCondition struct{}
func (h *httpRequestCondition) Execute(filePath string) error {
router := httprouter.New()
router.GET("/200", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.WriteHeader(http.StatusOK)
})
router.GET("/400", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.WriteHeader(http.StatusBadRequest)
})
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 httpRequestSelfContained struct{}
func (h *httpRequestSelfContained) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = w.Write([]byte("This is self-contained response"))
})
server := &http.Server{
Addr: fmt.Sprintf("localhost:%d", defaultStaticPort),
Handler: router,
}
go func() {
_ = server.ListenAndServe()
}()
defer func() {
_ = server.Close()
}()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-esc")
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
type httpRequestSelfContainedWithParams struct{}
func (h *httpRequestSelfContainedWithParams) Execute(filePath string) error {
router := httprouter.New()
var errx error
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
params := r.URL.Query()
if !reflect.DeepEqual(params["something"], []string{"here"}) {
errx = errkit.Append(errx, errkit.New("something not found in params", "expected", []string{"here"}, "got", params["something"]))
}
if !reflect.DeepEqual(params["key"], []string{"value"}) {
errx = errkit.Append(errx, errkit.New("key not found in params", "expected", []string{"value"}, "got", params["key"]))
}
_, _ = w.Write([]byte("This is self-contained response"))
})
server := &http.Server{
Addr: fmt.Sprintf("localhost:%d", defaultStaticPort),
Handler: router,
}
go func() {
_ = server.ListenAndServe()
}()
defer func() {
_ = server.Close()
}()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-esc")
if err != nil {
return err
}
if errx != nil {
return errx
}
return expectResultsCount(results, 1)
}
type httpRequestSelfContainedFileInput struct{}
func (h *httpRequestSelfContainedFileInput) Execute(filePath string) error {
router := httprouter.New()
gotReqToEndpoints := []string{}
router.GET("/one", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
gotReqToEndpoints = append(gotReqToEndpoints, "/one")
_, _ = w.Write([]byte("This is self-contained response"))
})
router.GET("/two", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
gotReqToEndpoints = append(gotReqToEndpoints, "/two")
_, _ = w.Write([]byte("This is self-contained response"))
})
server := &http.Server{
Addr: fmt.Sprintf("localhost:%d", defaultStaticPort),
Handler: router,
}
go func() {
_ = server.ListenAndServe()
}()
defer func() {
_ = server.Close()
}()
FileLoc, err := os.CreateTemp("", "self-contained-payload-*.txt")
if err != nil {
return errkit.Wrap(err, "failed to create temp file")
}
if _, err := FileLoc.Write([]byte("one\ntwo\n")); err != nil {
return errkit.Wrap(err, "failed to write payload to temp file")
}
defer func() {
_ = FileLoc.Close()
}()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-V", "test="+FileLoc.Name(), "-esc")
if err != nil {
return err
}
if err := expectResultsCount(results, 4); err != nil {
return err
}
if !sliceutil.ElementsMatch(gotReqToEndpoints, []string{"/one", "/two", "/one", "/two"}) {
return errkit.New("expected requests to be sent to `/one` and `/two` endpoints but were sent to `%v`", gotReqToEndpoints, "filePath", filePath)
}
return nil
}
type httpGetCaseInsensitive struct{}
func (h *httpGetCaseInsensitive) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprintf(w, "THIS IS TEST MATCHER TEXT")
})
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 httpGetCaseInsensitiveCluster struct{}
func (h *httpGetCaseInsensitiveCluster) Execute(filesPath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprintf(w, "This is test matcher text")
})
ts := httptest.NewServer(router)
defer ts.Close()
files := strings.Split(filesPath, ",")
results, err := testutils.RunNucleiTemplateAndGetResults(files[0], ts.URL, debug, "-t", files[1])
if err != nil {
return err
}
return expectResultsCount(results, 2)
}
type httpGetRedirectsChainHeaders struct{}
func (h *httpGetRedirectsChainHeaders) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
http.Redirect(w, r, "/redirected", http.StatusFound)
})
router.GET("/redirected", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.Header().Set("Secret", "TestRedirectHeaderMatch")
http.Redirect(w, r, "/final", http.StatusFound)
})
router.GET("/final", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = w.Write([]byte("ok"))
})
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 httpRaceSimple struct{}
func (h *httpRaceSimple) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.WriteHeader(http.StatusOK)
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
return expectResultsCount(results, 10)
}
type httpRaceMultiple struct{}
func (h *httpRaceMultiple) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.WriteHeader(http.StatusOK)
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
return expectResultsCount(results, 5)
}
type httpStopAtFirstMatch struct{}
func (h *httpStopAtFirstMatch) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprintf(w, "This is test")
})
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 httpStopAtFirstMatchWithExtractors struct{}
func (h *httpStopAtFirstMatchWithExtractors) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprintf(w, "This is test")
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
return expectResultsCount(results, 2)
}
type httpVariables struct{}
func (h *httpVariables) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprintf(w, "%s\n%s\n%s", r.Header.Get("Test"), r.Header.Get("Another"), r.Header.Get("Email"))
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
if err := expectResultsCount(results, 1); err != nil {
return err
}
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-var", "a1=failed")
if err != nil {
return err
}
return expectResultsCount(results, 0)
}
type httpVariableDSLFunction struct{}
func (h *httpVariableDSLFunction) Execute(filePath string) error {
results, err := testutils.RunNucleiBinaryAndGetCombinedOutput(debug, []string{"-t", filePath, "-u", "https://scanme.sh", "-debug-req"})
if err != nil {
return err
}
actual := []string{}
for _, v := range strings.Split(results, "\n") {
if strings.Contains(v, "GET") {
parts := strings.Fields(v)
if len(parts) == 3 {
actual = append(actual, parts[1])
}
}
}
if len(actual) == 2 && actual[0] == actual[1] {
return nil
}
return fmt.Errorf("expected 2 requests with same URL, got %v", actual)
}
type customCLISNI struct{}
func (h *customCLISNI) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
if r.TLS.ServerName == "test" {
_, _ = w.Write([]byte("test-ok"))
} else {
_, _ = w.Write([]byte("test-ko"))
}
})
ts := httptest.NewTLSServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-sni", "test")
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
type httpSniAnnotation struct{}
func (h *httpSniAnnotation) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
if r.TLS.ServerName == "test" {
_, _ = w.Write([]byte("test-ok"))
} else {
_, _ = w.Write([]byte("test-ko"))
}
})
ts := httptest.NewTLSServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
type httpRedirectMatchURL struct{}
func (h *httpRedirectMatchURL) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
http.Redirect(w, r, "/redirected", http.StatusFound)
_, _ = w.Write([]byte("This is test redirects matcher text"))
})
router.GET("/redirected", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprintf(w, "This is test redirects matcher text")
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-no-meta")
if err != nil {
return err
}
if err := expectResultsCount(results, 1); err != nil {
return err
}
if results[0] != fmt.Sprintf("%s/redirected", ts.URL) {
return fmt.Errorf("mismatched url found: %s", results[0])
}
return nil
}
type customCLISNIUnsafe struct{}
func (h *customCLISNIUnsafe) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
if r.TLS.ServerName == "test" {
_, _ = w.Write([]byte("test-ok"))
} else {
_, _ = w.Write([]byte("test-ko"))
}
})
ts := httptest.NewTLSServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-sni", "test")
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
type annotationTimeout struct{}
func (h *annotationTimeout) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
time.Sleep(4 * time.Second)
_, _ = fmt.Fprintf(w, "This is test matcher text")
})
ts := httptest.NewTLSServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-timeout", "1")
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
type customAttackType struct{}
func (h *customAttackType) Execute(filePath string) error {
router := httprouter.New()
got := []string{}
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
got = append(got, r.URL.RawQuery)
_, _ = fmt.Fprintf(w, "This is test custom payload")
})
ts := httptest.NewTLSServer(router)
defer ts.Close()
_, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-attack-type", "clusterbomb")
if err != nil {
return err
}
return expectResultsCount(got, 4)
}
type scanAllIPS struct{}
func (h *scanAllIPS) Execute(filePath string) error {
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, "https://scanme.sh", debug, "-scan-all-ips", "-iv", "4")
if err != nil {
return err
}
return expectResultsCount(got, 1)
}
type httpGetWithoutScheme struct{}
func (h *httpGetWithoutScheme) Execute(filePath string) error {
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, "scanme.sh", debug)
if err != nil {
return err
}
return expectResultsCount(got, 1)
}
type httpCLBodyWithoutHeader struct{}
func (h *httpCLBodyWithoutHeader) Execute(filePath string) error {
logutil.DisableDefaultLogger()
defer logutil.EnableDefaultLogger()
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.Header()["Content-Length"] = []string{"-1"}
_, _ = fmt.Fprintf(w, "this is a test")
})
ts := httptest.NewTLSServer(router)
defer ts.Close()
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
return expectResultsCount(got, 1)
}
type httpCLBodyWithHeader struct{}
func (h *httpCLBodyWithHeader) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.Header()["Content-Length"] = []string{"50000"}
_, _ = fmt.Fprintf(w, "this is a test")
})
ts := httptest.NewTLSServer(router)
defer ts.Close()
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
return expectResultsCount(got, 1)
}
type ConstantWithCliVar struct{}
func (h *ConstantWithCliVar) Execute(filePath string) error {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprint(w, r.URL.Query().Get("p"))
})
ts := httptest.NewTLSServer(router)
defer ts.Close()
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-V", "test=fromcli")
if err != nil {
return err
}
return expectResultsCount(got, 1)
}
type matcherStatusTest struct{}
func (h *matcherStatusTest) Execute(filePath string) error {
router := httprouter.New()
router.GET("/200", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.WriteHeader(http.StatusOK)
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-ms")
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
type httpDisablePathAutomerge struct{}
func (h *httpDisablePathAutomerge) Execute(filePath string) error {
router := httprouter.New()
router.GET("/api/v1/test", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprint(w, r.URL.Query().Get("id"))
})
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprint(w, "empty path in raw request")
})
ts := httptest.NewServer(router)
defer ts.Close()
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/api/v1/user", debug)
if err != nil {
return err
}
return expectResultsCount(got, 2)
}
type httpInteractshRequestsWithMCAnd struct{}
func (h *httpInteractshRequestsWithMCAnd) Execute(filePath string) error {
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, "honey.scanme.sh", debug)
if err != nil {
return err
}
return expectResultsCount(got, 1)
}
type httpPreprocessor struct{}
func (h *httpPreprocessor) Execute(filePath string) error {
router := httprouter.New()
re := regexp.MustCompile(`[A-Za-z0-9]{25,}`)
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
value := r.URL.RequestURI()
if re.MatchString(value) {
w.WriteHeader(http.StatusOK)
_, _ = fmt.Fprint(w, "ok")
} else {
w.WriteHeader(http.StatusBadRequest)
_, _ = fmt.Fprint(w, "not ok")
}
})
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 httpMultiRequest struct{}
func (h *httpMultiRequest) Execute(filePath string) error {
router := httprouter.New()
router.GET("/ping", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.WriteHeader(http.StatusOK)
_, _ = fmt.Fprint(w, "ping")
})
router.GET("/pong", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.WriteHeader(http.StatusOK)
_, _ = fmt.Fprint(w, "pong")
})
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 httpRawPathSingleSlash struct{}
func (h *httpRawPathSingleSlash) Execute(filepath string) error {
expectedPath := "/index.php"
results, err := testutils.RunNucleiBinaryAndGetCombinedOutput(debug, []string{"-t", filepath, "-u", "scanme.sh/index.php", "-debug-req"})
if err != nil {
return err
}
var actual string
for _, v := range strings.Split(results, "\n") {
if strings.Contains(v, "GET") {
parts := strings.Fields(v)
if len(parts) == 3 {
actual = parts[1]
}
}
}
if actual != expectedPath {
return fmt.Errorf("expected: %v\n\nactual: %v", expectedPath, actual)
}
return nil
}
type httpRawUnsafePathSingleSlash struct{}
func (h *httpRawUnsafePathSingleSlash) Execute(filepath string) error {
expectedPath := "/index.php"
results, err := testutils.RunNucleiBinaryAndGetCombinedOutput(debug, []string{"-t", filepath, "-u", "scanme.sh/index.php", "-debug-req"})
if err != nil {
return err
}
var actual string
for _, v := range strings.Split(results, "\n") {
if strings.Contains(v, "GET") {
parts := strings.Fields(v)
if len(parts) == 3 {
actual = parts[1]
}
}
}
if actual != expectedPath {
return fmt.Errorf("expected: %v\n\nactual: %v", expectedPath, actual)
}
return nil
}