From df9830aea66325bc9d1d958af79c3079f10fdacf Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Fri, 12 Jun 2026 12:19:03 +1000 Subject: [PATCH 1/2] fix(api): hide internal error detail from 5xx responses --- pkg/api/handler.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/pkg/api/handler.go b/pkg/api/handler.go index 004e329..c527dde 100644 --- a/pkg/api/handler.go +++ b/pkg/api/handler.go @@ -113,18 +113,14 @@ func (h *Handler) wrappedHandler(handler func(ctx context.Context, r *http.Reque response, err = handler(ctx, r, p, contentType) if err != nil { - if writeErr := WriteErrorResponse(w, err.Error(), response.StatusCode); writeErr != nil { - h.log.WithError(writeErr).Error("Failed to write error response") - } + h.writeError(w, registeredPath, response.StatusCode, err) return } data, err := response.MarshalAs(contentType) if err != nil { - if writeErr := WriteErrorResponse(w, err.Error(), http.StatusInternalServerError); writeErr != nil { - h.log.WithError(writeErr).Error("Failed to write error response") - } + h.writeError(w, registeredPath, http.StatusInternalServerError, err) return } @@ -139,6 +135,23 @@ func (h *Handler) wrappedHandler(handler func(ctx context.Context, r *http.Reque } } +// writeError responds with an error, hiding internal error detail from clients +// on 5xx responses while still logging it server-side. Client errors (4xx) keep +// their message since it is actionable feedback about the request. +func (h *Handler) writeError(w http.ResponseWriter, path string, statusCode int, err error) { + msg := err.Error() + + if statusCode >= http.StatusInternalServerError { + h.log.WithError(err).WithField("path", path).Error("Request failed") + + msg = http.StatusText(statusCode) + } + + if writeErr := WriteErrorResponse(w, msg, statusCode); writeErr != nil { + h.log.WithError(writeErr).Error("Failed to write error response") + } +} + func (h *Handler) handleEthV1BeaconGenesis(ctx context.Context, r *http.Request, p httprouter.Params, contentType ContentType) (*HTTPResponse, error) { if err := ValidateContentType(contentType, []ContentType{ContentTypeJSON}); err != nil { return NewUnsupportedMediaTypeResponse(nil), err From 9f34b1c3a6d9bb265ccf8ccb603137bcac5b9fce Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Fri, 12 Jun 2026 12:53:55 +1000 Subject: [PATCH 2/2] ci(lint): only flag issues introduced by the PR --- .github/workflows/golangci-lint.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/golangci-lint.yaml b/.github/workflows/golangci-lint.yaml index ce7054a..85af044 100644 --- a/.github/workflows/golangci-lint.yaml +++ b/.github/workflows/golangci-lint.yaml @@ -9,7 +9,7 @@ on: permissions: contents: read # Optional: allow read access to pull request. Use with `only-new-issues` option. - # pull-requests: read + pull-requests: read jobs: golangci: name: lint @@ -32,7 +32,7 @@ jobs: # args: --issues-exit-code=0 # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true + only-new-issues: true # Optional: if set to true then the all caching functionality will be complete disabled, # takes precedence over all other caching options.