Repository: deepmap/oapi-codegen
Pull request #740 (2022-08-31 23:28:56)

BindStringToObject() allows silent overflow of narrower-than-64-bit numeric types
Hey Marcin!  Let me know what you think of this.  There is similar code in `deepobject.go` but I couldn't really work out what that was for, and whether I should apply the same logic there.  Let me know, and I can correct this as needed.


Patch:
diff --git a/pkg/runtime/bindstring.go b/pkg/runtime/bindstring.go
index 19ca08b..f597dab 100644
--- a/pkg/runtime/bindstring.go
+++ b/pkg/runtime/bindstring.go
@@ -63,12 +63,20 @@ func BindStringToObject(src string, dst interface{}) error {
 		var val int64
 		val, err = strconv.ParseInt(src, 10, 64)
 		if err == nil {
-			v.SetInt(val)
+			if v.OverflowInt(val) {
+				err = fmt.Errorf("value '%s' overflows destination of type: %s", src, t.Kind())
+			}
+			if err == nil {
+				v.SetInt(val)
+			}
 		}
 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 		var val uint64
 		val, err = strconv.ParseUint(src, 10, 64)
 		if err == nil {
+			if v.OverflowUint(val) {
+				err = fmt.Errorf("value '%s' overflows destination of type: %s", src, t.Kind())
+			}
 			v.SetUint(val)
 		}
 	case reflect.String:
@@ -78,6 +86,9 @@ func BindStringToObject(src string, dst interface{}) error {
 		var val float64
 		val, err = strconv.ParseFloat(src, 64)
 		if err == nil {
+			if v.OverflowFloat(val) {
+				err = fmt.Errorf("value '%s' overflows destination of type: %s", src, t.Kind())
+			}
 			v.SetFloat(val)
 		}
 	case reflect.Bool:


Test patch:
diff --git a/pkg/runtime/bindstring_test.go b/pkg/runtime/bindstring_test.go
index 42b3cab..f2060de 100644
--- a/pkg/runtime/bindstring_test.go
+++ b/pkg/runtime/bindstring_test.go
@@ -14,6 +14,8 @@
 package runtime
 
 import (
+	"fmt"
+	"math"
 	"testing"
 	"time"
 
@@ -37,39 +39,56 @@ func TestBindStringToObject(t *testing.T) {
 	var i8 int8
 	assert.NoError(t, BindStringToObject("12", &i8))
 	assert.Equal(t, int8(12), i8)
+	assert.NoError(t, BindStringToObject("-12", &i8))
+	assert.Equal(t, int8(-12), i8)
 
 	assert.Error(t, BindStringToObject("5.7", &i8))
 	assert.Error(t, BindStringToObject("foo", &i8))
 	assert.Error(t, BindStringToObject("1,2,3", &i8))
+	assert.Error(t, BindStringToObject(fmt.Sprintf("%d", math.MinInt8-1), &i8))
+	assert.Error(t, BindStringToObject(fmt.Sprintf("%d", math.MaxInt8+1), &i8))
 
 	var i16 int16
 	assert.NoError(t, BindStringToObject("12", &i16))
 	assert.Equal(t, int16(12), i16)
+	assert.NoError(t, BindStringToObject("-12", &i16))
+	assert.Equal(t, int16(-12), i16)
 
 	assert.Error(t, BindStringToObject("5.7", &i16))
 	assert.Error(t, BindStringToObject("foo", &i16))
 	assert.Error(t, BindStringToObject("1,2,3", &i16))
+	assert.Error(t, BindStringToObject(fmt.Sprintf("%d", math.MinInt16-1), &i16))
+	assert.Error(t, BindStringToObject(fmt.Sprintf("%d", math.MaxInt16+1), &i16))
 
 	var i32 int32
 	assert.NoError(t, BindStringToObject("12", &i32))
 	assert.Equal(t, int32(12), i32)
+	assert.NoError(t, BindStringToObject("-12", &i32))
+	assert.Equal(t, int32(-12), i32)
 
 	assert.Error(t, BindStringToObject("5.7", &i32))
 	assert.Error(t, BindStringToObject("foo", &i32))
 	assert.Error(t, BindStringToObject("1,2,3", &i32))
+	assert.Error(t, BindStringToObject(fmt.Sprintf("%d", math.MinInt32-1), &i32))
+	assert.Error(t, BindStringToObject(fmt.Sprintf("%d", math.MaxInt32+1), &i32))
 
 	var i64 int64
 	assert.NoError(t, BindStringToObject("124", &i64))
 	assert.Equal(t, int64(124), i64)
+	assert.NoError(t, BindStringToObject("-124", &i64))
+	assert.Equal(t, int64(-124), i64)
 
 	assert.Error(t, BindStringToObject("5.7", &i64))
 	assert.Error(t, BindStringToObject("foo", &i64))
 	assert.Error(t, BindStringToObject("1,2,3", &i64))
+	assert.Error(t, BindStringToObject("-9223372036854775809", &i64))
+	assert.Error(t, BindStringToObject("9223372036854775808", &i64)) // 1<<63
 
 	var u uint
 	assert.NoError(t, BindStringToObject("5", &u))
 	assert.Equal(t, uint(5), u)
 
+	assert.Error(t, BindStringToObject("-5", &u))
 	assert.Error(t, BindStringToObject("5.7", &u))
 	assert.Error(t, BindStringToObject("foo", &u))
 	assert.Error(t, BindStringToObject("1,2,3", &u))
@@ -78,33 +97,41 @@ func TestBindStringToObject(t *testing.T) {
 	assert.NoError(t, BindStringToObject("12", &u8))
 	assert.Equal(t, uint8(12), u8)
 
+	assert.Error(t, BindStringToObject("-5", &u8))
 	assert.Error(t, BindStringToObject("5.7", &u8))
 	assert.Error(t, BindStringToObject("foo", &u8))
 	assert.Error(t, BindStringToObject("1,2,3", &u8))
+	assert.Error(t, BindStringToObject(fmt.Sprintf("%d", math.MaxUint8+1), &i8))
 
 	var u16 uint16
 	assert.NoError(t, BindStringToObject("12", &u16))
 	assert.Equal(t, uint16(12), u16)
 
+	assert.Error(t, BindStringToObject("-5", &u16))
 	assert.Error(t, BindStringToObject("5.7", &u16))
 	assert.Error(t, BindStringToObject("foo", &u16))
 	assert.Error(t, BindStringToObject("1,2,3", &u16))
+	assert.Error(t, BindStringToObject(fmt.Sprintf("%d", math.MaxUint16+1), &i16))
 
 	var u32 uint32
 	assert.NoError(t, BindStringToObject("12", &u32))
 	assert.Equal(t, uint32(12), u32)
 
+	assert.Error(t, BindStringToObject("-5", &u32))
 	assert.Error(t, BindStringToObject("5.7", &u32))
 	assert.Error(t, BindStringToObject("foo", &u32))
 	assert.Error(t, BindStringToObject("1,2,3", &u32))
+	assert.Error(t, BindStringToObject(fmt.Sprintf("%d", math.MaxUint32+1), &i32))
 
 	var u64 uint64
 	assert.NoError(t, BindStringToObject("124", &u64))
 	assert.Equal(t, uint64(124), u64)
 
+	assert.Error(t, BindStringToObject("-5", &u64))
 	assert.Error(t, BindStringToObject("5.7", &u64))
 	assert.Error(t, BindStringToObject("foo", &u64))
 	assert.Error(t, BindStringToObject("1,2,3", &u64))
+	assert.Error(t, BindStringToObject("18446744073709551616", &i64)) // 1<<64
 
 	var b bool
 	assert.NoError(t, BindStringToObject("True", &b))
@@ -113,6 +140,10 @@ func TestBindStringToObject(t *testing.T) {
 	assert.Equal(t, true, b)
 	assert.NoError(t, BindStringToObject("1", &b))
 	assert.Equal(t, true, b)
+	assert.NoError(t, BindStringToObject("0", &b))
+	assert.Equal(t, false, b)
+	assert.Error(t, BindStringToObject("-1", &b))
+	assert.Error(t, BindStringToObject("hello", &b))
 
 	var f64 float64
 	assert.NoError(t, BindStringToObject("1.25", &f64))
@@ -127,6 +158,8 @@ func TestBindStringToObject(t *testing.T) {
 
 	assert.Error(t, BindStringToObject("foo", &f32))
 	assert.Error(t, BindStringToObject("1,2,3", &f32))
+	assert.NoError(t, BindStringToObject(fmt.Sprintf("%f", math.MaxFloat32), &f32))
+	assert.Error(t, BindStringToObject(fmt.Sprintf("%f", math.MaxFloat32*2), &f32))
 
 	// This checks whether binding works through a type alias.
 	type SomeType int