From 85a36fc6bbdb615a7053e04d7e951710b89747d6 Mon Sep 17 00:00:00 2001 From: Apotheosis <0xapotheosis@gmail.com> Date: Fri, 12 Jun 2026 14:56:42 +1000 Subject: [PATCH] fix(cosmossdk): reject malformed tx history cursor at request boundary A caller-controlled cursor with a null state value (e.g. {"state":{"x":null}}) decodes to a nil *CursorState entry. That entry is later dereferenced in a filterByCursor errgroup worker goroutine, where the panic is not recovered by net/http (it only recovers the handler goroutine) and crashes the process. Validate the decoded cursor in ValidatePagingParams and return 400 Bad Request before any source state is built or worker goroutine is spawned, so a malformed cursor can never reach the crash path. Co-Authored-By: Claude Opus 4.8 (1M context) --- go/shared/cosmossdk/api.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/go/shared/cosmossdk/api.go b/go/shared/cosmossdk/api.go index a4bcfba04..bc0303155 100644 --- a/go/shared/cosmossdk/api.go +++ b/go/shared/cosmossdk/api.go @@ -85,6 +85,22 @@ func (a *API) Root(w http.ResponseWriter, r *http.Request) { func (a *API) ValidatePagingParams(w http.ResponseWriter, r *http.Request, defaultPageSize int, maxPageSize *int) (string, int, error) { cursor := r.URL.Query().Get("cursor") + if cursor != "" { + c := &Cursor{} + if err := c.Decode(cursor); err != nil { + api.HandleError(w, http.StatusBadRequest, "invalid cursor") + return cursor, 0, fmt.Errorf("invalid cursor: %w", err) + } + + // reject nil state entries to avoid a downstream nil pointer dereference + for source, state := range c.State { + if state == nil { + api.HandleError(w, http.StatusBadRequest, "invalid cursor") + return cursor, 0, fmt.Errorf("invalid cursor: nil state for source %q", source) + } + } + } + pageSizeQ := r.URL.Query().Get("pageSize") if pageSizeQ == "" { pageSizeQ = strconv.Itoa(defaultPageSize)