From 630011d05d410ff62a3695166ce15b7a126c714e Mon Sep 17 00:00:00 2001 From: Julien Fontanel Date: Wed, 26 Aug 2026 15:36:15 +0200 Subject: [PATCH 1/5] feat: use shareable permissions helepers in Tron snap --- .../src/handlers/keyring/keyring.ts | 5 +- .../tron-wallet-snap/src/handlers/rpc/rpc.ts | 5 +- .../tron-wallet-snap/src/permissions.test.ts | 22 +++++++ packages/tron-wallet-snap/src/permissions.ts | 65 +++++++++---------- .../src/validation/validators.ts | 13 +--- 5 files changed, 60 insertions(+), 50 deletions(-) create mode 100644 packages/tron-wallet-snap/src/permissions.test.ts diff --git a/packages/tron-wallet-snap/src/handlers/keyring/keyring.ts b/packages/tron-wallet-snap/src/handlers/keyring/keyring.ts index 62f60d678..35400dd95 100644 --- a/packages/tron-wallet-snap/src/handlers/keyring/keyring.ts +++ b/packages/tron-wallet-snap/src/handlers/keyring/keyring.ts @@ -18,6 +18,7 @@ import type { } from '@metamask/keyring-api/v2'; import { emitSnapKeyringEvent } from '@metamask/keyring-snap-sdk'; import { handleKeyringRequest } from '@metamask/keyring-snap-sdk/v2'; +import { validateOrigin } from '@metamask/snap-networks-utils'; import type { Logger } from '@metamask/snap-networks-utils'; import { InvalidParamsError, @@ -38,6 +39,7 @@ import { ESSENTIAL_ASSETS } from '../../constants'; import type { Network } from '../../constants'; import { asStrictKeyringAccount } from '../../entities/keyring-account'; import type { TronKeyringAccount } from '../../entities/keyring-account'; +import { originPermissions } from '../../permissions'; import type { AccountsService } from '../../services/accounts/AccountsService'; import type { AssetsService } from '../../services/assets/AssetsService'; import type { ConfirmationHandler } from '../../services/confirmation/ConfirmationHandler'; @@ -59,7 +61,6 @@ import { } from '../../validation/structs'; import type { TronWalletKeyringRequest } from '../../validation/structs'; import { - validateOrigin, validateRequest, validateResponse, } from '../../validation/validators'; @@ -108,7 +109,7 @@ export class KeyringHandler implements KeyringSnapRpc { } async handle(origin: string, request: JsonRpcRequest): Promise { - validateOrigin(origin, request.method); + validateOrigin(origin, request.method, originPermissions); const result = await handleKeyringRequest(this, request); return result ?? null; } diff --git a/packages/tron-wallet-snap/src/handlers/rpc/rpc.ts b/packages/tron-wallet-snap/src/handlers/rpc/rpc.ts index 6d144396f..ed1683e55 100644 --- a/packages/tron-wallet-snap/src/handlers/rpc/rpc.ts +++ b/packages/tron-wallet-snap/src/handlers/rpc/rpc.ts @@ -1,8 +1,9 @@ +import { validateOrigin } from '@metamask/snap-networks-utils'; import type { Logger } from '@metamask/snap-networks-utils'; import { MethodNotFoundError } from '@metamask/snaps-sdk'; import type { Json, JsonRpcRequest } from '@metamask/snaps-sdk'; -import { validateOrigin } from '../../validation/validators'; +import { originPermissions } from '../../permissions'; export class RpcHandler { readonly #logger: Logger; @@ -12,7 +13,7 @@ export class RpcHandler { } async handle(origin: string, request: JsonRpcRequest): Promise { - validateOrigin(origin, request.method); + validateOrigin(origin, request.method, originPermissions); this.#logger.log('Handling RPC request', request); diff --git a/packages/tron-wallet-snap/src/permissions.test.ts b/packages/tron-wallet-snap/src/permissions.test.ts new file mode 100644 index 000000000..25de24c01 --- /dev/null +++ b/packages/tron-wallet-snap/src/permissions.test.ts @@ -0,0 +1,22 @@ +import { KeyringSnapRpcMethod } from '@metamask/keyring-api/v2'; +import { + DEFAULT_DEV_ORIGINS, + DEFAULT_PROD_ORIGINS, +} from '@metamask/snap-networks-utils'; + +import { TestDappRpcRequestMethod } from './handlers/rpc/types'; +import { originPermissions } from './permissions'; + +describe('originPermissions', () => { + it('allows localhost dapp methods and MetaMask keyring methods in test', () => { + expect( + originPermissions.get(DEFAULT_DEV_ORIGINS[0])?.has( + TestDappRpcRequestMethod.ComputeFee, + ), + ).toBe(true); + expect( + originPermissions.get('metamask')?.has(KeyringSnapRpcMethod.CreateAccounts), + ).toBe(true); + expect(originPermissions.has(DEFAULT_PROD_ORIGINS[0])).toBe(false); + }); +}); diff --git a/packages/tron-wallet-snap/src/permissions.ts b/packages/tron-wallet-snap/src/permissions.ts index 5f6a6deb6..3dc1a15c7 100644 --- a/packages/tron-wallet-snap/src/permissions.ts +++ b/packages/tron-wallet-snap/src/permissions.ts @@ -1,37 +1,37 @@ import { KeyringRpcMethod } from '@metamask/keyring-api'; import { KeyringSnapRpcMethod } from '@metamask/keyring-api/v2'; +import { + createOriginPermissions, + DEFAULT_DEV_ORIGINS, + DEFAULT_PROD_ORIGINS, +} from '@metamask/snap-networks-utils'; import { TestDappRpcRequestMethod } from './handlers/rpc/types'; // eslint-disable-next-line no-restricted-globals const isDev = process.env.ENVIRONMENT !== 'production'; -const prodOrigins = ['https://portfolio.metamask.io']; -const allowedOrigins = isDev ? ['http://localhost:3000'] : prodOrigins; - -const dappPermissions = isDev - ? new Set([ - // Keyring v2 methods - KeyringSnapRpcMethod.GetAccounts, - KeyringSnapRpcMethod.GetAccount, - KeyringSnapRpcMethod.DeleteAccount, - KeyringSnapRpcMethod.GetAccountBalances, - KeyringSnapRpcMethod.SubmitRequest, - KeyringSnapRpcMethod.GetAccountTransactions, - KeyringSnapRpcMethod.GetAccountAssets, - // Keyring v1 methods kept for backwards compatibility — callers using - // old method names are still accepted by the permission layer. - KeyringRpcMethod.ListAccounts, - KeyringRpcMethod.CreateAccount, - KeyringRpcMethod.DiscoverAccounts, - KeyringRpcMethod.ListAccountTransactions, - KeyringRpcMethod.ListAccountAssets, - // Test dapp specific methods - TestDappRpcRequestMethod.ComputeFee, - ]) - : new Set([]); +const dappMethods = [ + // Keyring v2 methods + KeyringSnapRpcMethod.GetAccounts, + KeyringSnapRpcMethod.GetAccount, + KeyringSnapRpcMethod.DeleteAccount, + KeyringSnapRpcMethod.GetAccountBalances, + KeyringSnapRpcMethod.SubmitRequest, + KeyringSnapRpcMethod.GetAccountTransactions, + KeyringSnapRpcMethod.GetAccountAssets, + // Keyring v1 methods kept for backwards compatibility — callers using + // old method names are still accepted by the permission layer. + KeyringRpcMethod.ListAccounts, + KeyringRpcMethod.CreateAccount, + KeyringRpcMethod.DiscoverAccounts, + KeyringRpcMethod.ListAccountTransactions, + KeyringRpcMethod.ListAccountAssets, + // Test dapp specific methods + TestDappRpcRequestMethod.ComputeFee, +]; -const metamaskPermissions = new Set([ +const metamaskMethods = [ // Keyring v2 methods KeyringSnapRpcMethod.GetAccounts, KeyringSnapRpcMethod.GetAccount, @@ -51,13 +51,10 @@ const metamaskPermissions = new Set([ KeyringRpcMethod.DiscoverAccounts, KeyringRpcMethod.ListAccountTransactions, KeyringRpcMethod.ListAccountAssets, -]); - -const metamask = 'metamask'; - -export const originPermissions = new Map>([]); +]; -for (const origin of allowedOrigins) { - originPermissions.set(origin, dappPermissions); -} -originPermissions.set(metamask, metamaskPermissions); +export const originPermissions = createOriginPermissions({ + dappMethods: isDev ? dappMethods : [], + metamaskMethods, + origins: isDev ? DEFAULT_DEV_ORIGINS : DEFAULT_PROD_ORIGINS, +}); diff --git a/packages/tron-wallet-snap/src/validation/validators.ts b/packages/tron-wallet-snap/src/validation/validators.ts index 667bdc4a7..5dbc226b1 100644 --- a/packages/tron-wallet-snap/src/validation/validators.ts +++ b/packages/tron-wallet-snap/src/validation/validators.ts @@ -1,19 +1,8 @@ /* eslint-disable @typescript-eslint/only-throw-error */ -import { InvalidParamsError, UnauthorizedError } from '@metamask/snaps-sdk'; +import { InvalidParamsError } from '@metamask/snaps-sdk'; import type { Infer, Struct } from '@metamask/superstruct'; import { assert } from '@metamask/superstruct'; -import { originPermissions } from '../permissions'; - -export const validateOrigin = (origin: string, method: string): void => { - if (!origin) { - throw new UnauthorizedError('Origin not found'); - } - if (!originPermissions.get(origin)?.has(method)) { - throw new UnauthorizedError('Permission denied'); - } -}; - /** * Validates that the request parameters conform to the expected structure defined by the provided struct. * From 912536388596659b4e9106e493dba3422e708c76 Mon Sep 17 00:00:00 2001 From: Julien Fontanel Date: Wed, 26 Aug 2026 15:59:06 +0200 Subject: [PATCH 2/5] fix: lint + shasum --- packages/tron-wallet-snap/snap.manifest.json | 2 +- .../tron-wallet-snap/src/handlers/keyring/keyring.ts | 5 +---- packages/tron-wallet-snap/src/permissions.test.ts | 10 ++++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/tron-wallet-snap/snap.manifest.json b/packages/tron-wallet-snap/snap.manifest.json index 0f913d6c3..0a1f4d4c7 100644 --- a/packages/tron-wallet-snap/snap.manifest.json +++ b/packages/tron-wallet-snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/internal-snaps.git" }, "source": { - "shasum": "1pO58lSrPr77UAfnWKp2vkYda0BjiXybyYrG3SFy7u0=", + "shasum": "OJcsMInaVCec0/DBSWKbJFcdBMleQK8sWkIQnKHsRk8=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/tron-wallet-snap/src/handlers/keyring/keyring.ts b/packages/tron-wallet-snap/src/handlers/keyring/keyring.ts index 35400dd95..ccc54b2c9 100644 --- a/packages/tron-wallet-snap/src/handlers/keyring/keyring.ts +++ b/packages/tron-wallet-snap/src/handlers/keyring/keyring.ts @@ -60,10 +60,7 @@ import { UuidStruct, } from '../../validation/structs'; import type { TronWalletKeyringRequest } from '../../validation/structs'; -import { - validateRequest, - validateResponse, -} from '../../validation/validators'; +import { validateRequest, validateResponse } from '../../validation/validators'; import { BackgroundEventMethod } from '../cronjob/cronjob'; import { TronMultichainMethod } from './keyring-types'; diff --git a/packages/tron-wallet-snap/src/permissions.test.ts b/packages/tron-wallet-snap/src/permissions.test.ts index 25de24c01..4bc6833ef 100644 --- a/packages/tron-wallet-snap/src/permissions.test.ts +++ b/packages/tron-wallet-snap/src/permissions.test.ts @@ -10,12 +10,14 @@ import { originPermissions } from './permissions'; describe('originPermissions', () => { it('allows localhost dapp methods and MetaMask keyring methods in test', () => { expect( - originPermissions.get(DEFAULT_DEV_ORIGINS[0])?.has( - TestDappRpcRequestMethod.ComputeFee, - ), + originPermissions + .get(DEFAULT_DEV_ORIGINS[0]) + ?.has(TestDappRpcRequestMethod.ComputeFee), ).toBe(true); expect( - originPermissions.get('metamask')?.has(KeyringSnapRpcMethod.CreateAccounts), + originPermissions + .get('metamask') + ?.has(KeyringSnapRpcMethod.CreateAccounts), ).toBe(true); expect(originPermissions.has(DEFAULT_PROD_ORIGINS[0])).toBe(false); }); From 0c66f00da2a6dd7a8da07213d1b3664afcbbf16d Mon Sep 17 00:00:00 2001 From: Julien Fontanel Date: Wed, 26 Aug 2026 16:06:04 +0200 Subject: [PATCH 3/5] chore: fix shasum --- packages/tron-wallet-snap/snap.manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tron-wallet-snap/snap.manifest.json b/packages/tron-wallet-snap/snap.manifest.json index f17db91d4..d3edbcc44 100644 --- a/packages/tron-wallet-snap/snap.manifest.json +++ b/packages/tron-wallet-snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/internal-snaps.git" }, "source": { - "shasum": "y1eBQiM9lDqVQ6wJj1BG3qtDXB45dwC7SdKzYF5YZWo=", + "shasum": "s7ueqmygeuJDd4abueEEefDf6A0qh5SCvdTdc0v7ygc=", "location": { "npm": { "filePath": "dist/bundle.js", From 523ffa467f4b8dc7d08aea2ae524a1630ce1e2b2 Mon Sep 17 00:00:00 2001 From: Julien Fontanel Date: Wed, 26 Aug 2026 16:22:01 +0200 Subject: [PATCH 4/5] fix: unit test --- .../tron-wallet-snap/src/permissions.test.ts | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/tron-wallet-snap/src/permissions.test.ts b/packages/tron-wallet-snap/src/permissions.test.ts index 4bc6833ef..b40549a15 100644 --- a/packages/tron-wallet-snap/src/permissions.test.ts +++ b/packages/tron-wallet-snap/src/permissions.test.ts @@ -1,24 +1,25 @@ import { KeyringSnapRpcMethod } from '@metamask/keyring-api/v2'; -import { - DEFAULT_DEV_ORIGINS, - DEFAULT_PROD_ORIGINS, -} from '@metamask/snap-networks-utils'; +import { DEFAULT_METAMASK_ORIGIN } from '@metamask/snap-networks-utils'; -import { TestDappRpcRequestMethod } from './handlers/rpc/types'; import { originPermissions } from './permissions'; +// `ENVIRONMENT` differs between local runs (`test`) and CI (`production`), so +// these assertions hold for both permission shapes. describe('originPermissions', () => { - it('allows localhost dapp methods and MetaMask keyring methods in test', () => { + it('grants MetaMask the privileged keyring methods', () => { expect( originPermissions - .get(DEFAULT_DEV_ORIGINS[0]) - ?.has(TestDappRpcRequestMethod.ComputeFee), - ).toBe(true); - expect( - originPermissions - .get('metamask') + .get(DEFAULT_METAMASK_ORIGIN) ?.has(KeyringSnapRpcMethod.CreateAccounts), ).toBe(true); - expect(originPermissions.has(DEFAULT_PROD_ORIGINS[0])).toBe(false); + }); + + it('never grants privileged methods to dapp origins', () => { + const dappMethods = [...originPermissions.entries()] + .filter(([origin]) => origin !== DEFAULT_METAMASK_ORIGIN) + .flatMap(([, methods]) => [...methods]); + + expect(dappMethods).not.toContain(KeyringSnapRpcMethod.CreateAccounts); + expect(dappMethods).not.toContain(KeyringSnapRpcMethod.ExportAccount); }); }); From 201a6e75ae1f5e777219ec3dac7f981423f93f47 Mon Sep 17 00:00:00 2001 From: Julien Fontanel Date: Thu, 27 Aug 2026 13:41:30 +0200 Subject: [PATCH 5/5] chore: update shasum --- packages/tron-wallet-snap/snap.manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tron-wallet-snap/snap.manifest.json b/packages/tron-wallet-snap/snap.manifest.json index e67081d00..7997d931a 100644 --- a/packages/tron-wallet-snap/snap.manifest.json +++ b/packages/tron-wallet-snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/internal-snaps.git" }, "source": { - "shasum": "JjgAZZwXVK0y4gNHI1erLqSZV5tyn2n5xgbJl7mFT60=", + "shasum": "4Aj/lLoXEU4oXMBNzQBrTD4OHPhklvRQtUl2jFA+Goo=", "location": { "npm": { "filePath": "dist/bundle.js",