Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/snaps-rpc-methods/scripts/generate-schema.mts
Original file line number Diff line number Diff line change
Expand Up @@ -1294,7 +1294,7 @@ async function processPermittedHandler(
}

/**
* Process the permitted handlers defined in `src/permitted/handlers.ts`,
* Process the permitted handlers defined in `src/permitted/middleware.ts`,
* extracting the method names, descriptions, parameters, return types, and
* subject types for each handler.
*
Expand All @@ -1303,7 +1303,7 @@ async function processPermittedHandler(
* @returns An array of method schemas extracted from the permitted handlers.
*/
async function processPermittedHandlers(project: Project) {
const handlersFile = project.getSourceFile('src/permitted/handlers.ts');
const handlersFile = project.getSourceFile('src/permitted/middleware.ts');
assert(handlersFile, 'Handlers file not found.');

const permittedHandlers =
Expand Down
6 changes: 1 addition & 5 deletions packages/snaps-rpc-methods/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
export {
handlers as permittedMethods,
createSnapsMethodMiddleware,
} from './permitted';
export { createSnapsMethodMiddleware } from './permitted';
export type { PermittedRpcMethodHooks } from './permitted';
export { SnapCaveatType } from '@metamask/snaps-utils';
export { selectHooks } from './utils';
export * from './endowments';
export * from './middleware';
export * from './permissions';
Expand Down
61 changes: 34 additions & 27 deletions packages/snaps-rpc-methods/src/permitted/cancelBackgroundEvent.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine';
import type {
JsonRpcEngineEndCallback,
MethodHandler,
} from '@metamask/json-rpc-engine';
import type { Messenger } from '@metamask/messenger';
import type { PermissionControllerHasPermissionAction } from '@metamask/permission-controller';
import { providerErrors, rpcErrors } from '@metamask/rpc-errors';
import type {
JsonRpcRequest,
CancelBackgroundEventParams,
CancelBackgroundEventResult,
SnapId,
} from '@metamask/snaps-sdk';
import { type InferMatching } from '@metamask/snaps-utils';
import { StructError, create, object, string } from '@metamask/superstruct';
import { type PendingJsonRpcResponse } from '@metamask/utils';

import { SnapEndowments } from '../endowments';
import type { PermittedHandlerExport } from '../types';
import type { MethodHooksObject } from '../utils';

const methodName = 'snap_cancelBackgroundEvent';
import type { CronjobControllerCancelAction } from '../types';

const hookNames: MethodHooksObject<CancelBackgroundEventMethodHooks> = {
cancelBackgroundEvent: true,
hasPermission: true,
};

export type CancelBackgroundEventMethodHooks = {
cancelBackgroundEvent: (id: string) => void;
hasPermission: (permissionName: string) => boolean;
};
export type CancelBackgroundEventMethodActions =
| PermissionControllerHasPermissionAction
| CronjobControllerCancelAction;

/**
* Cancel a background event created by
Expand All @@ -46,13 +43,17 @@ export type CancelBackgroundEventMethodHooks = {
* ```
*/
export const cancelBackgroundEventHandler = {
methodNames: [methodName] as const,
implementation: getCancelBackgroundEventImplementation,
hookNames,
} satisfies PermittedHandlerExport<
CancelBackgroundEventMethodHooks,
actionNames: [
'PermissionController:hasPermission',
'CronjobController:cancel',
],
} satisfies MethodHandler<
never,
CancelBackgroundEventMethodActions,
CancelBackgroundEventParameters,
CancelBackgroundEventResult
CancelBackgroundEventResult,
{ origin: SnapId }
>;

const CancelBackgroundEventsParametersStruct = object({
Expand All @@ -72,21 +73,27 @@ export type CancelBackgroundEventParameters = InferMatching<
* @param _next - The `json-rpc-engine` "next" callback. Not used by this
* function.
* @param end - The `json-rpc-engine` "end" callback.
* @param hooks - The RPC method hooks.
* @param hooks.cancelBackgroundEvent - The function to cancel a background event.
* @param hooks.hasPermission - The function to check if a snap has the `endowment:cronjob` permission.
* @param _hooks - The RPC method hooks. Not used by this function.
* @param messenger - The messenger used to call controller actions.
* @returns Nothing.
*/
async function getCancelBackgroundEventImplementation(
req: JsonRpcRequest<CancelBackgroundEventParameters>,
req: JsonRpcRequest<CancelBackgroundEventParameters> & { origin: SnapId },
res: PendingJsonRpcResponse<CancelBackgroundEventResult>,
_next: unknown,
end: JsonRpcEngineEndCallback,
{ cancelBackgroundEvent, hasPermission }: CancelBackgroundEventMethodHooks,
_hooks: never,
messenger: Messenger<string, CancelBackgroundEventMethodActions>,
): Promise<void> {
const { params } = req;
const { params, origin } = req;

if (!hasPermission(SnapEndowments.Cronjob)) {
if (
!messenger.call(
'PermissionController:hasPermission',
origin,
SnapEndowments.Cronjob,
)
) {
return end(providerErrors.unauthorized());
}

Expand All @@ -95,7 +102,7 @@ async function getCancelBackgroundEventImplementation(

const { id } = validatedParams;

cancelBackgroundEvent(id);
messenger.call('CronjobController:cancel', origin, id);
res.result = null;
} catch (error) {
return end(error);
Expand Down
72 changes: 33 additions & 39 deletions packages/snaps-rpc-methods/src/permitted/clearState.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine';
import type {
JsonRpcEngineEndCallback,
MethodHandler,
} from '@metamask/json-rpc-engine';
import type { Messenger } from '@metamask/messenger';
import type { PermissionControllerHasPermissionAction } from '@metamask/permission-controller';
import { providerErrors, rpcErrors } from '@metamask/rpc-errors';
import type { ClearStateParams, ClearStateResult } from '@metamask/snaps-sdk';
import { type InferMatching } from '@metamask/snaps-utils';
Expand All @@ -12,15 +17,11 @@ import {
import type { PendingJsonRpcResponse, JsonRpcRequest } from '@metamask/utils';

import { manageStateBuilder } from '../restricted/manageState';
import type { PermittedHandlerExport } from '../types';
import type { MethodHooksObject } from '../utils';
import type { SnapControllerClearSnapStateAction } from '../types';

const methodName = 'snap_clearState';

const hookNames: MethodHooksObject<ClearStateHooks> = {
clearSnapState: true,
hasPermission: true,
};
export type ClearStateMethodActions =
| PermissionControllerHasPermissionAction
| SnapControllerClearSnapStateAction;

/**
* Clear the entire state of the Snap.
Expand All @@ -36,30 +37,19 @@ const hookNames: MethodHooksObject<ClearStateHooks> = {
* ```
*/
export const clearStateHandler = {
methodNames: [methodName] as const,
implementation: clearStateImplementation,
hookNames,
} satisfies PermittedHandlerExport<
ClearStateHooks,
actionNames: [
'PermissionController:hasPermission',
'SnapController:clearSnapState',
],
} satisfies MethodHandler<
never,
ClearStateMethodActions,
ClearStateParameters,
ClearStateResult
ClearStateResult,
{ origin: string }
>;

export type ClearStateHooks = {
/**
* A function that clears the state of the requesting Snap.
*/
clearSnapState: (encrypted: boolean) => void;

/**
* Check if the requesting origin has a given permission.
*
* @param permissionName - The name of the permission to check.
* @returns Whether the origin has the permission.
*/
hasPermission: (permissionName: string) => boolean;
};

const ClearStateParametersStruct = object({
encrypted: optional(boolean()),
});
Expand All @@ -77,31 +67,35 @@ export type ClearStateParameters = InferMatching<
* @param _next - The `json-rpc-engine` "next" callback. Not used by this
* function.
* @param end - The `json-rpc-engine` "end" callback.
* @param hooks - The RPC method hooks.
* @param hooks.clearSnapState - A function that clears the state of the
* requesting Snap.
* @param hooks.hasPermission - Check whether a given origin has a given
* permission.
* @param _hooks - The RPC method hooks. Not used by this function.
* @param messenger - The messenger used to call controller actions.
* @returns Nothing.
*/
async function clearStateImplementation(
request: JsonRpcRequest<ClearStateParameters>,
request: JsonRpcRequest<ClearStateParameters> & { origin: string },
response: PendingJsonRpcResponse<ClearStateResult>,
_next: unknown,
end: JsonRpcEngineEndCallback,
{ clearSnapState, hasPermission }: ClearStateHooks,
_hooks: never,
messenger: Messenger<string, ClearStateMethodActions>,
): Promise<void> {
const { params } = request;
const { params, origin } = request;

if (!hasPermission(manageStateBuilder.targetName)) {
if (
!messenger.call(
'PermissionController:hasPermission',
origin,
manageStateBuilder.targetName,
)
) {
return end(providerErrors.unauthorized());
}

try {
const validatedParams = getValidatedParams(params);
const { encrypted = true } = validatedParams;

clearSnapState(encrypted);
messenger.call('SnapController:clearSnapState', origin, encrypted);
response.result = null;
} catch (error) {
return end(error);
Expand Down
59 changes: 31 additions & 28 deletions packages/snaps-rpc-methods/src/permitted/closeWebSocket.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine';
import type {
JsonRpcEngineEndCallback,
MethodHandler,
} from '@metamask/json-rpc-engine';
import type { Messenger } from '@metamask/messenger';
import type { PermissionControllerHasPermissionAction } from '@metamask/permission-controller';
import { providerErrors, rpcErrors } from '@metamask/rpc-errors';
import type {
JsonRpcRequest,
Expand All @@ -10,20 +15,11 @@ import { create, object, string, StructError } from '@metamask/superstruct';
import type { PendingJsonRpcResponse } from '@metamask/utils';

import { SnapEndowments } from '../endowments';
import type { PermittedHandlerExport } from '../types';
import type { MethodHooksObject } from '../utils';

const methodName = 'snap_closeWebSocket';

const hookNames: MethodHooksObject<CloseWebSocketMethodHooks> = {
hasPermission: true,
closeWebSocket: true,
};
import type { WebSocketServiceCloseAction } from '../types';

export type CloseWebSocketMethodHooks = {
hasPermission: (permissionName: string) => boolean;
closeWebSocket: (id: string) => void;
};
export type CloseWebSocketMethodActions =
| PermissionControllerHasPermissionAction
| WebSocketServiceCloseAction;

const CloseWebSocketParametersStruct = object({
id: string(),
Expand Down Expand Up @@ -54,13 +50,14 @@ export type CloseWebSocketParameters = InferMatching<
* ```
*/
export const closeWebSocketHandler = {
methodNames: [methodName] as const,
implementation: closeWebSocketImplementation,
hookNames,
} satisfies PermittedHandlerExport<
CloseWebSocketMethodHooks,
actionNames: ['PermissionController:hasPermission', 'WebSocketService:close'],
} satisfies MethodHandler<
never,
CloseWebSocketMethodActions,
CloseWebSocketParams,
CloseWebSocketResult
CloseWebSocketResult,
{ origin: string }
>;

/**
Expand All @@ -70,27 +67,33 @@ export const closeWebSocketHandler = {
* @param res - The JSON-RPC response object.
* @param _next - The `json-rpc-engine` "next" callback. Not used by this function.
* @param end - The `json-rpc-engine` "end" callback.
* @param hooks - The RPC method hooks.
* @param hooks.hasPermission - The function to check if a snap has the `endowment:network-access` permission.
* @param hooks.closeWebSocket - The function to close a WebSocket.
* @param _hooks - The RPC method hooks. Not used by this function.
* @param messenger - The messenger used to call controller actions.
* @returns Nothing.
*/
function closeWebSocketImplementation(
req: JsonRpcRequest<CloseWebSocketParameters>,
req: JsonRpcRequest<CloseWebSocketParameters> & { origin: string },
res: PendingJsonRpcResponse<CloseWebSocketResult>,
_next: unknown,
end: JsonRpcEngineEndCallback,
{ hasPermission, closeWebSocket }: CloseWebSocketMethodHooks,
_hooks: never,
messenger: Messenger<string, CloseWebSocketMethodActions>,
): void {
if (!hasPermission(SnapEndowments.NetworkAccess)) {
const { params, origin } = req;

if (
!messenger.call(
'PermissionController:hasPermission',
origin,
SnapEndowments.NetworkAccess,
)
) {
return end(providerErrors.unauthorized());
}

const { params } = req;

try {
const { id } = getValidatedParams(params);
closeWebSocket(id);
messenger.call('WebSocketService:close', origin, id);
res.result = null;
} catch (error) {
return end(error);
Expand Down
Loading
Loading