Skip to content

fix(bind): return an error for a destination that is not a pointer - #3228

Open
shounakjoshi88-a11y wants to merge 1 commit into
labstack:masterfrom
shounakjoshi88-a11y:fix/bind-non-pointer-destination
Open

shounakjoshi88-a11y wants to merge 1 commit into
labstack:masterfrom
shounakjoshi88-a11y:fix/bind-non-pointer-destination

Conversation

@shounakjoshi88-a11y

Copy link
Copy Markdown

Binding into a value instead of a pointer panicked:

c := e.NewContext(httptest.NewRequest(http.MethodGet, "/?id=1", nil), httptest.NewRecorder())
c.Bind(user{})   // panic: reflect: Elem of invalid type echo.user

bindData called reflect.TypeOf(destination).Elem() on whatever the caller passed, and Elem panics when the type is not a pointer:

typ := reflect.TypeOf(destination).Elem()
val := reflect.ValueOf(destination).Elem()

The panic escaped the handler, so a request that reached the binder took the connection down with it. The same request without a query string returned nil, because there was nothing to bind.

The JSON binder already reports this mistake as a bad request instead of panicking:

code=400, message=Bad Request, err=json: Unmarshal(non-pointer echo.user)

so the two binders disagreed about a destination that is not a pointer.

The destination is now checked before it is used:

rv := reflect.ValueOf(destination)
if rv.Kind() != reflect.Pointer || rv.IsNil() {
	return errors.New("binding destination must be a non-nil pointer")
}

which BindQueryParams, BindPathValues, BindFormParams and BindHeaders wrap in a bad request, the way the JSON binder does.

The new test in bind_test.go binds into a value and checks that it neither panics nor returns no error:

--- FAIL: TestBindQueryParamsToNonPointer (0.00s)
    Error:      	func (assert.PanicTestFunc)(0x7ff6c10953a0) should not panic
    Panic value:	reflect: Elem of invalid type echo.user
    Error:      	An error is expected but got nil.

and passes with the change:

$ go test ./...
ok  	github.com/labstack/echo/v5	1.578s
ok  	github.com/labstack/echo/v5/echotest	4.539s
ok  	github.com/labstack/echo/v5/middleware	4.247s

bindData called reflect.TypeOf(destination).Elem() on whatever the caller
passed, so a destination that is not a pointer panicked with "reflect:
Elem of invalid type user" as soon as there was something to bind.

The panic escaped the handler, and the JSON binder reports the same
mistake as a bad request instead, so the two binders disagreed about a
destination that is not a pointer.

The destination is now checked before it is used, and a destination that
is not a non-nil pointer is reported as a bad request, the way the JSON
binder reports it.
@shounakjoshi88-a11y

Copy link
Copy Markdown
Author

The CI and Checks workflows on this pull request are in action_required, which needs a maintainer to approve them before they can run on a fork pull request. If you approve them, they should pass - the same commands run green locally:

$ go test ./...
ok  	github.com/labstack/echo/v5	1.578s
ok  	github.com/labstack/echo/v5/echotest	4.539s
ok  	github.com/labstack/echo/v5/middleware	4.247s

$ go test -race ./...
ok  	github.com/labstack/echo/v5	8.306s
ok  	github.com/labstack/echo/v5/echotest	4.626s
ok  	github.com/labstack/echo/v5/middleware	9.187s

gofmt reports nothing on the two changed files.

The new test fails on the unmodified tree, with the panic itself:

--- FAIL: TestBindQueryParamsToNonPointer (0.00s)
    Error:      	func (assert.PanicTestFunc)(0x7ff6c10953a0) should not panic
    Panic value:	reflect: Elem of invalid type echo.user

and passes with the change.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant