From f7f34e80a13c532aee04f43202452c6ae94f55d2 Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Sat, 3 Jan 2026 21:57:35 +0700 Subject: [PATCH 1/2] fix(http): race condition regression The `race` condition directive was broken due to a strict dependency on `threads > 0` for parallel execution, causing templates with `race` directive enabled but no explicit threads to fall back to seq execution. This regression was introduced in v3.2.0 (#4868), which restricted parallel execution to only when `payloads` were present. Fixes #5713 to allow race conditions even w/o explicit `payloads`, and add a default thread count when race is enabled but threads is 0. Signed-off-by: Dwi Siswanto --- pkg/protocols/http/request.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/protocols/http/request.go b/pkg/protocols/http/request.go index 0b7a35bc4..222020ebf 100644 --- a/pkg/protocols/http/request.go +++ b/pkg/protocols/http/request.go @@ -501,7 +501,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa } // verify if parallel elaboration was requested - if request.Threads > 0 && len(request.Payloads) > 0 { + if request.Threads > 0 && (len(request.Payloads) > 0 || request.Race) { return request.executeParallelHTTP(input, dynamicValues, callback) } From 46c183ef22c649ea68ff572ca98de7ac57f7799c Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Sat, 3 Jan 2026 21:59:43 +0700 Subject: [PATCH 2/2] test: add race with delay integration test Signed-off-by: Dwi Siswanto --- cmd/integration-test/http.go | 47 +++++++++++++++++++ .../http/race-condition-with-delay.yaml | 28 +++++++++++ 2 files changed, 75 insertions(+) create mode 100644 integration_tests/protocols/http/race-condition-with-delay.yaml diff --git a/cmd/integration-test/http.go b/cmd/integration-test/http.go index 17effbd21..d5d2e0a14 100644 --- a/cmd/integration-test/http.go +++ b/cmd/integration-test/http.go @@ -11,6 +11,7 @@ import ( "regexp" "strconv" "strings" + "sync" "time" "github.com/julienschmidt/httprouter" @@ -62,6 +63,7 @@ var httpTestcases = []TestCaseInfo{ {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/race-condition-with-delay.yaml", TestCase: &httpRaceWithDelay{}}, {Path: "protocols/http/race-with-variables.yaml", TestCase: &httpRaceWithVariables{}}, {Path: "protocols/http/stop-at-first-match.yaml", TestCase: &httpStopAtFirstMatch{}}, {Path: "protocols/http/stop-at-first-match-with-extractors.yaml", TestCase: &httpStopAtFirstMatchWithExtractors{}}, @@ -1156,6 +1158,51 @@ func (h *httpRaceMultiple) Execute(filePath string) error { return expectResultsCount(results, 5) } +type httpRaceWithDelay struct{} + +// Execute executes a test case and returns an error if occurred +func (h *httpRaceWithDelay) Execute(filePath string) error { + var requestTimes []time.Time + var mu sync.Mutex + + router := httprouter.New() + router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + mu.Lock() + requestTimes = append(requestTimes, time.Now()) + mu.Unlock() + time.Sleep(2 * time.Second) + w.WriteHeader(http.StatusOK) + }) + ts := httptest.NewServer(router) + defer ts.Close() + + results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug) + if err != nil { + return err + } + if err := expectResultsCount(results, 3); err != nil { + return err + } + + mu.Lock() + defer mu.Unlock() + if len(requestTimes) != 3 { + return fmt.Errorf("expected 3 requests, got %d", len(requestTimes)) + } + + // Check concurrency of first two requests (should be very close) + if diff := requestTimes[1].Sub(requestTimes[0]); diff > 500*time.Millisecond { + return fmt.Errorf("expected first 2 requests to be concurrent, diff: %v", diff) + } + + // Check delay of third request (should be after ~2s) + if diff := requestTimes[2].Sub(requestTimes[0]); diff < 1500*time.Millisecond { + return fmt.Errorf("expected 3rd request to be delayed, diff: %v", diff) + } + + return nil +} + type httpRaceWithVariables struct{} // Execute tests that variables and constants are properly resolved in race mode. diff --git a/integration_tests/protocols/http/race-condition-with-delay.yaml b/integration_tests/protocols/http/race-condition-with-delay.yaml new file mode 100644 index 000000000..fd54941c6 --- /dev/null +++ b/integration_tests/protocols/http/race-condition-with-delay.yaml @@ -0,0 +1,28 @@ +id: race-condition-with-delay + +info: + name: Race Condition Testing with Delay + author: pdteam + severity: info + description: | + Test race condition handling with induced server delay. + tags: test + +http: + - raw: + - | + GET / HTTP/1.1 + Host: {{Hostname}} + - | + GET / HTTP/1.1 + Host: {{Hostname}} + - | + GET / HTTP/1.1 + Host: {{Hostname}} + + threads: 2 + race: true + matchers: + - type: status + status: + - 200