Skip to content

Commit

Permalink
FIX: Validate Snap arguments (#7)
Browse files Browse the repository at this point in the history
* Validate snap parameters

* Fix formatting

* Fix comment

* Fix lint

* fix lint

* Fix typescript version

* Add validation

* Fix CI

* Sig validation

* fix snap shasum

---------

Co-authored-by: Victor Lopez <[email protected]>
  • Loading branch information
bkolad and vlopes11 authored Dec 7, 2023
1 parent ecc22a3 commit 369ec86
Show file tree
Hide file tree
Showing 8 changed files with 32,517 additions and 3,341 deletions.
28,388 changes: 28,388 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"prettier": "^2.7.1",
"prettier-plugin-packagejson": "^2.2.18",
"sharp": "^0.30.7",
"typescript": "^4.7.4"
"typescript": "4.8.4"
},
"packageManager": "[email protected]",
"engines": {
Expand Down
6 changes: 4 additions & 2 deletions packages/snap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
"@metamask/rpc-errors": "^6.1.0",
"@metamask/snaps-types": "^3.0.1",
"@metamask/snaps-ui": "^3.0.1",
"@metamask/snaps-utils": "^5.0.0",
"@metamask/utils": "^8.2.1",
"@noble/ed25519": "^1.6.0",
"buffer": "^6.0.3"
"buffer": "^6.0.3",
"superstruct": "^1.0.3"
},
"devDependencies": {
"@jest/globals": "^29.5.0",
Expand All @@ -60,7 +62,7 @@
"rimraf": "^3.0.2",
"through2": "^4.0.2",
"ts-jest": "^29.1.0",
"typescript": "^4.7.4"
"typescript": "4.8.4"
},
"packageManager": "[email protected]",
"engines": {
Expand Down
2 changes: 1 addition & 1 deletion packages/snap/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/Sovereign-Labs/sov-snap.git"
},
"source": {
"shasum": "ZcjIMDNneWe6FWkEl1qAXeyFYYTAwtJ+GuCnp6JKoMk=",
"shasum": "4o0iXdvcsoPhEgfJ41LTGihawXvb1EHJ67P7GNnXgbk=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
28 changes: 22 additions & 6 deletions packages/snap/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { SLIP10Node } from '@metamask/key-tree';
import { providerErrors } from '@metamask/rpc-errors';
import { providerErrors, rpcErrors } from '@metamask/rpc-errors';
import type { OnRpcRequestHandler } from '@metamask/snaps-types';
import { DialogType } from '@metamask/snaps-types';
import { copyable, heading, panel, text } from '@metamask/snaps-ui';
import { add0x, assert, bytesToHex, remove0x } from '@metamask/utils';
import { sign } from '@noble/ed25519';
import { validate as superstructValidate } from 'superstruct';

import type { GetBip32PublicKeyParams, SignTransactionParams } from './types';
import { GetBip32PublicKeyParamsStruct, SignTransactionStruct } from './types';
import { SovWasm } from './wasm';

const wasm = new SovWasm();
Expand All @@ -29,8 +30,16 @@ 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;
const [validationErr, params] = superstructValidate(
request.params,
GetBip32PublicKeyParamsStruct,
);

if (validationErr !== undefined) {
throw rpcErrors.invalidParams(validationErr.toString());
}

const { path, compressed } = params;
// eslint-disable-next-line @typescript-eslint/await-thenable
const approved = await snap.request({
method: 'snap_dialog',
Expand All @@ -44,7 +53,6 @@ export const onRpcRequest: OnRpcRequestHandler = async ({
]),
},
});

if (!approved) {
throw providerErrors.userRejectedRequest();
}
Expand All @@ -61,7 +69,16 @@ export const onRpcRequest: OnRpcRequestHandler = async ({
}

case 'signTransaction': {
const { transaction, path } = request.params as SignTransactionParams;
const [validationErr, params] = superstructValidate(
request.params,
SignTransactionStruct,
);

if (validationErr !== undefined) {
throw rpcErrors.invalidParams(validationErr.toString());
}

const { transaction, path } = params;

try {
const call = wasm.serializeCall(transaction.message, transaction.nonce);
Expand Down Expand Up @@ -113,7 +130,6 @@ export const onRpcRequest: OnRpcRequestHandler = async ({
const txHex = bytesToHex(tx);

wasm.dealloc();

return txHex;
} catch (er) {
wasm.dealloc();
Expand Down
53 changes: 24 additions & 29 deletions packages/snap/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,56 @@
import { Bip32PathStruct } from '@metamask/snaps-utils';
import {
boolean,
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;

/**
* Miscellaneous parameters, which are passed to `snap_getBip32PublicKey`.
*/
[key: string]: unknown;
};
compressed: optional(boolean()),
});

/**
* 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 type Transaction = {
export const TransactionStruct = object({
/**
* The JSON transaction to sign.
*/
message: string;

message: string(),
/**
* The nonce for the transaction signature.
*/
nonce: number;
};
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 = {
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()),
});

/**
* The expected WASM interface from the imported module.
Expand Down
1 change: 1 addition & 0 deletions packages/snap/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"strictNullChecks": true,
"baseUrl": "./"
},
"include": ["**/*.ts"]
Expand Down
Loading

0 comments on commit 369ec86

Please sign in to comment.