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
|
||||
}
|
||||
|
||||
const stdContractID = -2
|
||||
const (
|
||||
stdContractID = -2
|
||||
|
||||
// stdMaxInputLength is the maximum input length for string-related methods.
|
||||
stdMaxInputLength = 1024
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidBase is returned when base is invalid.
|
||||
ErrInvalidBase = errors.New("invalid base")
|
||||
// ErrInvalidFormat is returned when string is not a number.
|
||||
ErrInvalidFormat = errors.New("invalid format")
|
||||
// ErrTooBigInput is returned when input exceeds size limit.
|
||||
ErrTooBigInput = errors.New("input is too big")
|
||||
)
|
||||
|
||||
func newStd() *Std {
|
||||
|
@ -69,32 +76,32 @@ func newStd() *Std {
|
|||
desc = newDescriptor("atoi", smartcontract.IntegerType,
|
||||
manifest.NewParameter("value", smartcontract.StringType),
|
||||
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)
|
||||
|
||||
desc = newDescriptor("atoi", smartcontract.IntegerType,
|
||||
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)
|
||||
|
||||
desc = newDescriptor("base64Encode", smartcontract.StringType,
|
||||
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)
|
||||
|
||||
desc = newDescriptor("base64Decode", smartcontract.ByteArrayType,
|
||||
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)
|
||||
|
||||
desc = newDescriptor("base58Encode", smartcontract.StringType,
|
||||
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)
|
||||
|
||||
desc = newDescriptor("base58Decode", smartcontract.ByteArrayType,
|
||||
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)
|
||||
|
||||
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 {
|
||||
num := toString(args[0])
|
||||
num := s.toLimitedString(args[0])
|
||||
res := s.atoi10Aux(num)
|
||||
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 {
|
||||
num := toString(args[0])
|
||||
num := s.toLimitedString(args[0])
|
||||
base := toBigInt(args[1])
|
||||
if !base.IsInt64() {
|
||||
panic(ErrInvalidBase)
|
||||
|
@ -238,17 +245,14 @@ func reverse(b []byte) {
|
|||
}
|
||||
|
||||
func (s *Std) base64Encode(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||
src, err := args[0].TryBytes()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
src := s.toLimitedBytes(args[0])
|
||||
result := base64.StdEncoding.EncodeToString(src)
|
||||
|
||||
return stackitem.NewByteArray([]byte(result))
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
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 {
|
||||
src, err := args[0].TryBytes()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
src := s.toLimitedBytes(args[0])
|
||||
result := base58.Encode(src)
|
||||
|
||||
return stackitem.NewByteArray([]byte(result))
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -296,3 +297,22 @@ func (s *Std) OnPersist(ic *interop.Context) error {
|
|||
func (s *Std) PostPersist(ic *interop.Context) error {
|
||||
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"
|
||||
"math"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mr-tron/base58"
|
||||
|
@ -102,6 +103,7 @@ func TestStdLibItoaAtoi(t *testing.T) {
|
|||
{"1_000", big.NewInt(10), ErrInvalidFormat},
|
||||
{"FE", big.NewInt(10), ErrInvalidFormat},
|
||||
{"XD", big.NewInt(16), ErrInvalidFormat},
|
||||
{strings.Repeat("0", stdMaxInputLength+1), big.NewInt(10), ErrTooBigInput},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
@ -164,18 +166,28 @@ func TestStdLibEncodeDecode(t *testing.T) {
|
|||
ic := &interop.Context{VM: vm.New()}
|
||||
var actual stackitem.Item
|
||||
|
||||
bigInputArgs := []stackitem.Item{stackitem.Make(strings.Repeat("6", stdMaxInputLength+1))}
|
||||
|
||||
t.Run("Encode64", func(t *testing.T) {
|
||||
require.NotPanics(t, func() {
|
||||
actual = s.base64Encode(ic, []stackitem.Item{stackitem.Make(original)})
|
||||
})
|
||||
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) {
|
||||
require.NotPanics(t, func() {
|
||||
actual = s.base58Encode(ic, []stackitem.Item{stackitem.Make(original)})
|
||||
})
|
||||
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) {
|
||||
require.NotPanics(t, func() {
|
||||
actual = s.base64Decode(ic, []stackitem.Item{stackitem.Make(encoded64)})
|
||||
|
@ -189,6 +201,8 @@ func TestStdLibEncodeDecode(t *testing.T) {
|
|||
require.Panics(t, func() {
|
||||
_ = 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) {
|
||||
require.NotPanics(t, func() {
|
||||
|
@ -203,6 +217,8 @@ func TestStdLibEncodeDecode(t *testing.T) {
|
|||
require.Panics(t, func() {
|
||||
_ = s.base58Decode(ic, []stackitem.Item{stackitem.NewInterop(nil)})
|
||||
})
|
||||
require.PanicsWithError(t, ErrTooBigInput.Error(),
|
||||
func() { s.base58Decode(ic, bigInputArgs) })
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue