Skip to content

Commit 3077c08

Browse files
authored
Specify UseNumber() in the JSON decoder during JSON validation (#738)
1 parent 1e5c86e commit 3077c08

4 files changed

Lines changed: 126 additions & 2 deletions

File tree

openapi3/schema.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,19 @@ func (schema *Schema) visitJSON(settings *schemaValidationSettings, value interf
10771077
switch value := value.(type) {
10781078
case bool:
10791079
return schema.visitJSONBoolean(settings, value)
1080+
case json.Number:
1081+
valueFloat64, err := value.Float64()
1082+
if err != nil {
1083+
return &SchemaError{
1084+
Value: value,
1085+
Schema: schema,
1086+
SchemaField: "type",
1087+
Reason: "cannot convert json.Number to float64",
1088+
customizeMessageError: settings.customizeMessageError,
1089+
Origin: err,
1090+
}
1091+
}
1092+
return schema.visitJSONNumber(settings, valueFloat64)
10801093
case int:
10811094
return schema.visitJSONNumber(settings, float64(value))
10821095
case int32:

openapi3filter/issue733_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package openapi3filter_test
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"math"
8+
"math/big"
9+
"net/http"
10+
"testing"
11+
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
14+
15+
"github.com/getkin/kin-openapi/openapi3"
16+
"github.com/getkin/kin-openapi/openapi3filter"
17+
"github.com/getkin/kin-openapi/routers/gorillamux"
18+
)
19+
20+
func TestIntMax(t *testing.T) {
21+
spec := `
22+
openapi: 3.0.0
23+
info:
24+
version: 1.0.0
25+
title: test large integer value
26+
paths:
27+
/test:
28+
post:
29+
requestBody:
30+
content:
31+
application/json:
32+
schema:
33+
type: object
34+
properties:
35+
testInteger:
36+
type: integer
37+
format: int64
38+
testDefault:
39+
type: boolean
40+
default: false
41+
responses:
42+
'200':
43+
description: Successful response
44+
`[1:]
45+
46+
loader := openapi3.NewLoader()
47+
48+
doc, err := loader.LoadFromData([]byte(spec))
49+
require.NoError(t, err)
50+
51+
err = doc.Validate(loader.Context)
52+
require.NoError(t, err)
53+
54+
router, err := gorillamux.NewRouter(doc)
55+
require.NoError(t, err)
56+
57+
testOne := func(value *big.Int, pass bool) {
58+
valueString := value.String()
59+
60+
req, err := http.NewRequest(http.MethodPost, "/test", bytes.NewReader([]byte(`{"testInteger":`+valueString+`}`)))
61+
require.NoError(t, err)
62+
req.Header.Set("Content-Type", "application/json")
63+
64+
route, pathParams, err := router.FindRoute(req)
65+
require.NoError(t, err)
66+
67+
err = openapi3filter.ValidateRequest(
68+
context.Background(),
69+
&openapi3filter.RequestValidationInput{
70+
Request: req,
71+
PathParams: pathParams,
72+
Route: route,
73+
})
74+
if pass {
75+
require.NoError(t, err)
76+
77+
dec := json.NewDecoder(req.Body)
78+
dec.UseNumber()
79+
var jsonAfter map[string]interface{}
80+
err = dec.Decode(&jsonAfter)
81+
require.NoError(t, err)
82+
83+
valueAfter := jsonAfter["testInteger"]
84+
require.IsType(t, json.Number(""), valueAfter)
85+
assert.Equal(t, valueString, string(valueAfter.(json.Number)))
86+
} else {
87+
if assert.Error(t, err) {
88+
var serr *openapi3.SchemaError
89+
if assert.ErrorAs(t, err, &serr) {
90+
assert.Equal(t, "number must be an int64", serr.Reason)
91+
}
92+
}
93+
}
94+
}
95+
96+
bigMaxInt64 := big.NewInt(math.MaxInt64)
97+
bigMaxInt64Plus1 := new(big.Int).Add(bigMaxInt64, big.NewInt(1))
98+
bigMinInt64 := big.NewInt(math.MinInt64)
99+
bigMinInt64Minus1 := new(big.Int).Sub(bigMinInt64, big.NewInt(1))
100+
101+
testOne(bigMaxInt64, true)
102+
// XXX not yet fixed
103+
// testOne(bigMaxInt64Plus1, false)
104+
testOne(bigMaxInt64Plus1, true)
105+
testOne(bigMinInt64, true)
106+
// XXX not yet fixed
107+
// testOne(bigMinInt64Minus1, false)
108+
testOne(bigMinInt64Minus1, true)
109+
}

openapi3filter/req_resp_decoder.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1030,7 +1030,9 @@ func plainBodyDecoder(body io.Reader, header http.Header, schema *openapi3.Schem
10301030

10311031
func jsonBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (interface{}, error) {
10321032
var value interface{}
1033-
if err := json.NewDecoder(body).Decode(&value); err != nil {
1033+
dec := json.NewDecoder(body)
1034+
dec.UseNumber()
1035+
if err := dec.Decode(&value); err != nil {
10341036
return nil, &ParseError{Kind: KindInvalidFormat, Cause: err}
10351037
}
10361038
return value, nil

openapi3filter/req_resp_decoder_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1226,7 +1226,7 @@ func TestDecodeBody(t *testing.T) {
12261226
WithProperty("d", openapi3.NewObjectSchema().WithProperty("d1", openapi3.NewStringSchema())).
12271227
WithProperty("f", openapi3.NewStringSchema().WithFormat("binary")).
12281228
WithProperty("g", openapi3.NewStringSchema()),
1229-
want: map[string]interface{}{"a": "a1", "b": float64(10), "c": []interface{}{"c1", "c2"}, "d": map[string]interface{}{"d1": "d1"}, "f": "foo", "g": "g1"},
1229+
want: map[string]interface{}{"a": "a1", "b": json.Number("10"), "c": []interface{}{"c1", "c2"}, "d": map[string]interface{}{"d1": "d1"}, "f": "foo", "g": "g1"},
12301230
},
12311231
{
12321232
name: "multipartExtraPart",

0 commit comments

Comments
 (0)