diff --git a/CHANGELOG.md b/CHANGELOG.md index ba5b6877a..a200fe0c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -118,6 +118,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#2022](https://github.com/NibiruChain/nibiru/pull/2022) - feat(evm): debug_traceCall method implemented - [#2023](https://github.com/NibiruChain/nibiru/pull/2023) - fix(evm)!: adjusted generation and parsing of the block bloom events - [#2031](https://github.com/NibiruChain/nibiru/pull/2031) - fix(evm): debug calls with custom tracer and tracer options +- [#2032](https://github.com/NibiruChain/nibiru/pull/2032) - feat(evm): ante handler to prohibit authz grant evm messages #### Dapp modules: perp, spot, oracle, etc diff --git a/app/ante.go b/app/ante.go index f8c661163..79c2d5adf 100644 --- a/app/ante.go +++ b/app/ante.go @@ -62,6 +62,7 @@ func NewAnteHandlerNonEVM( ) sdk.AnteHandler { return sdk.ChainAnteDecorators( ante.AnteDecoratorPreventEtheruemTxMsgs{}, // reject MsgEthereumTxs + ante.AnteDecoratorAuthzGuard{}, // disable certain messages in authz grant "generic" authante.NewSetUpContextDecorator(), wasmkeeper.NewLimitSimulationGasDecorator(opts.WasmConfig.SimulationGasLimit), wasmkeeper.NewCountTXDecorator(opts.TxCounterStoreKey), diff --git a/app/ante/auth_grard_test.go b/app/ante/auth_grard_test.go new file mode 100644 index 000000000..be776ae70 --- /dev/null +++ b/app/ante/auth_grard_test.go @@ -0,0 +1,98 @@ +package ante_test + +import ( + "time" + + sdkclienttx "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/ante" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *AnteTestSuite) TestAnteDecoratorAuthzGuard() { + testCases := []struct { + name string + txMsg func() sdk.Msg + wantErr string + }{ + { + name: "sad: authz generic grant with evm message", + txMsg: func() sdk.Msg { + someTime := time.Now() + expiryTime := someTime.Add(time.Hour) + genericGrant, err := authz.NewGrant( + someTime, + authz.NewGenericAuthorization(sdk.MsgTypeURL(&evm.MsgEthereumTx{})), &expiryTime, + ) + s.Require().NoError(err) + return &authz.MsgGrant{Grant: genericGrant} + }, + wantErr: "not allowed", + }, + { + name: "happy: authz generic grant with non evm message", + txMsg: func() sdk.Msg { + someTime := time.Now() + expiryTime := someTime.Add(time.Hour) + genericGrant, err := authz.NewGrant( + someTime, + authz.NewGenericAuthorization(sdk.MsgTypeURL(&stakingtypes.MsgCreateValidator{})), &expiryTime, + ) + s.Require().NoError(err) + return &authz.MsgGrant{Grant: genericGrant} + }, + wantErr: "", + }, + { + name: "happy: authz non generic grant", + txMsg: func() sdk.Msg { + someTime := time.Now() + expiryTime := someTime.Add(time.Hour) + genericGrant, err := authz.NewGrant( + someTime, + &banktypes.SendAuthorization{}, + &expiryTime, + ) + s.Require().NoError(err) + return &authz.MsgGrant{Grant: genericGrant} + }, + wantErr: "", + }, + { + name: "happy: non authz message", + txMsg: func() sdk.Msg { + return &evm.MsgEthereumTx{} + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + anteDec := ante.AnteDecoratorAuthzGuard{} + + encCfg := app.MakeEncodingConfig() + txBuilder, err := sdkclienttx.Factory{}. + WithChainID(s.ctx.ChainID()). + WithTxConfig(encCfg.TxConfig). + BuildUnsignedTx(tc.txMsg()) + s.Require().NoError(err) + + _, err = anteDec.AnteHandle( + deps.Ctx, txBuilder.GetTx(), false, evmtest.NextNoOpAnteHandler, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) + } +} diff --git a/app/ante/authz_guard.go b/app/ante/authz_guard.go index 9ceb8b240..5b85bc147 100644 --- a/app/ante/authz_guard.go +++ b/app/ante/authz_guard.go @@ -1,9 +1,51 @@ +// Copyright (c) 2023-2024 Nibi, Inc. package ante -// TODO: https://github.com/NibiruChain/nibiru/issues/1915 -// feat(ante): Add an authz guard to disable authz Ethereum txs and provide -// additional security around the default functionality exposed by the module. -// -// Implemenetation Notes -// UD-NOTE - IsAuthzMessage fn. Use authz import with module name -// UD-NOTE - Define set of disabled txMsgs +import ( + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/authz" + "github.com/cosmos/gogoproto/proto" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +var genericAuthTypeTurl = "/" + proto.MessageName(&authz.GenericAuthorization{}) + +// AnteDecoratorAuthzGuard filters autz messages +type AnteDecoratorAuthzGuard struct{} + +// AnteHandle rejects "authz grant generic --msg-type '/eth.evm.v1.MsgEthereumTx'" +func (rmd AnteDecoratorAuthzGuard) AnteHandle( + ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler, +) (newCtx sdk.Context, err error) { + for _, msg := range tx.GetMsgs() { + if msgGrant, ok := msg.(*authz.MsgGrant); ok { + if msgGrant.Grant.Authorization == nil { + return ctx, errors.Wrapf( + errortypes.ErrInvalidType, + "grant authorization is missing", + ) + } + if msgGrant.Grant.Authorization.TypeUrl == genericAuthTypeTurl { + var genericAuth authz.GenericAuthorization + err = proto.Unmarshal(msgGrant.Grant.Authorization.Value, &genericAuth) + if err != nil { + return ctx, errors.Wrapf( + errortypes.ErrInvalidType, + "failed unmarshaling generic authorization", + ) + } + if genericAuth.MsgTypeURL() == sdk.MsgTypeURL(&evm.MsgEthereumTx{}) { + return ctx, errors.Wrapf( + errortypes.ErrNotSupported, + "authz grant generic for msg type %s is not allowed", + genericAuth.MsgTypeURL(), + ) + } + } + } + } + return next(ctx, tx, simulate) +}