Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(evm): ante handler to prohibit authz grant evm messages #2032

Merged
merged 7 commits into from
Oct 1, 2024

Conversation

onikonychev
Copy link
Contributor

@onikonychev onikonychev commented Sep 12, 2024

Authz allows granting a generic permission for any message.
This ante handler disables granting the message type MsgEthereumTx.

So, now

nibid tx authz grant nibi1jle8khj3aennq24zx6g93aam9rt0fqhgyp4h52 generic --from validator --msg-type '/eth.evm.v1.MsgEthereumTx' --yes

fails with:

authz grant generic for msg type /eth.evm.v1.MsgEthereumTx is not allowed: feature not supported

Summary by CodeRabbit

  • New Features

    • Introduced an authorization guard to enhance security by prohibiting unauthorized EVM message grants.
    • Added functionality to filter and reject certain authorization messages in Ethereum transactions.
  • Bug Fixes

    • Improved transaction handling by ensuring unauthorized authorization grants are not processed.
  • Tests

    • Implemented a comprehensive suite of unit tests for the new authorization guard, covering various transaction scenarios to ensure correct behavior.

@onikonychev onikonychev requested a review from a team as a code owner September 12, 2024 12:46
Copy link
Contributor

coderabbitai bot commented Sep 12, 2024

Warning

Rate limit exceeded

@onikonychev has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 16 minutes and 58 seconds before requesting another review.

How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Commits

Files that changed from the base of the PR and between edb81e2 and 17292be.

Walkthrough

The pull request introduces a new authorization guard in the EVM module to prevent unauthorized authorization grant messages. This is achieved through the AnteDecoratorAuthzGuard, which filters transaction messages to enhance security. Additionally, a suite of unit tests is added to ensure the proper functionality of this new guard, validating various scenarios involving authorization grants.

Changes

File Change Summary
CHANGELOG.md Added entry for the new ante handler to prohibit authorization grant EVM messages.
app/ante.go Introduced ante.AnteDecoratorAuthzGuard in NewAnteHandlerNonEVM to disable certain authorization messages.
app/ante/auth_grard_test.go Added unit tests for AnteDecoratorAuthzGuard, covering various authorization scenarios.
app/ante/authz_guard.go Implemented AnteHandle method in AnteDecoratorAuthzGuard to reject unauthorized Ethereum transaction messages.

Assessment against linked issues

Objective Addressed Explanation
Implement an "authz guard" (#[1915])
Document why such a guard exists in this GitHub issue (#[1915]) Documentation was not provided.

Poem

🐇 In the meadow, where bunnies play,
A guard stands watch, keeping harm at bay.
With messages filtered, safe and sound,
In the world of EVM, security is found!
Hops of joy, a new feature's here,
Protecting our realm, let's give a cheer! 🌼


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    -- I pushed a fix in commit <commit_id>, please review it.
    -- Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    -- @coderabbitai generate unit testing code for this file.
    -- @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    -- @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    -- @coderabbitai read src/utils.ts and generate unit testing code.
    -- @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    -- @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

codecov bot commented Sep 12, 2024

Codecov Report

Attention: Patch coverage is 58.33333% with 15 lines in your changes missing coverage. Please review.

Project coverage is 65.57%. Comparing base (81ea61d) to head (17292be).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
app/ante/authz_guard.go 57.14% 12 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2032      +/-   ##
==========================================
- Coverage   65.58%   65.57%   -0.02%     
==========================================
  Files         264      265       +1     
  Lines       16653    16689      +36     
==========================================
+ Hits        10922    10943      +21     
- Misses       4846     4858      +12     
- Partials      885      888       +3     
Files with missing lines Coverage Δ
app/ante.go 75.00% <100.00%> (+0.53%) ⬆️
app/ante/authz_guard.go 57.14% <57.14%> (ø)

Comment on lines 14 to 24
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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revoke seems harmless. I would also remove Exec as well to address this vulnerability from Jump.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added rejection for exec of MsgEthereumTx

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Outside diff range and nitpick comments (1)
app/ante.go (1)

65-65: Add documentation explaining the rationale for the guard.

Please add comments or documentation explaining the necessity of the ante.AnteDecoratorAuthzGuard{} to ensure that users understand the risks involved with the authz module. This was one of the objectives mentioned in the linked issue #1915.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 60167ca and 2257dee.

Files selected for processing (4)
  • CHANGELOG.md (1 hunks)
  • app/ante.go (1 hunks)
  • app/ante/auth_grard_test.go (1 hunks)
  • app/ante/authz_guard.go (1 hunks)
Additional comments not posted (4)
app/ante/authz_guard.go (1)

20-51: LGTM!

The AnteHandle function correctly rejects the granting of the message type MsgEthereumTx within the authorization (authz) module. This change enhances security by preventing the generic permission granting for Ethereum transaction messages.

app/ante/auth_grard_test.go (1)

18-98: LGTM!

The test function TestAnteDecoratorAuthzGuard is well-structured and covers important scenarios. The test cases are clearly defined and the expected behavior is specified. The test function uses good practices such as:

  • Using s.Run to run each test case as a sub-test for better isolation and reporting.
  • Using s.Require assertions to check the expected behavior.
  • Using evmtest.NewTestDeps to create a new set of dependencies for each test case to ensure a clean state.
  • Using sdkclienttx.Factory to create a new unsigned transaction for each test case.
  • Using app.MakeEncodingConfig to create a new encoding configuration for each test case to ensure a clean state.
  • Using AnteHandle method of the AnteDecoratorAuthzGuard ante decorator to handle the transaction, which is the main functionality being tested.
  • Checking the error returned by the AnteHandle method and comparing it with the expected error.

Overall, the test function is well-written and thoroughly tests the AnteDecoratorAuthzGuard ante decorator.

app/ante.go (1)

65-65: LGTM!

The ante.AnteDecoratorAuthzGuard{} is correctly added to the ante handler chain for non-EVM transactions. This change aligns with the PR objective of implementing an "authz guard" to restrict the granting of certain message types, specifically Ethereum transactions, to mitigate potential security risks associated with the authz module.

CHANGELOG.md (1)

122-122: LGTM, the ante handler feature looks good!

The new ante handler to prohibit authz grant EVM messages enhances security as intended.

To confirm the ante handler is properly integrated, run this script to search for the handler registration:

Verification successful

Ante handler integration verified successfully.

The new ante handler to prohibit authz grant EVM messages is properly integrated into the application. The AnteDecoratorAuthzGuard is included in the NewAnteHandlerNonEVM function, ensuring the intended functionality.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the ante handler is properly wired in the app. 
# Expect: A call to `app.SetAnteHandler` with the new handler.

rg --type go --context 5 --ignore-case 'SetAnteHandler\('

Length of output: 518


Script:

#!/bin/bash
# Description: Search for the definition or assignment of `anteHandler` to verify it includes the new ante handler.
rg --type go --context 5 --ignore-case 'anteHandler'

Length of output: 41220

Comment on lines 14 to 24
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider removing Exec as well to address the vulnerability.

The past review comments suggest removing Exec as well to address this vulnerability from Jump.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Outside diff range and nitpick comments (1)
CHANGELOG.md (1)

123-124: Approved, but consider providing more context.

The refactors to remove unnecessary code and mocks from the eth/rpc/backend are good changes that will help with maintainability. However, consider providing a bit more rationale in the future for why the code/mocks were unnecessary and how it improves things. More context can help the reader understand the motivations and impact better.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 2257dee and 262d035.

Files selected for processing (1)
  • CHANGELOG.md (1 hunks)
Additional comments not posted (2)
CHANGELOG.md (2)

122-122: LGTM!

The changelog entry is clear and the feature makes sense from a security perspective.


125-125: Excellent!

Adding more realistic backend tests using a test network and real transactions is a great way to expand coverage and catch bugs. Nice work.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Outside diff range and nitpick comments (4)
app/ante/authz_guard.go (4)

12-12: Typo in comment: 'autz' should be 'authz'

There's a typo in the comment on line 12. It should be "filters authz messages" instead of "filters autz messages".


23-27: Use a more appropriate error type when authorization is missing

When msgGrant.Grant.Authorization is nil, the code returns an ErrInvalidType error. Consider using errortypes.ErrInvalidRequest to more accurately represent a missing required field.


31-35: Clarify error message for unmarshaling authorization

The error message "failed unmarshaling generic authorization %s" may be misleading if the authorization is not a GenericAuthorization. Consider rephrasing it to "failed unmarshaling authorization: %s" for clarity.


58-60: Use ErrNotSupported when rejecting MsgEthereumTx in MsgExec

Currently, when rejecting a MsgEthereumTx within a MsgExec, the code returns an ErrInvalidType error. Since this is a policy decision to not support MsgEthereumTx in MsgExec, using errortypes.ErrNotSupported would be more appropriate.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between be7a9ca and edb81e2.

Files selected for processing (2)
  • app/ante/auth_grard_test.go (1 hunks)
  • app/ante/authz_guard.go (1 hunks)

Comment on lines +17 to +66
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() {
// Do not allow grant for MsgEthereumTx
if msgGrant, ok := msg.(*authz.MsgGrant); ok {
if msgGrant.Grant.Authorization == nil {
return ctx, errors.Wrapf(
errortypes.ErrInvalidType,
"grant authorization is missing",
)
}
authorization, err := msgGrant.Grant.GetAuthorization()
if err != nil {
return ctx, errors.Wrapf(
errortypes.ErrInvalidType,
"failed unmarshaling generic authorization %s", err,
)
}
if genericAuth, ok := authorization.(*authz.GenericAuthorization); ok {
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(),
)
}
}
}
// Also reject MsgEthereumTx in exec
if msgExec, ok := msg.(*authz.MsgExec); ok {
msgsInExec, err := msgExec.GetMessages()
if err != nil {
return ctx, errors.Wrapf(
errortypes.ErrInvalidType,
"failed getting exec messages %s", err,
)
}
for _, msgInExec := range msgsInExec {
if _, ok := msgInExec.(*evm.MsgEthereumTx); ok {
return ctx, errors.Wrapf(
errortypes.ErrInvalidType,
"MsgEthereumTx needs to be contained within a tx with 'ExtensionOptionsEthereumTx' option",
)
}
}
}
}
return next(ctx, tx, simulate)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommend adding unit tests for AnteDecoratorAuthzGuard

To ensure that AnteDecoratorAuthzGuard works as intended, please consider adding unit tests covering:

  • Attempting to grant authorization for MsgEthereumTx and expecting rejection.
  • Executing a MsgExec containing MsgEthereumTx and verifying it is rejected.
  • Confirming that valid authz messages not involving MsgEthereumTx are processed correctly.

Comment on lines +29 to +32
genericGrant, err := authz.NewGrant(
someTime,
authz.NewGenericAuthorization(sdk.MsgTypeURL(&evm.MsgEthereumTx{})), &expiryTime,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect argument order in 'authz.NewGrant' function calls

The authz.NewGrant function expects the authorization as the first argument and expiration as the second argument. Currently, someTime (the current time) is being passed as the first argument, which is incorrect.

Apply these diffs to fix the argument order:

For lines 29-32:

 genericGrant, err := authz.NewGrant(
-	someTime,
 	authz.NewGenericAuthorization(sdk.MsgTypeURL(&evm.MsgEthereumTx{})), &expiryTime,
+)

For lines 43-46:

 genericGrant, err := authz.NewGrant(
-	someTime,
 	authz.NewGenericAuthorization(sdk.MsgTypeURL(&stakingtypes.MsgCreateValidator{})), &expiryTime,
+)

For lines 57-61:

 genericGrant, err := authz.NewGrant(
-	someTime,
 	&banktypes.SendAuthorization{},
 	&expiryTime,
+)

Ensure that someTime is not passed to authz.NewGrant, as it is not a required parameter for this function.

Also applies to: 43-46, 57-61

Comment on lines +78 to +79
sdk.AccAddress("nibiuser"),
[]sdk.Msg{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect usage of sdk.AccAddress with string literals

Passing a string directly to sdk.AccAddress does not produce a valid address. sdk.AccAddress expects a byte slice. To create a valid address from a string, use sdk.AccAddressFromBech32 or convert the string to bytes.

Apply these diffs to correct the address usage:

For lines 78-79:

-msgExec := authz.NewMsgExec(
-	sdk.AccAddress("nibiuser"),
+addr, err := sdk.AccAddressFromBech32("nibi1...") // Replace with a valid Bech32 address
+s.Require().NoError(err)
+msgExec := authz.NewMsgExec(
+	addr,

For lines 90-91:

-msgExec := authz.NewMsgExec(
-	sdk.AccAddress("nibiuser"),
+addr, err := sdk.AccAddressFromBech32("nibi1...") // Replace with a valid Bech32 address
+s.Require().NoError(err)
+msgExec := authz.NewMsgExec(
+	addr,

For lines 104-105:

-msgExec := authz.NewMsgExec(
-	sdk.AccAddress("nibiuser"),
+addr, err := sdk.AccAddressFromBech32("nibi1...") // Replace with a valid Bech32 address
+s.Require().NoError(err)
+msgExec := authz.NewMsgExec(
+	addr,

Replace "nibi1..." with a valid Bech32 test address.

Also applies to: 90-91, 104-105

Comment on lines +116 to +137
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)
})
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider refactoring to improve test readability and maintainability

The loop over test cases and the structure of the tests are good, but there's repetitive code within each test case, especially in the transaction building and ante handler invocation. Consider abstracting common setup steps into helper functions to reduce duplication and enhance readability.

Example:

  • Create a helper function to build transactions:

    func buildTx(msg sdk.Msg) (sdk.Tx, error) {
    	encCfg := app.MakeEncodingConfig()
    	return sdkclienttx.Factory{}.
    		WithChainID(s.ctx.ChainID()).
    		WithTxConfig(encCfg.TxConfig).
    		BuildUnsignedTx(msg)
    }
  • Use the helper function in your tests:

    txBuilder, err := buildTx(tc.txMsg())
    s.Require().NoError(err)

"github.com/NibiruChain/nibiru/v2/x/evm/evmtest"
)

func (s *AnteTestSuite) TestAnteDecoratorAuthzGuar() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in function name: Missing 'd' in 'AuthzGuard'

The function name TestAnteDecoratorAuthzGuar appears to have a typo. It should be TestAnteDecoratorAuthzGuard to accurately reflect the tested component and maintain consistency.

Apply this diff to correct the function name:

-func (s *AnteTestSuite) TestAnteDecoratorAuthzGuar() {
+func (s *AnteTestSuite) TestAnteDecoratorAuthzGuard() {
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (s *AnteTestSuite) TestAnteDecoratorAuthzGuar() {
func (s *AnteTestSuite) TestAnteDecoratorAuthzGuard() {

@onikonychev onikonychev enabled auto-merge (squash) September 27, 2024 18:03
@onikonychev onikonychev merged commit 1829bc4 into main Oct 1, 2024
14 checks passed
@onikonychev onikonychev deleted the feat/ante-guard branch October 1, 2024 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants