From 8ffee467c179ab8de5f1a74f94594492ba0c2d57 Mon Sep 17 00:00:00 2001 From: bkolad Date: Tue, 5 Dec 2023 11:38:18 +0100 Subject: [PATCH] Validate snap parameters --- packages/snap/src/index.ts | 6 ++- packages/snap/src/types.ts | 79 +++++++++++++++++++++----------------- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/packages/snap/src/index.ts b/packages/snap/src/index.ts index 9028ea4e..5601c325 100644 --- a/packages/snap/src/index.ts +++ b/packages/snap/src/index.ts @@ -29,8 +29,10 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ // the return is a plain hex string // https://docs.metamask.io/snaps/reference/rpc-api/#returns-5 case 'getPublicKey': { + const { path, compressed } = request.params as GetBip32PublicKeyParams; + // eslint-disable-next-line @typescript-eslint/await-thenable const approved = await snap.request({ method: 'snap_dialog', @@ -44,6 +46,7 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ ]), }, }); + if (!approved) { throw providerErrors.userRejectedRequest(); @@ -63,6 +66,7 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ case 'signTransaction': { const { transaction, path } = request.params as SignTransactionParams; + try { const call = wasm.serializeCall(transaction.message, transaction.nonce); const entropy = await snap.request({ @@ -78,6 +82,7 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ const node = await SLIP10Node.fromJSON(entropy); assert(node.privateKey); + // eslint-disable-next-line @typescript-eslint/await-thenable const approved = await snap.request({ method: 'snap_dialog', @@ -113,7 +118,6 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ const txHex = bytesToHex(tx); wasm.dealloc(); - return txHex; } catch (er) { wasm.dealloc(); diff --git a/packages/snap/src/types.ts b/packages/snap/src/types.ts index df11ccd8..7f097fcb 100644 --- a/packages/snap/src/types.ts +++ b/packages/snap/src/types.ts @@ -1,61 +1,70 @@ +import { Bytes } from '@metamask/utils'; +import { Bip32PathStruct } from '@metamask/snaps-utils/*'; +import { Infer, boolean, enums, object, optional, type, string, number, array } from 'superstruct'; + + /** - * The parameters for calling the `getPublicKey` JSON-RPC method. - * - * Note: For simplicity, these are not validated by the snap. In production, you - * should validate that the request object matches this type before using it. + * `type` is used instead of `object` to allow unknown properties. */ -export type GetBip32PublicKeyParams = { +export const GetBip32PublicKeyParamsStruct = type({ /** * The BIP-32 path to the account. */ - path: ['m', ...(`${number}` | `${number}'`)[]]; - + path: Bip32PathStruct, /** * Whether to return the public key in compressed form. */ - compressed?: boolean | undefined; + compressed: optional(boolean()), +}); - /** - * Miscellaneous parameters, which are passed to `snap_getBip32PublicKey`. - */ - [key: string]: unknown; -}; /** - * The transaction object to be submitted by the UI so the signature can be generated. + * The parameters for calling the `getPublicKey` JSON-RPC method. * - * Note: For simplicity, these are not validated by the snap. In production, you - * should validate that the request object matches this type before using it. + * Unknown properties are ignored and passed to `snap_getBip32PublicKey`. */ -export type Transaction = { - /** - * The JSON transaction to sign. - */ - message: string; +export type GetBip32PublicKeyParams = Infer< + typeof GetBip32PublicKeyParamsStruct +>; - /** - * The nonce for the transaction signature. + +/** +* The transaction object to be submitted by the UI so the signature can be generated. +* +* Note: For simplicity, these are not validated by the snap. In production, you +* should validate that the request object matches this type before using it. +*/ +export const TransactionStruct = object({ + /** + * The JSON transaction to sign. */ - nonce: number; -}; + message: string(), + /** + * The nonce for the transaction signature. + */ + nonce: number(), +}) /** * The parameters for calling the `signTransaction` JSON-RPC method. - * - * Note: For simplicity, these are not validated by the snap. In production, you - * should validate that the request object matches this type before using it. - */ -export type SignTransactionParams = { - /** +* +* Note: For simplicity, these are not validated by the snap. In production, you +* should validate that the request object matches this type before using it. +*/ +export const SignTransactionStruct = object({ + /** * The JSON transaction to sign. */ - transaction: Transaction; + transaction: TransactionStruct, - /** + /** * The BIP-32 path to the account. */ - path: string[]; -}; + path: array(string()), +}) + +export type SignTransactionParams = Infer; + /** * The expected WASM interface from the imported module.