From 3b0e9226c0cb3b91df9dba3f123c34e578faf401 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Fri, 4 Oct 2024 16:54:02 +0200 Subject: [PATCH 1/2] Restrict proposed name in manifest --- .../snaps-utils/src/manifest/validation.test.ts | 1 + packages/snaps-utils/src/manifest/validation.ts | 9 +++++++-- packages/snaps-utils/src/types.test.ts | 15 ++++++++++++++- packages/snaps-utils/src/types.ts | 6 ++++++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/snaps-utils/src/manifest/validation.test.ts b/packages/snaps-utils/src/manifest/validation.test.ts index 082552d01b..e11ed6a067 100644 --- a/packages/snaps-utils/src/manifest/validation.test.ts +++ b/packages/snaps-utils/src/manifest/validation.test.ts @@ -206,6 +206,7 @@ describe('isSnapManifest', () => { { name: 'foo' }, { version: '1.0.0' }, getSnapManifest({ version: 'foo bar' }), + getSnapManifest({ proposedName: '😄' }), ])('returns false for an invalid snap manifest', (value) => { expect(isSnapManifest(value)).toBe(false); }); diff --git a/packages/snaps-utils/src/manifest/validation.ts b/packages/snaps-utils/src/manifest/validation.ts index 6ed252ae52..5bd7a0c703 100644 --- a/packages/snaps-utils/src/manifest/validation.ts +++ b/packages/snaps-utils/src/manifest/validation.ts @@ -36,7 +36,12 @@ import { KeyringOriginsStruct, RpcOriginsStruct } from '../json-rpc'; import { ChainIdStruct } from '../namespace'; import { SnapIdStruct } from '../snaps'; import { mergeStructs, type InferMatching } from '../structs'; -import { NameStruct, NpmSnapFileNames, uri } from '../types'; +import { + NameStruct, + NpmSnapFileNames, + ProposedNameStruct, + uri, +} from '../types'; // BIP-43 purposes that cannot be used for entropy derivation. These are in the // string form, ending with `'`. @@ -266,7 +271,7 @@ export type InitialConnections = Infer; export const SnapManifestStruct = object({ version: VersionStruct, description: size(string(), 1, 280), - proposedName: size(string(), 1, 214), + proposedName: ProposedNameStruct, repository: optional( type({ type: size(string(), 1, Infinity), diff --git a/packages/snaps-utils/src/types.test.ts b/packages/snaps-utils/src/types.test.ts index abfb636ae7..fee3347ae2 100644 --- a/packages/snaps-utils/src/types.test.ts +++ b/packages/snaps-utils/src/types.test.ts @@ -1,6 +1,19 @@ import { enums, is, literal } from '@metamask/superstruct'; -import { isValidUrl, uri } from './types'; +import { ProposedNameStruct, isValidUrl, uri } from './types'; + +describe('ProposedNameStruct', () => { + it.each(['foo', 'bar baz', 'foo-bar', '@foo/bar', '@^_-()[]'])( + 'accepts %p', + (value) => { + expect(is(value, ProposedNameStruct)).toBe(true); + }, + ); + + it.each([1, '', '😄', String.fromCharCode(8206)])('rejects %p', (value) => { + expect(is(value, ProposedNameStruct)).toBe(false); + }); +}); describe.each([ isValidUrl, diff --git a/packages/snaps-utils/src/types.ts b/packages/snaps-utils/src/types.ts index abbba8b2e6..c021ba8963 100644 --- a/packages/snaps-utils/src/types.ts +++ b/packages/snaps-utils/src/types.ts @@ -34,6 +34,12 @@ export const NameStruct = size( 214, ); +/** + * A struct that matches anything from ASCII index 32 (space) to 126 (~), i.e., + * all printable ASCII characters, between 1 and 214 characters long. + */ +export const ProposedNameStruct = size(pattern(string(), /^[ -~]+$/u), 1, 214); + // Note we use `type` instead of `object` here, because the latter does not // allow unknown keys. export const NpmSnapPackageJsonStruct = type({ From dccbdaffb6d706db335e62fb601ff1f925af9e76 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Fri, 4 Oct 2024 16:59:26 +0200 Subject: [PATCH 2/2] Update coverage --- packages/snaps-utils/coverage.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/snaps-utils/coverage.json b/packages/snaps-utils/coverage.json index 7e74294584..1cf403086b 100644 --- a/packages/snaps-utils/coverage.json +++ b/packages/snaps-utils/coverage.json @@ -2,5 +2,5 @@ "branches": 99.74, "functions": 98.91, "lines": 99.45, - "statements": 96.3 + "statements": 96.31 }