Skip to content

Commit 16f5e63

Browse files
joeybloggsjoeybloggs
authored andcommitted
Add ability to set maximum array size to avoid potential man-in-the-middle or DOS attacks
for issue-#2
1 parent 4effd86 commit 16f5e63

File tree

4 files changed

+81
-31
lines changed

4 files changed

+81
-31
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package form
22
============
33
<img align="right" src="https://raw.githubusercontent.com/go-playground/form/master/logo.jpg">
4-
![Project status](https://img.shields.io/badge/version-1.4.0-green.svg)
4+
![Project status](https://img.shields.io/badge/version-1.5.0-green.svg)
55
[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/form/branches/master/badge.svg)](https://semaphoreci.com/joeybloggs/form)
66
[![Coverage Status](https://coveralls.io/repos/github/go-playground/form/badge.svg?branch=master)](https://coveralls.io/github/go-playground/form?branch=master)
77
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/form)](https://goreportcard.com/report/github.com/go-playground/form)

decoder.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import (
1010
"time"
1111
)
1212

13+
const (
14+
errArraySize = "Array size of '%d' is larger than the maximum currently set on the decoder of '%d'. To increase this limit please see, SetMaxArraySize(size uint)"
15+
)
16+
1317
type decoder struct {
1418
d *Decoder
1519
errs DecodeErrors
@@ -293,22 +297,42 @@ func (d *decoder) setFieldByType(current reflect.Value, namespace string, idx in
293297

294298
d.parseMapData()
295299

296-
// maybe it's an numbered array i.e. Pnone[0].Number
300+
// maybe it's an numbered array i.e. Phone[0].Number
297301
if rd := d.dm[namespace]; rd != nil {
298302

299303
var varr reflect.Value
300304

301305
sl := rd.sliceLen + 1
302306

307+
// checking below for maxArraySize, but if array exists and already
308+
// has sufficient capacity allocated then we do not check as the code
309+
// obviously allows that capacity.
310+
303311
if v.IsNil() {
312+
313+
if sl > d.d.maxArraySize {
314+
d.setError(namespace, fmt.Errorf(errArraySize, sl, d.d.maxArraySize))
315+
return
316+
}
317+
304318
varr = reflect.MakeSlice(v.Type(), sl, sl)
319+
305320
} else if v.Len() < sl {
321+
306322
if v.Cap() <= sl {
323+
324+
if sl > d.d.maxArraySize {
325+
d.setError(namespace, fmt.Errorf(errArraySize, sl, d.d.maxArraySize))
326+
return
327+
}
328+
307329
varr = reflect.MakeSlice(v.Type(), sl, sl)
308330
} else {
309331
varr = reflect.MakeSlice(v.Type(), sl, v.Cap())
310332
}
333+
311334
reflect.Copy(varr, v)
335+
312336
} else {
313337
varr = v
314338
}

decoder_test.go

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -651,40 +651,47 @@ func TestDecoderNativeTime(t *testing.T) {
651651
func TestDecoderErrors(t *testing.T) {
652652

653653
type TestError struct {
654-
Bool bool `form:"bool"`
655-
Int int
656-
Uint uint
657-
Float32 float32
658-
String string
659-
Time time.Time
660-
MapBadIntKey map[int]int
661-
MapBadUintKey map[uint]uint
662-
MapBadFloatKey map[float32]float32
663-
MapBadBoolKey map[bool]bool
664-
MapBadKeyType map[complex64]int
665-
BadArrayValue []int
666-
BadMapKey map[time.Time]string
654+
Bool bool `form:"bool"`
655+
Int int
656+
Uint uint
657+
Float32 float32
658+
String string
659+
Time time.Time
660+
MapBadIntKey map[int]int
661+
MapBadUintKey map[uint]uint
662+
MapBadFloatKey map[float32]float32
663+
MapBadBoolKey map[bool]bool
664+
MapBadKeyType map[complex64]int
665+
BadArrayValue []int
666+
BadMapKey map[time.Time]string
667+
OverflowNilArray []int
668+
OverFlowExistingArray []int
667669
}
668670

669671
values := url.Values{
670-
"bool": []string{"uh-huh"},
671-
"Int": []string{"bad"},
672-
"Uint": []string{"bad"},
673-
"Float32": []string{"bad"},
674-
"String": []string{"str bad return val"},
675-
"Time": []string{"bad"},
676-
"MapBadIntKey[key]": []string{"1"},
677-
"MapBadUintKey[key]": []string{"1"},
678-
"MapBadFloatKey[key]": []string{"1.1"},
679-
"MapBadBoolKey[uh-huh]": []string{"true"},
680-
"MapBadKeyType[1.4]": []string{"5"},
681-
"BadArrayValue[0]": []string{"badintval"},
682-
"BadMapKey[badtime]": []string{"badtime"},
672+
"bool": []string{"uh-huh"},
673+
"Int": []string{"bad"},
674+
"Uint": []string{"bad"},
675+
"Float32": []string{"bad"},
676+
"String": []string{"str bad return val"},
677+
"Time": []string{"bad"},
678+
"MapBadIntKey[key]": []string{"1"},
679+
"MapBadUintKey[key]": []string{"1"},
680+
"MapBadFloatKey[key]": []string{"1.1"},
681+
"MapBadBoolKey[uh-huh]": []string{"true"},
682+
"MapBadKeyType[1.4]": []string{"5"},
683+
"BadArrayValue[0]": []string{"badintval"},
684+
"BadMapKey[badtime]": []string{"badtime"},
685+
"OverflowNilArray[999]": []string{"idx 1000"},
686+
"OverFlowExistingArray[999]": []string{"idx 1000"},
683687
}
684688

685-
var test TestError
689+
test := TestError{
690+
OverFlowExistingArray: make([]int, 2, 2),
691+
}
686692

687693
decoder := NewDecoder()
694+
decoder.SetMaxArraySize(4)
688695
decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) {
689696
return nil, errors.New("Bad Type Conversion")
690697
}, "")
@@ -696,6 +703,8 @@ func TestDecoderErrors(t *testing.T) {
696703
NotEqual(t, e, "")
697704

698705
err := errs.(DecodeErrors)
706+
Equal(t, len(err), 15)
707+
699708
k := err["bool"]
700709
Equal(t, k.Error(), "Invalid Boolean Value 'uh-huh' Type 'bool' Namespace 'bool'")
701710

@@ -732,6 +741,12 @@ func TestDecoderErrors(t *testing.T) {
732741
k = err["BadArrayValue[0]"]
733742
Equal(t, k.Error(), "Invalid Integer Value 'badintval' Type 'int' Namespace 'BadArrayValue[0]'")
734743

744+
k = err["OverflowNilArray"]
745+
Equal(t, k.Error(), "Array size of '1000' is larger than the maximum currently set on the decoder of '4'. To increase this limit please see, SetMaxArraySize(size uint)")
746+
747+
k = err["OverFlowExistingArray"]
748+
Equal(t, k.Error(), "Array size of '1000' is larger than the maximum currently set on the decoder of '4'. To increase this limit please see, SetMaxArraySize(size uint)")
749+
735750
type TestError2 struct {
736751
BadMapKey map[time.Time]string
737752
}

form_decoder.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,15 @@ type Decoder struct {
5050
tagName string
5151
structCache structCacheMap
5252
customTypeFuncs map[reflect.Type]DecodeCustomTypeFunc
53+
maxArraySize int
5354
}
5455

5556
// NewDecoder creates a new decoder instance with sane defaults
5657
func NewDecoder() *Decoder {
5758
return &Decoder{
58-
tagName: "form",
59-
structCache: structCacheMap{m: map[reflect.Type]cachedStruct{}},
59+
tagName: "form",
60+
structCache: structCacheMap{m: map[reflect.Type]cachedStruct{}},
61+
maxArraySize: 10000,
6062
}
6163
}
6264

@@ -66,6 +68,15 @@ func (d *Decoder) SetTagName(tagName string) {
6668
d.tagName = tagName
6769
}
6870

71+
// SetMaxArraySize sets maximum array size that can be created.
72+
// This limit is for the array indexing this library supports to
73+
// avoid potential DOS or man-in-the-middle attacks using an unusually
74+
// high number.
75+
// DEFAULT: 10000
76+
func (d *Decoder) SetMaxArraySize(size uint) {
77+
d.maxArraySize = int(size)
78+
}
79+
6980
// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
7081
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any parsing
7182
func (d *Decoder) RegisterCustomTypeFunc(fn DecodeCustomTypeFunc, types ...interface{}) {

0 commit comments

Comments
 (0)