Skip to content

BindHeaders panics on nil or empty header value slices #3169

Description

@taljeon

Issue Description

BindHeaders panics when an in-process request header contains a key whose value is a nil or zero-length []string. This is different from []string{""}, which binds without a panic.

On current master, BindHeaders passes the header map to bindData. The scalar-map paths read v[0], and the tagged struct path reads inputValue[0], without checking the slice length. With Header["X-Empty"] = nil or []string{}, these paths panic with index out of range [0] with length 0 instead of returning normally/an error.

This reproduction constructs the request in-process. I have not demonstrated a way for a raw incoming HTTP header to produce this map state, and I am not claiming a remotely exploitable denial of service.

Working code to debug

package echo_test

import (
    "net/http"
    "net/http/httptest"
    "testing"

    "github.com/labstack/echo/v5"
)

func TestEmptyHeaderValues(t *testing.T) {
    for _, values := range [][]string{nil, {}} {
        func() {
            defer func() {
                if p := recover(); p != nil {
                    t.Errorf("BindHeaders panicked: %v", p)
                }
            }()
            e := echo.New()
            req := httptest.NewRequest(http.MethodGet, "/", nil)
            req.Header["X-Empty"] = values
            c := e.NewContext(req, httptest.NewRecorder())
            var dst struct { Value string `header:"X-Empty"` }
            if err := echo.BindHeaders(c, &dst); err != nil {
                t.Errorf("BindHeaders: %v", err)
            }
        }()
    }
}

I also reproduced the panic for ordinary *map[string]string and *map[string]any targets. A private table test against unchanged master produced 6 failing panic cases (nil/empty slices × three targets) and 6 passing no-panic controls (normal value/one empty string × three targets). Package go vet passed; no full-suite, race, other Go-version or OS run is claimed.

Proposed narrow follow-up

Would skipping entries with no values be the preferred treatment, or should binding return a specific error? I can prepare a narrow bind.go/regression follow-up after confirming this policy and coordination with #3150, which changes adjacent map assignment code but still indexes v[0]. This would not alter named-map conversions, first-value versus all-values behavior, []string{""} semantics, or existing custom multi-value unmarshalling without agreement. #2778's empty-string-to-nil-pointer proposal is a different input case.

Version/commit

  • master 3882266a3641a36fc2111b48cd597adab1c1ecea, module github.com/labstack/echo/v5
  • Go 1.27.1, Linux/arm64; synthetic requests, no running application/provider
  • OpenAI Codex assisted with source tracing and the reproduction. No production fix/PR has been submitted.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions