Skip to content

Commit

Permalink
Validate that initial permissions type matches validation struct (#2293)
Browse files Browse the repository at this point in the history
By using Superstruct's `Describe` type, we can validate that a struct is
assignable to a type. In this case, we make sure that the validation
struct in `snaps-utils` matches the type in `snaps-sdk`. I had to make a
small change to the type to get validation to pass.
  • Loading branch information
Mrtenz authored Mar 20, 2024
1 parent 081b0f7 commit 3b94315
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 14 deletions.
14 changes: 13 additions & 1 deletion packages/snaps-sdk/src/types/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ export type Cronjob = {
request: Omit<JsonRpcRequest, 'jsonrpc' | 'id'>;
};

export type NameLookupMatchers =
| {
tlds: string[];
}
| {
schemes: string[];
}
| {
tlds: string[];
schemes: string[];
};

export type Bip32Entropy = {
curve: 'secp256k1' | 'ed25519';
path: string[];
Expand Down Expand Up @@ -37,7 +49,7 @@ export type InitialPermissions = Partial<{
};
'endowment:name-lookup': {
chains?: ChainId[];
matchers?: { tlds?: string[]; schemes?: string[] };
matchers?: NameLookupMatchers;
maxRequestTime?: number;
};
'endowment:network-access': EmptyObject;
Expand Down
2 changes: 1 addition & 1 deletion packages/snaps-utils/coverage.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"branches": 96.48,
"functions": 98.64,
"lines": 98.74,
"statements": 94.51
"statements": 94.52
}
11 changes: 11 additions & 0 deletions packages/snaps-utils/src/manifest/validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Bip32EntropyStruct,
Bip32PathStruct,
createSnapManifest,
EmptyObjectStruct,
isSnapManifest,
SnapIdsStruct,
} from './validation';
Expand Down Expand Up @@ -152,6 +153,16 @@ describe('SnapIdsStruct', () => {
});
});

describe('EmptyObjectStruct', () => {
it('accepts an empty object', () => {
expect(is({}, EmptyObjectStruct)).toBe(true);
});

it('rejects non-empty objects', () => {
expect(is({ foo: 'bar' }, EmptyObjectStruct)).toBe(false);
});
});

describe('isSnapManifest', () => {
it('returns true for a valid snap manifest', () => {
expect(isSnapManifest(getSnapManifest())).toBe(true);
Expand Down
29 changes: 17 additions & 12 deletions packages/snaps-utils/src/manifest/validation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isValidBIP32PathSegment } from '@metamask/key-tree';
import type { InitialPermissions } from '@metamask/snaps-sdk';
import type { EmptyObject, InitialPermissions } from '@metamask/snaps-sdk';
import {
assertStruct,
ChecksumStruct,
Expand All @@ -8,7 +8,7 @@ import {
inMilliseconds,
Duration,
} from '@metamask/utils';
import type { Infer, Struct } from 'superstruct';
import type { Describe, Infer, Struct } from 'superstruct';
import {
array,
boolean,
Expand Down Expand Up @@ -175,15 +175,20 @@ export const HandlerCaveatsStruct = object({

export type HandlerCaveats = Infer<typeof HandlerCaveatsStruct>;

export const EmptyObjectStruct = object<EmptyObject>({}) as unknown as Struct<
EmptyObject,
null
>;

/* eslint-disable @typescript-eslint/naming-convention */
export const PermissionsStruct = type({
export const PermissionsStruct: Describe<InitialPermissions> = type({
'endowment:cronjob': optional(
assign(
HandlerCaveatsStruct,
object({ jobs: CronjobSpecificationArrayStruct }),
),
),
'endowment:ethereum-provider': optional(object({})),
'endowment:ethereum-provider': optional(EmptyObjectStruct),
'endowment:keyring': optional(
assign(HandlerCaveatsStruct, KeyringOriginsStruct),
),
Expand All @@ -197,7 +202,7 @@ export const PermissionsStruct = type({
}),
),
),
'endowment:network-access': optional(object({})),
'endowment:network-access': optional(EmptyObjectStruct),
'endowment:page-home': optional(HandlerCaveatsStruct),
'endowment:rpc': optional(assign(HandlerCaveatsStruct, RpcOriginsStruct)),
'endowment:signature-insight': optional(
Expand All @@ -216,11 +221,11 @@ export const PermissionsStruct = type({
}),
),
),
'endowment:webassembly': optional(object({})),
snap_dialog: optional(object({})),
snap_manageState: optional(object({})),
snap_manageAccounts: optional(object({})),
snap_notify: optional(object({})),
'endowment:webassembly': optional(EmptyObjectStruct),
snap_dialog: optional(EmptyObjectStruct),
snap_manageState: optional(EmptyObjectStruct),
snap_manageAccounts: optional(EmptyObjectStruct),
snap_notify: optional(EmptyObjectStruct),
snap_getBip32Entropy: optional(SnapGetBip32EntropyPermissionsStruct),
snap_getBip32PublicKey: optional(SnapGetBip32EntropyPermissionsStruct),
snap_getBip44Entropy: optional(
Expand All @@ -230,8 +235,8 @@ export const PermissionsStruct = type({
Infinity,
),
),
snap_getEntropy: optional(object({})),
snap_getLocale: optional(object({})),
snap_getEntropy: optional(EmptyObjectStruct),
snap_getLocale: optional(EmptyObjectStruct),
wallet_snap: optional(SnapIdsStruct),
});
/* eslint-enable @typescript-eslint/naming-convention */
Expand Down

0 comments on commit 3b94315

Please sign in to comment.