diff --git a/huma.go b/huma.go index a80bf2c2..d471ccd1 100644 --- a/huma.go +++ b/huma.go @@ -1361,7 +1361,12 @@ func setDeepObjectValue(pb *PathBuffer, res *ValidateResult, f reflect.Value, da } } else { if val := field.Tag.Get("default"); val != "" { - setFieldValue(fv, val) + if err := setFieldValue(fv, val); err != nil { + pb.Push(fieldName) + res.Add(pb, val, err.Error()) + pb.Pop() + continue + } result[fieldName] = fv.Interface() } } @@ -1377,19 +1382,19 @@ func setFieldValue(f reflect.Value, value string) error { case reflect.Interface: f.Set(reflect.ValueOf(value)) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - v, err := strconv.ParseInt(value, 10, 64) + v, err := strconv.ParseInt(value, 10, f.Type().Bits()) if err != nil { return errors.New("invalid integer") } f.SetInt(v) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - v, err := strconv.ParseUint(value, 10, 64) + v, err := strconv.ParseUint(value, 10, f.Type().Bits()) if err != nil { return errors.New("invalid integer") } f.SetUint(v) case reflect.Float32, reflect.Float64: - v, err := strconv.ParseFloat(value, 64) + v, err := strconv.ParseFloat(value, f.Type().Bits()) if err != nil { return errors.New("invalid float") } @@ -1824,7 +1829,7 @@ func parseInto(ctx Context, f reflect.Value, value string, preSplit []string, p f.SetString(value) return value, nil case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - v, err := strconv.ParseInt(value, 10, 64) + v, err := strconv.ParseInt(value, 10, p.Type.Bits()) if err != nil { return nil, errors.New("invalid integer") } @@ -1833,7 +1838,7 @@ func parseInto(ctx Context, f reflect.Value, value string, preSplit []string, p return v, nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - v, err := strconv.ParseUint(value, 10, 64) + v, err := strconv.ParseUint(value, 10, p.Type.Bits()) if err != nil { return nil, errors.New("invalid integer") } @@ -1842,7 +1847,7 @@ func parseInto(ctx Context, f reflect.Value, value string, preSplit []string, p return v, nil case reflect.Float32, reflect.Float64: - v, err := strconv.ParseFloat(value, 64) + v, err := strconv.ParseFloat(value, p.Type.Bits()) if err != nil { return nil, errors.New("invalid float") } diff --git a/huma_test.go b/huma_test.go index 5d35f4c1..7d960ea2 100644 --- a/huma_test.go +++ b/huma_test.go @@ -870,6 +870,28 @@ func TestFeatures(t *testing.T) { Method: http.MethodGet, URL: "/test?test[int]=1&test[uint]=12&test[float]=123.0&test[bool]=true&test[string]=foo&test[any]=foo&test2[foo]=a", }, + { + Name: "param-deepObject-default-overflow", + Register: func(t *testing.T, api huma.API) { + huma.Register(api, huma.Operation{ + Method: http.MethodGet, + Path: "/test", + }, func(ctx context.Context, i *struct { + Test struct { + Name string `json:"name"` + Value int8 `json:"value" default:"128"` + } `query:"test,deepObject"` + }) (*struct{}, error) { + return nil, nil + }) + }, + Method: http.MethodGet, + URL: "/test?test[name]=foo", + Assert: func(t *testing.T, resp *httptest.ResponseRecorder) { + assert.Equal(t, http.StatusUnprocessableEntity, resp.Code) + assert.Contains(t, resp.Body.String(), "invalid integer") + }, + }, { Name: "param-deepObject-map-required", Register: func(t *testing.T, api huma.API) { @@ -2919,10 +2941,10 @@ Content-Type: text/plain }, Method: http.MethodGet, URL: "/transform", -Assert: func(t *testing.T, resp *httptest.ResponseRecorder) { - assert.Equal(t, http.StatusOK, resp.Code) - assert.JSONEq(t, `null`, resp.Body.String()) -}, + Assert: func(t *testing.T, resp *httptest.ResponseRecorder) { + assert.Equal(t, http.StatusOK, resp.Code) + assert.JSONEq(t, `null`, resp.Body.String()) + }, }, { Name: "schema-url-from-x-forwarded-host", @@ -4975,3 +4997,37 @@ func TestWriteResponseTransformErrorStatus(t *testing.T) { assert.Equal(t, http.StatusInternalServerError, res.StatusCode) assert.Contains(t, string(body), "error transforming response") } + +func TestInputOverflowIsRejected(t *testing.T) { + mux, api := humatest.New(t, huma.DefaultConfig("Test API", "1.0.0")) + + var handlerCalled bool + var gotVal int8 + + huma.Register(api, huma.Operation{ + Method: http.MethodGet, + Path: "/number", + }, func(_ context.Context, input *struct { + Value int8 `query:"value"` + }) (*struct{}, error) { + handlerCalled = true + gotVal = input.Value + return &struct{}{}, nil + }) + + req := httptest.NewRequest( + http.MethodGet, + "/number?value=128", + nil, + ) + res := httptest.NewRecorder() + mux.ServeHTTP(res, req) + + if handlerCalled { + t.Fatalf("handler was called with value=%d, 128 is not representable as int8", gotVal) + } + + if got := res.Code; got < 400 || got >= 500 { + t.Fatalf("expected a 4xx response status, got %d", got) + } +}