Merge pull request #2105 from nspcc-dev/json-restrict
native/std: restrint amount of items in JSON deserialization
This commit is contained in:
commit
82f481e143
3 changed files with 38 additions and 5 deletions
|
@ -204,7 +204,7 @@ func (s *Std) jsonDeserialize(_ *interop.Context, args []stackitem.Item) stackit
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
item, err := stackitem.FromJSON(data)
|
item, err := stackitem.FromJSON(data, stackitem.MaxDeserialized)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
type decoder struct {
|
type decoder struct {
|
||||||
json.Decoder
|
json.Decoder
|
||||||
|
|
||||||
|
count int
|
||||||
depth int
|
depth int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,8 +158,11 @@ func itemToJSONString(it Item) ([]byte, error) {
|
||||||
// null -> Null
|
// null -> Null
|
||||||
// array -> Array
|
// array -> Array
|
||||||
// map -> Map, keys are UTF-8
|
// map -> Map, keys are UTF-8
|
||||||
func FromJSON(data []byte) (Item, error) {
|
func FromJSON(data []byte, maxCount int) (Item, error) {
|
||||||
d := decoder{Decoder: *json.NewDecoder(bytes.NewReader(data))}
|
d := decoder{
|
||||||
|
Decoder: *json.NewDecoder(bytes.NewReader(data)),
|
||||||
|
count: maxCount,
|
||||||
|
}
|
||||||
if item, err := d.decode(); err != nil {
|
if item, err := d.decode(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if _, err := d.Token(); err != gio.EOF {
|
} else if _, err := d.Token(); err != gio.EOF {
|
||||||
|
@ -173,6 +177,12 @@ func (d *decoder) decode() (Item, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d.count--
|
||||||
|
if d.count < 0 && tok != json.Delim('}') && tok != json.Delim(']') {
|
||||||
|
return nil, errTooBigElements
|
||||||
|
}
|
||||||
|
|
||||||
switch t := tok.(type) {
|
switch t := tok.(type) {
|
||||||
case json.Delim:
|
case json.Delim:
|
||||||
switch t {
|
switch t {
|
||||||
|
@ -190,6 +200,7 @@ func (d *decoder) decode() (Item, error) {
|
||||||
d.depth--
|
d.depth--
|
||||||
return item, err
|
return item, err
|
||||||
default:
|
default:
|
||||||
|
d.count++
|
||||||
// no error above means corresponding closing token
|
// no error above means corresponding closing token
|
||||||
// was encountered for map or array respectively
|
// was encountered for map or array respectively
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -234,6 +245,11 @@ func (d *decoder) decodeMap() (*Map, error) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d.count--
|
||||||
|
if d.count < 0 {
|
||||||
|
return nil, errTooBigElements
|
||||||
|
}
|
||||||
val, err := d.decode()
|
val, err := d.decode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
func getTestDecodeFunc(js string, expected ...interface{}) func(t *testing.T) {
|
func getTestDecodeFunc(js string, expected ...interface{}) func(t *testing.T) {
|
||||||
return func(t *testing.T) {
|
return func(t *testing.T) {
|
||||||
actual, err := FromJSON([]byte(js))
|
actual, err := FromJSON([]byte(js), 20)
|
||||||
if expected[0] == nil {
|
if expected[0] == nil {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -50,6 +50,14 @@ func TestFromToJSON(t *testing.T) {
|
||||||
NewArray([]Item{NewBigInteger(big.NewInt(1)), NewByteArray([]byte("test")), NewBool(true), Null{}})))
|
NewArray([]Item{NewBigInteger(big.NewInt(1)), NewByteArray([]byte("test")), NewBool(true), Null{}})))
|
||||||
t.Run("Nested", getTestDecodeFunc(`[[],[{},null]]`,
|
t.Run("Nested", getTestDecodeFunc(`[[],[{},null]]`,
|
||||||
NewArray([]Item{NewArray([]Item{}), NewArray([]Item{NewMap(), Null{}})})))
|
NewArray([]Item{NewArray([]Item{}), NewArray([]Item{NewMap(), Null{}})})))
|
||||||
|
t.Run("ManyElements", func(t *testing.T) {
|
||||||
|
js := `[1, 2, 3]` // 3 elements + array itself
|
||||||
|
_, err := FromJSON([]byte(js), 4)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = FromJSON([]byte(js), 3)
|
||||||
|
require.True(t, errors.Is(err, errTooBigElements), err)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
t.Run("Map", func(t *testing.T) {
|
t.Run("Map", func(t *testing.T) {
|
||||||
small := NewMap()
|
small := NewMap()
|
||||||
|
@ -64,6 +72,15 @@ func TestFromToJSON(t *testing.T) {
|
||||||
m := NewMap()
|
m := NewMap()
|
||||||
m.Add(NewByteArray([]byte("\t")), NewBool(true))
|
m.Add(NewByteArray([]byte("\t")), NewBool(true))
|
||||||
t.Run("escape keys", getTestDecodeFunc(`{"\t":true}`, m))
|
t.Run("escape keys", getTestDecodeFunc(`{"\t":true}`, m))
|
||||||
|
|
||||||
|
t.Run("ManyElements", func(t *testing.T) {
|
||||||
|
js := `{"a":1,"b":3}` // 4 elements + map itself
|
||||||
|
_, err := FromJSON([]byte(js), 5)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = FromJSON([]byte(js), 4)
|
||||||
|
require.True(t, errors.Is(err, errTooBigElements), err)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
t.Run("Invalid", func(t *testing.T) {
|
t.Run("Invalid", func(t *testing.T) {
|
||||||
t.Run("Empty", getTestDecodeFunc(``, nil))
|
t.Run("Empty", getTestDecodeFunc(``, nil))
|
||||||
|
@ -114,7 +131,7 @@ func testToJSON(t *testing.T, expectedErr error, item Item) {
|
||||||
}
|
}
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
actual, err := FromJSON(data)
|
actual, err := FromJSON(data, 1024)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, item, actual)
|
require.Equal(t, item, actual)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue