manifest: don't accept manifests with invalid features

Refs. #3522.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
This commit is contained in:
Roman Khimov 2024-07-26 11:43:10 +03:00
parent e861aeec2e
commit 58ab24efdb
6 changed files with 44 additions and 9 deletions

View file

@ -1 +1 @@
{"name":"verify","abi":{"methods":[{"name":"verify","offset":0,"parameters":[],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":5,"parameters":[{"name":"from","type":"ByteArray"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false}],"events":[{"name":"Hello world!","parameters":[{"name":"args","type":"Array"}]}]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null} {"name":"verify","abi":{"methods":[{"name":"verify","offset":0,"parameters":[],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":5,"parameters":[{"name":"from","type":"ByteArray"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false}],"events":[{"name":"Hello world!","parameters":[{"name":"args","type":"Array"}]}]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null,"features":{}}

View file

@ -4,6 +4,7 @@
"supportedstandards" : [], "supportedstandards" : [],
"name" : "verify", "name" : "verify",
"trusts" : [], "trusts" : [],
"features": {},
"permissions" : [ "permissions" : [
{ {
"methods" : "*", "methods" : "*",

View file

@ -713,6 +713,7 @@ func TestSystemRuntimeNotify_HFBasilisk(t *testing.T) {
m := &manifest.Manifest{ m := &manifest.Manifest{
Name: "ctr", Name: "ctr",
Features: json.RawMessage("{}"),
Groups: []manifest.Group{}, Groups: []manifest.Group{},
ABI: manifest.ABI{ ABI: manifest.ABI{
Methods: []manifest.Method{ Methods: []manifest.Method{

View file

@ -53,6 +53,7 @@ func TestManagement_DeployUpdate_HFBasilisk(t *testing.T) {
m := &manifest.Manifest{ m := &manifest.Manifest{
Name: "ctr", Name: "ctr",
Features: json.RawMessage("{}"),
Groups: []manifest.Group{}, Groups: []manifest.Group{},
ABI: manifest.ABI{ ABI: manifest.ABI{
Methods: []manifest.Method{ Methods: []manifest.Method{
@ -89,6 +90,7 @@ func TestManagement_CallInTheSameBlock(t *testing.T) {
m := &manifest.Manifest{ m := &manifest.Manifest{
Name: "ctr", Name: "ctr",
Features: json.RawMessage("{}"),
Groups: []manifest.Group{}, Groups: []manifest.Group{},
ABI: manifest.ABI{ ABI: manifest.ABI{
Methods: []manifest.Method{ Methods: []manifest.Method{

View file

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"math" "math"
"strings"
ojson "github.com/nspcc-dev/go-ordered-json" ojson "github.com/nspcc-dev/go-ordered-json"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -24,6 +25,8 @@ const (
NEP11Payable = "NEP-11-Payable" NEP11Payable = "NEP-11-Payable"
// NEP17Payable represents the name of contract interface which can receive NEP-17 tokens. // NEP17Payable represents the name of contract interface which can receive NEP-17 tokens.
NEP17Payable = "NEP-17-Payable" NEP17Payable = "NEP-17-Payable"
emptyFeatures = "{}"
) )
// Manifest represens contract metadata. // Manifest represens contract metadata.
@ -53,7 +56,7 @@ func NewManifest(name string) *Manifest {
Methods: []Method{}, Methods: []Method{},
Events: []Event{}, Events: []Event{},
}, },
Features: json.RawMessage("{}"), Features: json.RawMessage(emptyFeatures),
Groups: []Group{}, Groups: []Group{},
Permissions: []Permission{}, Permissions: []Permission{},
SupportedStandards: []string{}, SupportedStandards: []string{},
@ -107,6 +110,16 @@ func (m *Manifest) IsValid(hash util.Uint160, checkSize bool) error {
if err != nil { if err != nil {
return fmt.Errorf("ABI: %w", err) return fmt.Errorf("ABI: %w", err)
} }
if strings.Map(func(c rune) rune {
switch c {
case ' ', '\n', '\t', '\r': // Strip all JSON whitespace.
return -1
}
return c
}, string(m.Features)) != emptyFeatures { // empty struct should be left.
return errors.New("invalid features")
}
err = Groups(m.Groups).AreValid(hash) err = Groups(m.Groups).AreValid(hash)
if err != nil { if err != nil {
return err return err
@ -235,7 +248,7 @@ func (m *Manifest) FromStackItem(item stackitem.Item) error {
if str[2].Type() != stackitem.MapT || str[2].(*stackitem.Map).Len() != 0 { if str[2].Type() != stackitem.MapT || str[2].(*stackitem.Map).Len() != 0 {
return errors.New("invalid Features stackitem") return errors.New("invalid Features stackitem")
} }
m.Features = json.RawMessage("{}") m.Features = json.RawMessage(emptyFeatures)
if str[3].Type() != stackitem.ArrayT { if str[3].Type() != stackitem.ArrayT {
return errors.New("invalid SupportedStandards stackitem type") return errors.New("invalid SupportedStandards stackitem type")
} }

View file

@ -144,6 +144,24 @@ func TestIsValid(t *testing.T) {
require.NoError(t, m.IsValid(contractHash, true)) require.NoError(t, m.IsValid(contractHash, true))
}) })
t.Run("invalid, no features", func(t *testing.T) {
m.Features = nil
require.Error(t, m.IsValid(contractHash, true))
})
m.Features = json.RawMessage(emptyFeatures)
t.Run("invalid, bad features", func(t *testing.T) {
m.Features = json.RawMessage(`{ "v" : true}`)
require.Error(t, m.IsValid(contractHash, true))
})
m.Features = json.RawMessage(emptyFeatures)
t.Run("valid, features with spaces", func(t *testing.T) {
m.Features = json.RawMessage("{ \t\n\r }")
require.NoError(t, m.IsValid(contractHash, true))
})
m.Features = json.RawMessage(emptyFeatures)
m.ABI.Events = append(m.ABI.Events, Event{ m.ABI.Events = append(m.ABI.Events, Event{
Name: "itHappened", Name: "itHappened",
Parameters: []Parameter{}, Parameters: []Parameter{},