From 6f5a8ec5d4c270758376b1334bc320ff8638db5f Mon Sep 17 00:00:00 2001 From: Jiho Lee Date: Sun, 26 Jul 2026 08:48:33 +0900 Subject: [PATCH] fix: avoid panic in BuildRequest when request response has no request --- pkg/input/types/http.go | 8 ++++++++ pkg/input/types/http_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/pkg/input/types/http.go b/pkg/input/types/http.go index 3b34660f72..9c2cddf688 100644 --- a/pkg/input/types/http.go +++ b/pkg/input/types/http.go @@ -59,6 +59,14 @@ func (rr *RequestResponse) Clone() *RequestResponse { // BuildRequest builds a retryablehttp request from the request response func (rr *RequestResponse) BuildRequest() (*retryablehttp.Request, error) { rr.once.Do(func() { + // Request is optional: UnmarshalJSON only populates it when a "request" + // key is present, so an entry carrying just a "url" leaves it nil. + // Dereferencing it below would panic with a nil pointer instead of + // surfacing a usable error, taking the whole scan down. + if rr.Request == nil { + rr.reqErr = fmt.Errorf("could not create request: no request in request response") + return + } urlx := rr.URL.Clone() var body io.Reader = nil if rr.Request.Body != "" { diff --git a/pkg/input/types/http_test.go b/pkg/input/types/http_test.go index 6fc36e620d..8cc44e39cb 100644 --- a/pkg/input/types/http_test.go +++ b/pkg/input/types/http_test.go @@ -143,3 +143,34 @@ func TestUnmarshalJSON(t *testing.T) { }) } } + +// BuildRequest used to dereference rr.Request unconditionally. Request is +// optional — UnmarshalJSON only sets it when a "request" key is present — so an +// entry carrying just a "url" (see TestUnmarshalJSON above, which relies on +// exactly that shape) panicked with a nil pointer dereference instead of +// returning an error, taking down the caller in pkg/protocols/http/request_fuzz.go. +func TestBuildRequestWithoutRequest(t *testing.T) { + var rr RequestResponse + err := rr.UnmarshalJSON([]byte(`{"url": "https://example.com/path"}`)) + require.NoError(t, err) + require.Nil(t, rr.Request) + + require.NotPanics(t, func() { + req, err := rr.BuildRequest() + require.Error(t, err) + require.Nil(t, req) + }) +} + +// Guard rail: a request response that does carry a request must still build +// normally, so the nil check above can't be satisfied by refusing everything. +func TestBuildRequestWithRequestStillWorks(t *testing.T) { + rr, err := ParseRawRequest("GET /path HTTP/1.1\r\nHost: example.com\r\n\r\n") + require.NoError(t, err) + require.NotNil(t, rr.Request) + + req, err := rr.BuildRequest() + require.NoError(t, err) + require.NotNil(t, req) + require.Equal(t, "GET", req.Method) +}