mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-26 19:42:23 +00:00
native/std: limit input size for some methods
Also fix prices accordingly.
This commit is contained in:
parent
e4b34833da
commit
82a6c3266c
2 changed files with 55 additions and 19 deletions
|
@ -22,13 +22,20 @@ type Std struct {
|
||||||
interop.ContractMD
|
interop.ContractMD
|
||||||
}
|
}
|
||||||
|
|
||||||
const stdContractID = -2
|
const (
|
||||||
|
stdContractID = -2
|
||||||
|
|
||||||
|
// stdMaxInputLength is the maximum input length for string-related methods.
|
||||||
|
stdMaxInputLength = 1024
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrInvalidBase is returned when base is invalid.
|
// ErrInvalidBase is returned when base is invalid.
|
||||||
ErrInvalidBase = errors.New("invalid base")
|
ErrInvalidBase = errors.New("invalid base")
|
||||||
// ErrInvalidFormat is returned when string is not a number.
|
// ErrInvalidFormat is returned when string is not a number.
|
||||||
ErrInvalidFormat = errors.New("invalid format")
|
ErrInvalidFormat = errors.New("invalid format")
|
||||||
|
// ErrTooBigInput is returned when input exceeds size limit.
|
||||||
|
ErrTooBigInput = errors.New("input is too big")
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStd() *Std {
|
func newStd() *Std {
|
||||||
|
@ -69,32 +76,32 @@ func newStd() *Std {
|
||||||
desc = newDescriptor("atoi", smartcontract.IntegerType,
|
desc = newDescriptor("atoi", smartcontract.IntegerType,
|
||||||
manifest.NewParameter("value", smartcontract.StringType),
|
manifest.NewParameter("value", smartcontract.StringType),
|
||||||
manifest.NewParameter("base", smartcontract.IntegerType))
|
manifest.NewParameter("base", smartcontract.IntegerType))
|
||||||
md = newMethodAndPrice(s.atoi, 1<<12, callflag.NoneFlag)
|
md = newMethodAndPrice(s.atoi, 1<<6, callflag.NoneFlag)
|
||||||
s.AddMethod(md, desc)
|
s.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("atoi", smartcontract.IntegerType,
|
desc = newDescriptor("atoi", smartcontract.IntegerType,
|
||||||
manifest.NewParameter("value", smartcontract.StringType))
|
manifest.NewParameter("value", smartcontract.StringType))
|
||||||
md = newMethodAndPrice(s.atoi10, 1<<12, callflag.NoneFlag)
|
md = newMethodAndPrice(s.atoi10, 1<<6, callflag.NoneFlag)
|
||||||
s.AddMethod(md, desc)
|
s.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("base64Encode", smartcontract.StringType,
|
desc = newDescriptor("base64Encode", smartcontract.StringType,
|
||||||
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
||||||
md = newMethodAndPrice(s.base64Encode, 1<<12, callflag.NoneFlag)
|
md = newMethodAndPrice(s.base64Encode, 1<<5, callflag.NoneFlag)
|
||||||
s.AddMethod(md, desc)
|
s.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("base64Decode", smartcontract.ByteArrayType,
|
desc = newDescriptor("base64Decode", smartcontract.ByteArrayType,
|
||||||
manifest.NewParameter("s", smartcontract.StringType))
|
manifest.NewParameter("s", smartcontract.StringType))
|
||||||
md = newMethodAndPrice(s.base64Decode, 1<<12, callflag.NoneFlag)
|
md = newMethodAndPrice(s.base64Decode, 1<<5, callflag.NoneFlag)
|
||||||
s.AddMethod(md, desc)
|
s.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("base58Encode", smartcontract.StringType,
|
desc = newDescriptor("base58Encode", smartcontract.StringType,
|
||||||
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
||||||
md = newMethodAndPrice(s.base58Encode, 1<<12, callflag.NoneFlag)
|
md = newMethodAndPrice(s.base58Encode, 1<<13, callflag.NoneFlag)
|
||||||
s.AddMethod(md, desc)
|
s.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("base58Decode", smartcontract.ByteArrayType,
|
desc = newDescriptor("base58Decode", smartcontract.ByteArrayType,
|
||||||
manifest.NewParameter("s", smartcontract.StringType))
|
manifest.NewParameter("s", smartcontract.StringType))
|
||||||
md = newMethodAndPrice(s.base58Decode, 1<<12, callflag.NoneFlag)
|
md = newMethodAndPrice(s.base58Decode, 1<<10, callflag.NoneFlag)
|
||||||
s.AddMethod(md, desc)
|
s.AddMethod(md, desc)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
@ -186,7 +193,7 @@ func (s *Std) itoa(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Std) atoi10(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (s *Std) atoi10(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
num := toString(args[0])
|
num := s.toLimitedString(args[0])
|
||||||
res := s.atoi10Aux(num)
|
res := s.atoi10Aux(num)
|
||||||
return stackitem.NewBigInteger(res)
|
return stackitem.NewBigInteger(res)
|
||||||
}
|
}
|
||||||
|
@ -200,7 +207,7 @@ func (s *Std) atoi10Aux(num string) *big.Int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Std) atoi(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (s *Std) atoi(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
num := toString(args[0])
|
num := s.toLimitedString(args[0])
|
||||||
base := toBigInt(args[1])
|
base := toBigInt(args[1])
|
||||||
if !base.IsInt64() {
|
if !base.IsInt64() {
|
||||||
panic(ErrInvalidBase)
|
panic(ErrInvalidBase)
|
||||||
|
@ -238,17 +245,14 @@ func reverse(b []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Std) base64Encode(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (s *Std) base64Encode(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
src, err := args[0].TryBytes()
|
src := s.toLimitedBytes(args[0])
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
result := base64.StdEncoding.EncodeToString(src)
|
result := base64.StdEncoding.EncodeToString(src)
|
||||||
|
|
||||||
return stackitem.NewByteArray([]byte(result))
|
return stackitem.NewByteArray([]byte(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Std) base64Decode(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (s *Std) base64Decode(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
src := toString(args[0])
|
src := s.toLimitedString(args[0])
|
||||||
result, err := base64.StdEncoding.DecodeString(src)
|
result, err := base64.StdEncoding.DecodeString(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -258,17 +262,14 @@ func (s *Std) base64Decode(_ *interop.Context, args []stackitem.Item) stackitem.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Std) base58Encode(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (s *Std) base58Encode(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
src, err := args[0].TryBytes()
|
src := s.toLimitedBytes(args[0])
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
result := base58.Encode(src)
|
result := base58.Encode(src)
|
||||||
|
|
||||||
return stackitem.NewByteArray([]byte(result))
|
return stackitem.NewByteArray([]byte(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Std) base58Decode(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (s *Std) base58Decode(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
src := toString(args[0])
|
src := s.toLimitedString(args[0])
|
||||||
result, err := base58.Decode(src)
|
result, err := base58.Decode(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -296,3 +297,22 @@ func (s *Std) OnPersist(ic *interop.Context) error {
|
||||||
func (s *Std) PostPersist(ic *interop.Context) error {
|
func (s *Std) PostPersist(ic *interop.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Std) toLimitedBytes(item stackitem.Item) []byte {
|
||||||
|
src, err := item.TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if len(src) > stdMaxInputLength {
|
||||||
|
panic(ErrTooBigInput)
|
||||||
|
}
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Std) toLimitedString(item stackitem.Item) string {
|
||||||
|
src := toString(item)
|
||||||
|
if len(src) > stdMaxInputLength {
|
||||||
|
panic(ErrTooBigInput)
|
||||||
|
}
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mr-tron/base58"
|
"github.com/mr-tron/base58"
|
||||||
|
@ -102,6 +103,7 @@ func TestStdLibItoaAtoi(t *testing.T) {
|
||||||
{"1_000", big.NewInt(10), ErrInvalidFormat},
|
{"1_000", big.NewInt(10), ErrInvalidFormat},
|
||||||
{"FE", big.NewInt(10), ErrInvalidFormat},
|
{"FE", big.NewInt(10), ErrInvalidFormat},
|
||||||
{"XD", big.NewInt(16), ErrInvalidFormat},
|
{"XD", big.NewInt(16), ErrInvalidFormat},
|
||||||
|
{strings.Repeat("0", stdMaxInputLength+1), big.NewInt(10), ErrTooBigInput},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
@ -164,18 +166,28 @@ func TestStdLibEncodeDecode(t *testing.T) {
|
||||||
ic := &interop.Context{VM: vm.New()}
|
ic := &interop.Context{VM: vm.New()}
|
||||||
var actual stackitem.Item
|
var actual stackitem.Item
|
||||||
|
|
||||||
|
bigInputArgs := []stackitem.Item{stackitem.Make(strings.Repeat("6", stdMaxInputLength+1))}
|
||||||
|
|
||||||
t.Run("Encode64", func(t *testing.T) {
|
t.Run("Encode64", func(t *testing.T) {
|
||||||
require.NotPanics(t, func() {
|
require.NotPanics(t, func() {
|
||||||
actual = s.base64Encode(ic, []stackitem.Item{stackitem.Make(original)})
|
actual = s.base64Encode(ic, []stackitem.Item{stackitem.Make(original)})
|
||||||
})
|
})
|
||||||
require.Equal(t, stackitem.Make(encoded64), actual)
|
require.Equal(t, stackitem.Make(encoded64), actual)
|
||||||
})
|
})
|
||||||
|
t.Run("Encode64/error", func(t *testing.T) {
|
||||||
|
require.PanicsWithError(t, ErrTooBigInput.Error(),
|
||||||
|
func() { s.base64Encode(ic, bigInputArgs) })
|
||||||
|
})
|
||||||
t.Run("Encode58", func(t *testing.T) {
|
t.Run("Encode58", func(t *testing.T) {
|
||||||
require.NotPanics(t, func() {
|
require.NotPanics(t, func() {
|
||||||
actual = s.base58Encode(ic, []stackitem.Item{stackitem.Make(original)})
|
actual = s.base58Encode(ic, []stackitem.Item{stackitem.Make(original)})
|
||||||
})
|
})
|
||||||
require.Equal(t, stackitem.Make(encoded58), actual)
|
require.Equal(t, stackitem.Make(encoded58), actual)
|
||||||
})
|
})
|
||||||
|
t.Run("Encode58/error", func(t *testing.T) {
|
||||||
|
require.PanicsWithError(t, ErrTooBigInput.Error(),
|
||||||
|
func() { s.base58Encode(ic, bigInputArgs) })
|
||||||
|
})
|
||||||
t.Run("Decode64/positive", func(t *testing.T) {
|
t.Run("Decode64/positive", func(t *testing.T) {
|
||||||
require.NotPanics(t, func() {
|
require.NotPanics(t, func() {
|
||||||
actual = s.base64Decode(ic, []stackitem.Item{stackitem.Make(encoded64)})
|
actual = s.base64Decode(ic, []stackitem.Item{stackitem.Make(encoded64)})
|
||||||
|
@ -189,6 +201,8 @@ func TestStdLibEncodeDecode(t *testing.T) {
|
||||||
require.Panics(t, func() {
|
require.Panics(t, func() {
|
||||||
_ = s.base64Decode(ic, []stackitem.Item{stackitem.NewInterop(nil)})
|
_ = s.base64Decode(ic, []stackitem.Item{stackitem.NewInterop(nil)})
|
||||||
})
|
})
|
||||||
|
require.PanicsWithError(t, ErrTooBigInput.Error(),
|
||||||
|
func() { s.base64Decode(ic, bigInputArgs) })
|
||||||
})
|
})
|
||||||
t.Run("Decode58/positive", func(t *testing.T) {
|
t.Run("Decode58/positive", func(t *testing.T) {
|
||||||
require.NotPanics(t, func() {
|
require.NotPanics(t, func() {
|
||||||
|
@ -203,6 +217,8 @@ func TestStdLibEncodeDecode(t *testing.T) {
|
||||||
require.Panics(t, func() {
|
require.Panics(t, func() {
|
||||||
_ = s.base58Decode(ic, []stackitem.Item{stackitem.NewInterop(nil)})
|
_ = s.base58Decode(ic, []stackitem.Item{stackitem.NewInterop(nil)})
|
||||||
})
|
})
|
||||||
|
require.PanicsWithError(t, ErrTooBigInput.Error(),
|
||||||
|
func() { s.base58Decode(ic, bigInputArgs) })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue