From 06bb8398a434c7dc1e226f6533d53037a30d24fa Mon Sep 17 00:00:00 2001 From: Julien Fontanel Date: Wed, 26 Aug 2026 17:00:24 +0200 Subject: [PATCH 1/2] feat(stellar-wallet-snap): use shareable permissions helepers in Stellar snap --- .../src/handlers/keyring/keyring.test.ts | 4 +-- .../src/handlers/keyring/keyring.ts | 5 +-- .../stellar-wallet-snap/src/permissions.ts | 33 +++++++++---------- .../src/utils/requestResponse.test.ts | 30 ++++++++++------- .../src/utils/requestResponse.ts | 25 +------------- 5 files changed, 41 insertions(+), 56 deletions(-) diff --git a/packages/stellar-wallet-snap/src/handlers/keyring/keyring.test.ts b/packages/stellar-wallet-snap/src/handlers/keyring/keyring.test.ts index 0753e83e4..cef059155 100644 --- a/packages/stellar-wallet-snap/src/handlers/keyring/keyring.test.ts +++ b/packages/stellar-wallet-snap/src/handlers/keyring/keyring.test.ts @@ -47,8 +47,8 @@ import { KeyringHandler } from './keyring'; jest.mock('../../utils/logger'); jest.mock('../../utils/snap'); -jest.mock('../../utils/requestResponse', () => ({ - ...jest.requireActual('../../utils/requestResponse'), +jest.mock('@metamask/snap-networks-utils', () => ({ + ...jest.requireActual('@metamask/snap-networks-utils'), validateOrigin: jest.fn(), })); jest.mock('@metamask/keyring-snap-sdk/v2', () => ({ diff --git a/packages/stellar-wallet-snap/src/handlers/keyring/keyring.ts b/packages/stellar-wallet-snap/src/handlers/keyring/keyring.ts index 982860e42..08ee085da 100644 --- a/packages/stellar-wallet-snap/src/handlers/keyring/keyring.ts +++ b/packages/stellar-wallet-snap/src/handlers/keyring/keyring.ts @@ -13,6 +13,7 @@ import { } from '@metamask/keyring-api'; 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 } from '@metamask/snaps-sdk'; import type { Json, JsonRpcRequest } from '@metamask/snaps-sdk'; @@ -27,6 +28,7 @@ import type { KnownCaip2ChainId, } from '../../api'; import { AppConfig } from '../../config'; +import { originPermissions } from '../../permissions'; import type { AccountService, StellarKeyringAccount, @@ -48,7 +50,6 @@ import { getSlip44AssetId, isClassicAssetId, isSlip44Id, - validateOrigin, validateRequest, withCatchAndThrowSnapError, } from '../../utils'; @@ -105,7 +106,7 @@ export class KeyringHandler implements KeyringSnapRpc { origin, method: request.method, }); - validateOrigin(origin, request.method); + validateOrigin(origin, request.method, originPermissions); const keyringRequestResult = await handleKeyringRequest(this, request); this.#logger.debug('Keyring request handled', { origin, diff --git a/packages/stellar-wallet-snap/src/permissions.ts b/packages/stellar-wallet-snap/src/permissions.ts index 7106f893e..e4495aed6 100644 --- a/packages/stellar-wallet-snap/src/permissions.ts +++ b/packages/stellar-wallet-snap/src/permissions.ts @@ -1,18 +1,13 @@ import { KeyringRpcMethod } from '@metamask/keyring-api'; import { KeyringSnapRpcMethod } from '@metamask/keyring-api/v2'; +import { + createOriginPermissions, + DEFAULT_PROD_ORIGINS, +} from '@metamask/snap-networks-utils'; import { METAMASK_ORIGIN } from './constants'; -const prodOrigins = ['https://portfolio.metamask.io']; -const allowedOrigins = prodOrigins; - -/** - * Dapp origins are connected to the snap, but the snap does not expose any - * keyring method to them. The set is empty so every dapp call is rejected. - */ -const dappPermissions = new Set([]); - -const metamaskPermissions = new Set([ +const metamaskMethods = [ KeyringSnapRpcMethod.GetAccounts, KeyringSnapRpcMethod.GetAccount, KeyringSnapRpcMethod.CreateAccounts, @@ -31,11 +26,15 @@ const metamaskPermissions = new Set([ */ KeyringRpcMethod.ListAccountAssets, KeyringRpcMethod.ListAccountTransactions, -]); +]; -export const originPermissions = new Map>([]); - -for (const origin of allowedOrigins) { - originPermissions.set(origin, dappPermissions); -} -originPermissions.set(METAMASK_ORIGIN, metamaskPermissions); +/** + * Dapp origins are connected to the snap, but the snap does not expose any + * keyring method to them. `dappMethods` is empty so every dapp call is rejected. + */ +export const originPermissions = createOriginPermissions({ + dappMethods: [], + metamaskMethods, + origins: DEFAULT_PROD_ORIGINS, + metamaskOrigin: METAMASK_ORIGIN, +}); diff --git a/packages/stellar-wallet-snap/src/utils/requestResponse.test.ts b/packages/stellar-wallet-snap/src/utils/requestResponse.test.ts index b6d7ed325..ce1df28e4 100644 --- a/packages/stellar-wallet-snap/src/utils/requestResponse.test.ts +++ b/packages/stellar-wallet-snap/src/utils/requestResponse.test.ts @@ -1,5 +1,6 @@ import { KeyringRpcMethod } from '@metamask/keyring-api'; import { KeyringSnapRpcMethod } from '@metamask/keyring-api/v2'; +import { validateOrigin } from '@metamask/snap-networks-utils'; import { InvalidParamsError, SnapError, @@ -8,11 +9,8 @@ import { import { string, object } from '@metamask/superstruct'; import { METAMASK_ORIGIN } from '../constants'; -import { - validateRequest, - validateResponse, - validateOrigin, -} from './requestResponse'; +import { originPermissions } from '../permissions'; +import { validateRequest, validateResponse } from './requestResponse'; const TestStruct = object({ url: string(), @@ -55,9 +53,9 @@ describe('validateOrigin', () => { KeyringSnapRpcMethod.CreateAccounts, KeyringSnapRpcMethod.SubmitRequest, ])('rejects method %s for dapps', (method) => { - expect(() => validateOrigin('http://localhost:3000', method)).toThrow( - UnauthorizedError, - ); + expect(() => + validateOrigin('http://localhost:3000', method, originPermissions), + ).toThrow(UnauthorizedError); }); it.each([ @@ -67,7 +65,11 @@ describe('validateOrigin', () => { KeyringRpcMethod.ListAccountAssets, ])('rejects method %s for the connected dapp origin', (method) => { expect(() => - validateOrigin('https://portfolio.metamask.io', method), + validateOrigin( + 'https://portfolio.metamask.io', + method, + originPermissions, + ), ).toThrow(UnauthorizedError); }); @@ -85,14 +87,20 @@ describe('validateOrigin', () => { ])('allows method %s for metamask', (method) => { const origin = METAMASK_ORIGIN; - expect(() => validateOrigin(origin, method)).not.toThrow(); + expect(() => + validateOrigin(origin, method, originPermissions), + ).not.toThrow(); }); it.each(['invalid', undefined, '', null])( 'rejects unauthorized origin %s', (origin) => { expect(() => - validateOrigin(origin as string, KeyringSnapRpcMethod.GetAccounts), + validateOrigin( + origin as string, + KeyringSnapRpcMethod.GetAccounts, + originPermissions, + ), ).toThrow(UnauthorizedError); }, ); diff --git a/packages/stellar-wallet-snap/src/utils/requestResponse.ts b/packages/stellar-wallet-snap/src/utils/requestResponse.ts index 2d96d0e9b..cfa2f1f10 100644 --- a/packages/stellar-wallet-snap/src/utils/requestResponse.ts +++ b/packages/stellar-wallet-snap/src/utils/requestResponse.ts @@ -1,31 +1,8 @@ /* eslint-disable @typescript-eslint/only-throw-error */ -import { - InvalidParamsError, - SnapError, - UnauthorizedError, -} from '@metamask/snaps-sdk'; +import { InvalidParamsError, SnapError } from '@metamask/snaps-sdk'; import type { Struct } from '@metamask/superstruct'; import { assert, create } from '@metamask/superstruct'; -import { originPermissions } from '../permissions'; - -/** - * Validates that the origin is allowed to make the request. - * If the origin is not found or the method is not allowed, an UnauthorizedError is thrown. - * - * @param origin - The origin of the request. - * @param method - The method of the request. - * @throws {UnauthorizedError} If the origin is not found or the method is not allowed. - */ -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. * Returns the validated (and coerced) value so handlers receive the correct types. From 1f8d599005d0dbd0914937c8cbe6e6f1a0e4ad50 Mon Sep 17 00:00:00 2001 From: Julien Fontanel Date: Thu, 27 Aug 2026 10:18:20 +0200 Subject: [PATCH 2/2] chore: update shasum --- packages/stellar-wallet-snap/snap.manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stellar-wallet-snap/snap.manifest.json b/packages/stellar-wallet-snap/snap.manifest.json index fec5aec77..4c877cf20 100644 --- a/packages/stellar-wallet-snap/snap.manifest.json +++ b/packages/stellar-wallet-snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/internal-snaps.git" }, "source": { - "shasum": "394xQCAQ/v69sB9OcdC5tUFRzUtDOhBdQ8lANM/r41k=", + "shasum": "XmHWNIVVLlhvKq+iksu3PyMYqx95Zj7lQAB/s1cOFlk=", "location": { "npm": { "filePath": "dist/bundle.js",