diff --git a/integration_test/tests/operations/operationkinds_test.go b/integration_test/tests/operations/operationkinds_test.go index 6b2052f1..6dfba740 100644 --- a/integration_test/tests/operations/operationkinds_test.go +++ b/integration_test/tests/operations/operationkinds_test.go @@ -209,13 +209,19 @@ func TestOperationAllowPolicy(t *testing.T) { } func getAllOps() []string { - return []string{"activate_account", "ballot", "dal_attestation", "dal_publish_slot_header", "delegation", - "double_baking_evidence", "double_endorsement_evidence", "double_preendorsement_evidence", "drain_delegate", - "endorsement", "event", "failing_noop", "increase_paid_storage", "origination", "preendorsement", "proposals", - "register_global_constant", "reveal", "sc_rollup_add_messages", "sc_rollup_cement", - "sc_rollup_execute_outbox_message", "sc_rollup_originate", "sc_rollup_publish", "sc_rollup_recover_bond", - "sc_rollup_refute", "sc_rollup_timeout", "seed_nonce_revelation", "set_deposits_limit", "transaction", - "transfer_ticket", "update_consensus_key", "vdf_revelation", "zk_rollup_origination", "zk_rollup_publish", "zk_rollup_update"} + return []string{ // operations available in both proto_022_PsRiotum and proto_023_PtSeouLo + "activate_account", "attestation", "attestation_with_dal", "attestations_aggregate", + "ballot", "dal_entrapment_evidence", "dal_publish_commitment", "delegation", + "double_baking_evidence", "drain_delegate", "failing_noop", "increase_paid_storage", + "origination", "preattestation", "proposals", "register_global_constant", "reveal", + "seed_nonce_revelation", "set_deposits_limit", "signature_prefix", + "smart_rollup_add_messages", "smart_rollup_cement", + "smart_rollup_execute_outbox_message", "smart_rollup_originate", + "smart_rollup_publish", "smart_rollup_recover_bond", "smart_rollup_refute", + "smart_rollup_timeout", "transaction", "transfer_ticket", "update_consensus_key", + "vdf_revelation", "zk_rollup_origination", "zk_rollup_publish", "zk_rollup_update", + "finalize_unstake", "set_delegate_parameters", "stake", "unstake", + } } func getAllOpsExcluding(exclude []string) []string { diff --git a/pkg/signatory/signatory.go b/pkg/signatory/signatory.go index 967c6db0..93e61713 100644 --- a/pkg/signatory/signatory.go +++ b/pkg/signatory/signatory.go @@ -10,6 +10,7 @@ import ( "fmt" "net" "net/http" + "slices" "sort" "strings" "sync" @@ -17,6 +18,7 @@ import ( "github.com/ecadlabs/gotez/v2/b58" "github.com/ecadlabs/gotez/v2/crypt" "github.com/ecadlabs/gotez/v2/protocol/core" + proto "github.com/ecadlabs/gotez/v2/protocol/latest" "github.com/ecadlabs/signatory/pkg/auth" "github.com/ecadlabs/signatory/pkg/config" "github.com/ecadlabs/signatory/pkg/errors" @@ -652,6 +654,42 @@ func fixupRequests(req []string) { sort.Strings(req) } +func fixupOperations(ops []string) { + for i, o := range ops { + switch o { + case "endorsement": + ops[i] = "attestation" + case "preendorsement": + ops[i] = "preattestation" + case "double_endorsement_evidence": + ops[i] = "double_attestation_evidence" + case "double_preendorsement_evidence": + ops[i] = "double_preattestation_evidence" + } + } + sort.Strings(ops) +} + +func checkRequestKind(allowedKinds []string) error { + avilKinds := proto.ListSignRequests() + for _, kind := range allowedKinds { + if !slices.Contains(avilKinds, kind) { + return fmt.Errorf("invalid request kind `%s` in `allow` list", kind) + } + } + return nil +} + +func checkOperationKind(allowedKinds []string) error { + avilKinds := append(proto.ListOperations(), proto.ListPseudoOperations()...) + for _, kind := range allowedKinds { + if !slices.Contains(avilKinds, kind) { + return fmt.Errorf("invalid operation kind `%s` in `allow.generic` list", kind) + } + } + return nil +} + // PreparePolicy prepares policy data by hashing keys etc func PreparePolicy(src config.TezosConfig) (out Policy, err error) { policy := make(Policy, len(src)) @@ -671,6 +709,9 @@ func PreparePolicy(src config.TezosConfig) (out Policy, err error) { pol.AllowedRequests = append(pol.AllowedRequests, req) } fixupRequests(pol.AllowedRequests) + if err = checkRequestKind(pol.AllowedRequests); err != nil { + return false + } if ops, ok := v.Allow["generic"]; ok { pol.AllowedOps = make([]string, len(ops)) @@ -682,6 +723,9 @@ func PreparePolicy(src config.TezosConfig) (out Policy, err error) { pol.AllowedRequests = make([]string, len(v.AllowedOperations)) copy(pol.AllowedRequests, v.AllowedOperations) fixupRequests(pol.AllowedRequests) + if err = checkRequestKind(pol.AllowedRequests); err != nil { + return false + } } if v.AllowedKinds != nil { pol.AllowedOps = make([]string, len(v.AllowedKinds)) @@ -710,19 +754,10 @@ func PreparePolicy(src config.TezosConfig) (out Policy, err error) { pipe.Close() } - for i, o := range pol.AllowedOps { - switch o { - case "endorsement": - pol.AllowedOps[i] = "attestation" - case "preendorsement": - pol.AllowedOps[i] = "preattestation" - case "double_endorsement_evidence": - pol.AllowedOps[i] = "double_attestation_evidence" - case "double_preendorsement_evidence": - pol.AllowedOps[i] = "double_preattestation_evidence" - } + fixupOperations(pol.AllowedOps) + if err = checkOperationKind(pol.AllowedOps); err != nil { + return false } - sort.Strings(pol.AllowedOps) if v.AuthorizedKeys != nil { keys := v.AuthorizedKeys.List() diff --git a/pkg/signatory/signatory_test.go b/pkg/signatory/signatory_test.go index 40876ef4..79f2cb8e 100644 --- a/pkg/signatory/signatory_test.go +++ b/pkg/signatory/signatory_test.go @@ -516,3 +516,87 @@ func TestListPublicKeys(t *testing.T) { }) } } + +func TestRequestKindCheck(t *testing.T) { + priv, err := crypt.ParsePrivateKey([]byte(privateKey)) + require.NoError(t, err) + pk := priv.Public() + + tezosCfg := hashmap.NewPublicKeyHashMap([]hashmap.PublicKeyKV[*config.TezosPolicy]{ + { + Key: pk.Hash(), + Val: &config.TezosPolicy{ + Allow: map[string][]string{ + "block": nil, + "foo": nil, // invalid request kind + }, + }, + }, + }) + + _, err = signatory.PreparePolicy(tezosCfg) + require.EqualError(t, err, "invalid request kind `foo` in `allow` list") + + tezosCfgValid := hashmap.NewPublicKeyHashMap([]hashmap.PublicKeyKV[*config.TezosPolicy]{ + { + Key: pk.Hash(), + Val: &config.TezosPolicy{ + Allow: map[string][]string{ + "attestation": nil, + "generic": nil, + "block": nil, + "preendorsement": nil, + "attestation_with_dal": nil, + }, + }, + }, + }) + + _, err = signatory.PreparePolicy(tezosCfgValid) + require.NoError(t, err) +} + +func TestOperationKindCheck(t *testing.T) { + priv, err := crypt.ParsePrivateKey([]byte(privateKey)) + require.NoError(t, err) + pk := priv.Public() + + tezosCfg := hashmap.NewPublicKeyHashMap([]hashmap.PublicKeyKV[*config.TezosPolicy]{ + { + Key: pk.Hash(), + Val: &config.TezosPolicy{ + Allow: map[string][]string{ + "generic": {"transaction", "invalid_op"}, // invalid operation kind included + }, + }, + }, + }) + + _, err = signatory.PreparePolicy(tezosCfg) + require.EqualError(t, err, "invalid operation kind `invalid_op` in `allow.generic` list") + + tezosCfgValid := hashmap.NewPublicKeyHashMap([]hashmap.PublicKeyKV[*config.TezosPolicy]{ + { + Key: pk.Hash(), + Val: &config.TezosPolicy{ + Allow: map[string][]string{ + "generic": { + "transaction", + "delegation", + "origination", + "reveal", + "stake", + "unstake", + "finalize_unstake", + "attestation", + "preendorsement", + "ballot", + }, + }, + }, + }, + }) + + _, err = signatory.PreparePolicy(tezosCfgValid) + require.NoError(t, err) +} diff --git a/signatory.yaml b/signatory.yaml index f60a5add..76790e52 100644 --- a/signatory.yaml +++ b/signatory.yaml @@ -64,17 +64,18 @@ tezos: # purposes. log_payloads: true allow: - # List of [block, endorsement, failing_noop, generic, preendorsement] + # List of [attestation, attestation_with_dal, block, generic, preattestation] generic: # List of - # [activate_account, attestation, attestation_with_dal, ballot, dal_publish_commitment, delegation, double_attestation_evidence, - # double_baking_evidence, double_preattestation_evidence, drain_delegate, failing_noop, finalize_unstake, increase_paid_storage, - # origination, preattestation, proposals, register_global_constant, reveal, seed_nonce_revelation, set_delegate_parameters, - # set_deposits_limit, signature_prefix, smart_rollup_add_messages, smart_rollup_cement, smart_rollup_execute_outbox_message, - # smart_rollup_originate, smart_rollup_publish, smart_rollup_recover_bond, smart_rollup_refute, smart_rollup_timeout, stake, - # transaction, transfer_ticket, unstake, update_consensus_key, vdf_revelation, zk_rollup_origination, zk_rollup_publish, - # zk_rollup_update] + # [activate_account, attestations_aggregate, ballot, dal_entrapment_evidence, dal_publish_commitment, + # delegation, double_baking_evidence, double_consensus_operation_evidence, drain_delegate, failing_noop, + # finalize_unstake, increase_paid_storage, origination, preattestations_aggregate, proposals, + # register_global_constant, reveal, seed_nonce_revelation, set_delegate_parameters, set_deposits_limit, + # signature_prefix, smart_rollup_add_messages, smart_rollup_cement, smart_rollup_execute_outbox_message, + # smart_rollup_originate, smart_rollup_publish, smart_rollup_recover_bond, smart_rollup_refute, + # smart_rollup_timeout, stake, transaction, transfer_ticket, unstake, update_companion_key, + # update_consensus_key, vdf_revelation, zk_rollup_origination, zk_rollup_publish, zk_rollup_update] - transaction - - endorsement + - attestation block: - endorsement: + attestation: