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", diff --git a/packages/tron-wallet-snap/src/handlers/keyring/keyring.ts b/packages/tron-wallet-snap/src/handlers/keyring/keyring.ts index a2eda3a1b..048e63f41 100644 --- a/packages/tron-wallet-snap/src/handlers/keyring/keyring.ts +++ b/packages/tron-wallet-snap/src/handlers/keyring/keyring.ts @@ -14,6 +14,7 @@ import type { KeyringSnapRpc, } from '@metamask/keyring-api/v2'; 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, @@ -34,6 +35,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'; @@ -54,11 +56,7 @@ import { UuidStruct, } from '../../validation/structs'; import type { TronWalletKeyringRequest } from '../../validation/structs'; -import { - validateOrigin, - validateRequest, - validateResponse, -} from '../../validation/validators'; +import { validateRequest, validateResponse } from '../../validation/validators'; import { BackgroundEventMethod } from '../cronjob/cronjob'; import { TronMultichainMethod } from './keyring-types'; @@ -104,7 +102,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..b40549a15 --- /dev/null +++ b/packages/tron-wallet-snap/src/permissions.test.ts @@ -0,0 +1,25 @@ +import { KeyringSnapRpcMethod } from '@metamask/keyring-api/v2'; +import { DEFAULT_METAMASK_ORIGIN } from '@metamask/snap-networks-utils'; + +import { originPermissions } from './permissions'; + +// `ENVIRONMENT` differs between local runs (`test`) and CI (`production`), so +// these assertions hold for both permission shapes. +describe('originPermissions', () => { + it('grants MetaMask the privileged keyring methods', () => { + expect( + originPermissions + .get(DEFAULT_METAMASK_ORIGIN) + ?.has(KeyringSnapRpcMethod.CreateAccounts), + ).toBe(true); + }); + + 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); + }); +}); 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. *