diff --git a/integration_tests/http/redirect-match-url.yaml b/integration_tests/http/redirect-match-url.yaml new file mode 100644 index 00000000..d24f96d6 --- /dev/null +++ b/integration_tests/http/redirect-match-url.yaml @@ -0,0 +1,18 @@ +id: redirect-match-url + +info: + name: Redirect Match URL + author: pdteam + severity: info + +requests: + - method: GET + path: + - "{{BaseURL}}" + stop-at-first-match: true # Confirm stop-at-first-match + redirects: true # Confirm redirected URL matched value + max-redirects: 3 + matchers: + - type: word + words: + - "This is test redirects matcher text" \ No newline at end of file diff --git a/v2/cmd/integration-test/http.go b/v2/cmd/integration-test/http.go index 153944b6..c3764f11 100644 --- a/v2/cmd/integration-test/http.go +++ b/v2/cmd/integration-test/http.go @@ -50,6 +50,7 @@ var httpTestcases = map[string]testutils.TestCase{ "http/variables.yaml": &httpVariables{}, "http/get-override-sni.yaml": &httpSniAnnotation{}, "http/get-sni.yaml": &customCLISNI{}, + "http/redirect-match-url.yaml": &httpRedirectMatchURL{}, "http/get-sni-unsafe.yaml": &customCLISNIUnsafe{}, } @@ -858,6 +859,35 @@ func (h *httpSniAnnotation) Execute(filePath string) error { return expectResultsCount(results, 1) } +type httpRedirectMatchURL struct{} + +// Execute executes a test case and returns an error if occurred +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{} // Execute executes a test case and returns an error if occurred diff --git a/v2/pkg/protocols/http/request.go b/v2/pkg/protocols/http/request.go index 796a249e..b82d0053 100644 --- a/v2/pkg/protocols/http/request.go +++ b/v2/pkg/protocols/http/request.go @@ -555,6 +555,12 @@ func (request *Request) executeRequest(reqURL string, generatedRequest *generate if generatedRequest.request != nil { matchedURL = generatedRequest.request.URL.String() } + // Give precedence to the final URL from response + if response.resp.Request != nil { + if responseURL := response.resp.Request.URL.String(); responseURL != "" { + matchedURL = responseURL + } + } finalEvent := make(output.InternalEvent) outputEvent := request.responseToDSLMap(response.resp, reqURL, matchedURL, tostring.UnsafeToString(dumpedRequest), tostring.UnsafeToString(response.fullResponse), tostring.UnsafeToString(response.body), tostring.UnsafeToString(response.headers), duration, generatedRequest.meta) @@ -598,6 +604,11 @@ func (request *Request) executeRequest(reqURL string, generatedRequest *generate dumpResponse(event, request, response.fullResponse, formedURL, responseContentType, isResponseTruncated, reqURL) callback(event) + + // Skip further responses if we have stop-at-first-match and a match + if (request.options.Options.StopAtFirstMatch || request.options.StopAtFirstMatch || request.StopAtFirstMatch) && len(event.Results) > 0 { + return nil + } } return nil }