@metamask/snaps-controllers 17.1.2 → 17.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/dist/interface/SnapInterfaceController.cjs +8 -3
  3. package/dist/interface/SnapInterfaceController.cjs.map +1 -1
  4. package/dist/interface/SnapInterfaceController.d.cts +2 -1
  5. package/dist/interface/SnapInterfaceController.d.cts.map +1 -1
  6. package/dist/interface/SnapInterfaceController.d.mts +2 -1
  7. package/dist/interface/SnapInterfaceController.d.mts.map +1 -1
  8. package/dist/interface/SnapInterfaceController.mjs +8 -3
  9. package/dist/interface/SnapInterfaceController.mjs.map +1 -1
  10. package/dist/services/AbstractExecutionService.cjs +4 -5
  11. package/dist/services/AbstractExecutionService.cjs.map +1 -1
  12. package/dist/services/AbstractExecutionService.d.cts +3 -2
  13. package/dist/services/AbstractExecutionService.d.cts.map +1 -1
  14. package/dist/services/AbstractExecutionService.d.mts +3 -2
  15. package/dist/services/AbstractExecutionService.d.mts.map +1 -1
  16. package/dist/services/AbstractExecutionService.mjs +4 -5
  17. package/dist/services/AbstractExecutionService.mjs.map +1 -1
  18. package/dist/services/iframe/IframeExecutionService.cjs +12 -2
  19. package/dist/services/iframe/IframeExecutionService.cjs.map +1 -1
  20. package/dist/services/iframe/IframeExecutionService.d.cts +1 -1
  21. package/dist/services/iframe/IframeExecutionService.d.cts.map +1 -1
  22. package/dist/services/iframe/IframeExecutionService.d.mts +1 -1
  23. package/dist/services/iframe/IframeExecutionService.d.mts.map +1 -1
  24. package/dist/services/iframe/IframeExecutionService.mjs +12 -2
  25. package/dist/services/iframe/IframeExecutionService.mjs.map +1 -1
  26. package/dist/services/node-js/NodeProcessExecutionService.cjs +1 -1
  27. package/dist/services/node-js/NodeProcessExecutionService.cjs.map +1 -1
  28. package/dist/services/node-js/NodeProcessExecutionService.d.cts +1 -1
  29. package/dist/services/node-js/NodeProcessExecutionService.d.cts.map +1 -1
  30. package/dist/services/node-js/NodeProcessExecutionService.d.mts +1 -1
  31. package/dist/services/node-js/NodeProcessExecutionService.d.mts.map +1 -1
  32. package/dist/services/node-js/NodeProcessExecutionService.mjs +1 -1
  33. package/dist/services/node-js/NodeProcessExecutionService.mjs.map +1 -1
  34. package/dist/services/webview/WebViewExecutionService.cjs +1 -1
  35. package/dist/services/webview/WebViewExecutionService.cjs.map +1 -1
  36. package/dist/services/webview/WebViewExecutionService.d.cts +1 -1
  37. package/dist/services/webview/WebViewExecutionService.d.cts.map +1 -1
  38. package/dist/services/webview/WebViewExecutionService.d.mts +1 -1
  39. package/dist/services/webview/WebViewExecutionService.d.mts.map +1 -1
  40. package/dist/services/webview/WebViewExecutionService.mjs +1 -1
  41. package/dist/services/webview/WebViewExecutionService.mjs.map +1 -1
  42. package/dist/snaps/SnapController.cjs +25 -7
  43. package/dist/snaps/SnapController.cjs.map +1 -1
  44. package/dist/snaps/SnapController.d.cts +1 -0
  45. package/dist/snaps/SnapController.d.cts.map +1 -1
  46. package/dist/snaps/SnapController.d.mts +1 -0
  47. package/dist/snaps/SnapController.d.mts.map +1 -1
  48. package/dist/snaps/SnapController.mjs +25 -7
  49. package/dist/snaps/SnapController.mjs.map +1 -1
  50. package/dist/snaps/registry/json.cjs +19 -2
  51. package/dist/snaps/registry/json.cjs.map +1 -1
  52. package/dist/snaps/registry/json.d.cts +1 -0
  53. package/dist/snaps/registry/json.d.cts.map +1 -1
  54. package/dist/snaps/registry/json.d.mts +1 -0
  55. package/dist/snaps/registry/json.d.mts.map +1 -1
  56. package/dist/snaps/registry/json.mjs +19 -2
  57. package/dist/snaps/registry/json.mjs.map +1 -1
  58. package/package.json +8 -7
@@ -12,6 +12,7 @@ const DEFAULT_PUBLIC_KEY = '0x025b65308f0f0fb8bc7f7ff87bfc296e0330eee5d3c1d1ee4a
12
12
  const controllerName = 'SnapsRegistry';
13
13
  const defaultState = {
14
14
  database: null,
15
+ signature: null,
15
16
  lastUpdated: null,
16
17
  databaseUnavailable: false,
17
18
  };
@@ -36,6 +37,12 @@ class JsonSnapsRegistry extends base_controller_1.BaseController {
36
37
  includeInDebugSnapshot: false,
37
38
  usedInUi: true,
38
39
  },
40
+ signature: {
41
+ includeInStateLogs: true,
42
+ persist: true,
43
+ includeInDebugSnapshot: true,
44
+ usedInUi: false,
45
+ },
39
46
  lastUpdated: {
40
47
  includeInStateLogs: true,
41
48
  persist: true,
@@ -104,11 +111,21 @@ class JsonSnapsRegistry extends base_controller_1.BaseController {
104
111
  this.#safeFetch(this.#url.registry),
105
112
  this.#safeFetch(this.#url.signature),
106
113
  ]);
107
- await this.#verifySignature(database, signature);
114
+ const signatureJson = JSON.parse(signature);
115
+ // If the signature matches the existing state, we can skip verification and don't need to update the database.
116
+ if (signatureJson.signature === this.state.signature) {
117
+ this.update((state) => {
118
+ state.lastUpdated = Date.now();
119
+ state.databaseUnavailable = false;
120
+ });
121
+ return;
122
+ }
123
+ await this.#verifySignature(database, signatureJson);
108
124
  this.update((state) => {
109
125
  state.database = JSON.parse(database);
110
126
  state.lastUpdated = Date.now();
111
127
  state.databaseUnavailable = false;
128
+ state.signature = signatureJson.signature;
112
129
  });
113
130
  }
114
131
  catch {
@@ -227,7 +244,7 @@ class JsonSnapsRegistry extends base_controller_1.BaseController {
227
244
  (0, utils_1.assert)(this.#publicKey, 'No public key provided.');
228
245
  const valid = await (0, snaps_registry_1.verify)({
229
246
  registry: database,
230
- signature: JSON.parse(signature),
247
+ signature,
231
248
  publicKey: this.#publicKey,
232
249
  });
233
250
  (0, utils_1.assert)(valid, 'Invalid registry signature.');
@@ -1 +1 @@
1
- {"version":3,"file":"json.cjs","sourceRoot":"","sources":["../../../src/snaps/registry/json.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { SnapsRegistryDatabase } from '@metamask/snaps-registry';\nimport { verify } from '@metamask/snaps-registry';\nimport { getTargetVersion } from '@metamask/snaps-utils';\nimport type { Hex, SemVerRange, SemVerVersion } from '@metamask/utils';\nimport {\n assert,\n assertIsSemVerRange,\n Duration,\n inMilliseconds,\n satisfiesVersionRange,\n} from '@metamask/utils';\n\nimport type {\n SnapsRegistry,\n SnapsRegistryInfo,\n SnapsRegistryMetadata,\n SnapsRegistryRequest,\n SnapsRegistryResult,\n} from './registry';\nimport { SnapsRegistryStatus } from './registry';\n\nconst SNAP_REGISTRY_URL =\n 'https://acl.execution.metamask.io/latest/registry.json';\n\nconst SNAP_REGISTRY_SIGNATURE_URL =\n 'https://acl.execution.metamask.io/latest/signature.json';\n\nconst DEFAULT_PUBLIC_KEY =\n '0x025b65308f0f0fb8bc7f7ff87bfc296e0330eee5d3c1d1ee4a048b2fd6a86fa0a6';\n\ntype JsonSnapsRegistryUrl = {\n registry: string;\n signature: string;\n};\n\nexport type ClientConfig = {\n type: 'extension' | 'mobile';\n version: SemVerVersion;\n};\n\nexport type JsonSnapsRegistryArgs = {\n messenger: SnapsRegistryMessenger;\n state?: SnapsRegistryState;\n fetchFunction?: typeof fetch;\n url?: JsonSnapsRegistryUrl;\n recentFetchThreshold?: number;\n refetchOnAllowlistMiss?: boolean;\n publicKey?: Hex;\n clientConfig: ClientConfig;\n};\n\nexport type GetResult = {\n type: `${typeof controllerName}:get`;\n handler: SnapsRegistry['get'];\n};\n\nexport type ResolveVersion = {\n type: `${typeof controllerName}:resolveVersion`;\n handler: SnapsRegistry['resolveVersion'];\n};\n\nexport type GetMetadata = {\n type: `${typeof controllerName}:getMetadata`;\n handler: SnapsRegistry['getMetadata'];\n};\n\nexport type Update = {\n type: `${typeof controllerName}:update`;\n handler: SnapsRegistry['update'];\n};\n\nexport type SnapsRegistryGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n SnapsRegistryState\n>;\n\nexport type SnapsRegistryActions =\n | SnapsRegistryGetStateAction\n | GetResult\n | GetMetadata\n | Update\n | ResolveVersion;\n\nexport type SnapsRegistryStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n SnapsRegistryState\n>;\n\nexport type SnapsRegistryEvents = SnapsRegistryStateChangeEvent;\n\nexport type SnapsRegistryMessenger = Messenger<\n 'SnapsRegistry',\n SnapsRegistryActions,\n SnapsRegistryEvents\n>;\n\nexport type SnapsRegistryState = {\n database: SnapsRegistryDatabase | null;\n lastUpdated: number | null;\n databaseUnavailable: boolean;\n};\n\nconst controllerName = 'SnapsRegistry';\n\nconst defaultState = {\n database: null,\n lastUpdated: null,\n databaseUnavailable: false,\n};\n\nexport class JsonSnapsRegistry extends BaseController<\n typeof controllerName,\n SnapsRegistryState,\n SnapsRegistryMessenger\n> {\n readonly #url: JsonSnapsRegistryUrl;\n\n readonly #publicKey: Hex;\n\n readonly #clientConfig: ClientConfig;\n\n readonly #fetchFunction: typeof fetch;\n\n readonly #recentFetchThreshold: number;\n\n readonly #refetchOnAllowlistMiss: boolean;\n\n #currentUpdate: Promise<void> | null;\n\n constructor({\n messenger,\n state,\n url = {\n registry: SNAP_REGISTRY_URL,\n signature: SNAP_REGISTRY_SIGNATURE_URL,\n },\n publicKey = DEFAULT_PUBLIC_KEY,\n clientConfig,\n fetchFunction = globalThis.fetch.bind(undefined),\n recentFetchThreshold = inMilliseconds(5, Duration.Minute),\n refetchOnAllowlistMiss = true,\n }: JsonSnapsRegistryArgs) {\n super({\n messenger,\n metadata: {\n database: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n lastUpdated: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n databaseUnavailable: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n },\n name: controllerName,\n state: {\n ...defaultState,\n ...state,\n },\n });\n this.#url = url;\n this.#publicKey = publicKey;\n this.#clientConfig = clientConfig;\n this.#fetchFunction = fetchFunction;\n this.#recentFetchThreshold = recentFetchThreshold;\n this.#refetchOnAllowlistMiss = refetchOnAllowlistMiss;\n this.#currentUpdate = null;\n\n this.messenger.registerActionHandler('SnapsRegistry:get', async (...args) =>\n this.#get(...args),\n );\n\n this.messenger.registerActionHandler(\n 'SnapsRegistry:getMetadata',\n (...args) => this.#getMetadata(...args),\n );\n\n this.messenger.registerActionHandler(\n 'SnapsRegistry:resolveVersion',\n async (...args) => this.#resolveVersion(...args),\n );\n\n this.messenger.registerActionHandler('SnapsRegistry:update', async () =>\n this.#triggerUpdate(),\n );\n }\n\n #wasRecentlyFetched() {\n return (\n this.state.lastUpdated &&\n Date.now() - this.state.lastUpdated < this.#recentFetchThreshold\n );\n }\n\n /**\n * Triggers an update of the registry database.\n *\n * If an existing update is in progress this function will await that update.\n */\n async #triggerUpdate() {\n // If an update is ongoing, wait for that.\n if (this.#currentUpdate) {\n await this.#currentUpdate;\n return;\n }\n // If no update exists, create promise and store globally.\n if (this.#currentUpdate === null) {\n this.#currentUpdate = this.#update();\n }\n await this.#currentUpdate;\n this.#currentUpdate = null;\n }\n\n /**\n * Updates the registry database if the registry hasn't been updated recently.\n *\n * NOTE: SHOULD NOT be called directly, instead `triggerUpdate` should be used.\n */\n async #update() {\n // No-op if we recently fetched the registry.\n if (this.#wasRecentlyFetched()) {\n return;\n }\n\n try {\n const [database, signature] = await Promise.all([\n this.#safeFetch(this.#url.registry),\n this.#safeFetch(this.#url.signature),\n ]);\n\n await this.#verifySignature(database, signature);\n\n this.update((state) => {\n state.database = JSON.parse(database);\n state.lastUpdated = Date.now();\n state.databaseUnavailable = false;\n });\n } catch {\n // Ignore\n this.update((state) => {\n state.databaseUnavailable = true;\n });\n }\n }\n\n async #getDatabase(): Promise<SnapsRegistryDatabase | null> {\n if (this.state.database === null) {\n await this.#triggerUpdate();\n }\n\n return this.state.database;\n }\n\n async #getSingle(\n snapId: string,\n snapInfo: SnapsRegistryInfo,\n refetch = false,\n ): Promise<SnapsRegistryResult> {\n const database = await this.#getDatabase();\n\n const blockedEntry = database?.blockedSnaps.find((blocked) => {\n if ('id' in blocked) {\n return (\n blocked.id === snapId &&\n satisfiesVersionRange(snapInfo.version, blocked.versionRange)\n );\n }\n\n return blocked.checksum === snapInfo.checksum;\n });\n\n if (blockedEntry) {\n return {\n status: SnapsRegistryStatus.Blocked,\n reason: blockedEntry.reason,\n };\n }\n\n const verified = database?.verifiedSnaps[snapId];\n const version = verified?.versions?.[snapInfo.version];\n const clientRange = version?.clientVersions?.[this.#clientConfig.type];\n const isCompatible =\n !clientRange ||\n satisfiesVersionRange(this.#clientConfig.version, clientRange);\n if (version && version.checksum === snapInfo.checksum && isCompatible) {\n return { status: SnapsRegistryStatus.Verified };\n }\n // For now, if we have an allowlist miss, we can refetch once and try again.\n if (this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#getSingle(snapId, snapInfo, true);\n }\n return {\n status: this.state.databaseUnavailable\n ? SnapsRegistryStatus.Unavailable\n : SnapsRegistryStatus.Unverified,\n };\n }\n\n async #get(\n snaps: SnapsRegistryRequest,\n ): Promise<Record<string, SnapsRegistryResult>> {\n return Object.entries(snaps).reduce<\n Promise<Record<string, SnapsRegistryResult>>\n >(async (previousPromise, [snapId, snapInfo]) => {\n const result = await this.#getSingle(snapId, snapInfo);\n const acc = await previousPromise;\n acc[snapId] = result;\n return acc;\n }, Promise.resolve({}));\n }\n\n /**\n * Find an allowlisted version within a specified version range. Otherwise return the version range itself.\n *\n * @param snapId - The ID of the snap we are trying to resolve a version for.\n * @param versionRange - The version range.\n * @param refetch - An optional flag used to determine if we are refetching the registry.\n * @returns An allowlisted version within the specified version range if available otherwise returns the input version range.\n */\n async #resolveVersion(\n snapId: string,\n versionRange: SemVerRange,\n refetch = false,\n ): Promise<SemVerRange> {\n const database = await this.#getDatabase();\n const versions = database?.verifiedSnaps[snapId]?.versions ?? null;\n\n if (!versions && this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#resolveVersion(snapId, versionRange, true);\n }\n\n // If we cannot narrow down the version range we return the unaltered version range.\n if (!versions) {\n return versionRange;\n }\n\n const compatibleVersions = Object.entries(versions).reduce<SemVerVersion[]>(\n (accumulator, [version, metadata]) => {\n const clientRange = metadata.clientVersions?.[this.#clientConfig.type];\n if (\n !clientRange ||\n satisfiesVersionRange(this.#clientConfig.version, clientRange)\n ) {\n accumulator.push(version as SemVerVersion);\n }\n\n return accumulator;\n },\n [],\n );\n\n const targetVersion = getTargetVersion(compatibleVersions, versionRange);\n\n if (!targetVersion && this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#resolveVersion(snapId, versionRange, true);\n }\n\n // If we cannot narrow down the version range we return the unaltered version range.\n if (!targetVersion) {\n return versionRange;\n }\n\n // A semver version is technically also a valid semver range.\n assertIsSemVerRange(targetVersion);\n return targetVersion;\n }\n\n /**\n * Get metadata for the given snap ID, if available, without updating registry.\n *\n * @param snapId - The ID of the snap to get metadata for.\n * @returns The metadata for the given snap ID, or `null` if the snap is not\n * verified.\n */\n #getMetadata(snapId: string): SnapsRegistryMetadata | null {\n return this.state?.database?.verifiedSnaps[snapId]?.metadata ?? null;\n }\n\n /**\n * Verify the signature of the registry.\n *\n * @param database - The registry database.\n * @param signature - The signature of the registry.\n * @throws If the signature is invalid.\n */\n async #verifySignature(database: string, signature: string) {\n assert(this.#publicKey, 'No public key provided.');\n\n const valid = await verify({\n registry: database,\n signature: JSON.parse(signature),\n publicKey: this.#publicKey,\n });\n\n assert(valid, 'Invalid registry signature.');\n }\n\n /**\n * Fetch the given URL, throwing if the response is not OK.\n *\n * @param url - The URL to fetch.\n * @returns The response body.\n * @private\n */\n async #safeFetch(url: string) {\n const response = await this.#fetchFunction(url, { cache: 'no-cache' });\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}.`);\n }\n\n return await response.text();\n }\n}\n"]}
1
+ {"version":3,"file":"json.cjs","sourceRoot":"","sources":["../../../src/snaps/registry/json.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n SnapsRegistryDatabase,\n SignatureStruct,\n} from '@metamask/snaps-registry';\nimport { verify } from '@metamask/snaps-registry';\nimport { getTargetVersion } from '@metamask/snaps-utils';\nimport type { Infer } from '@metamask/superstruct';\nimport type { Hex, SemVerRange, SemVerVersion } from '@metamask/utils';\nimport {\n assert,\n assertIsSemVerRange,\n Duration,\n inMilliseconds,\n satisfiesVersionRange,\n} from '@metamask/utils';\n\nimport type {\n SnapsRegistry,\n SnapsRegistryInfo,\n SnapsRegistryMetadata,\n SnapsRegistryRequest,\n SnapsRegistryResult,\n} from './registry';\nimport { SnapsRegistryStatus } from './registry';\n\nconst SNAP_REGISTRY_URL =\n 'https://acl.execution.metamask.io/latest/registry.json';\n\nconst SNAP_REGISTRY_SIGNATURE_URL =\n 'https://acl.execution.metamask.io/latest/signature.json';\n\nconst DEFAULT_PUBLIC_KEY =\n '0x025b65308f0f0fb8bc7f7ff87bfc296e0330eee5d3c1d1ee4a048b2fd6a86fa0a6';\n\ntype JsonSnapsRegistryUrl = {\n registry: string;\n signature: string;\n};\n\nexport type ClientConfig = {\n type: 'extension' | 'mobile';\n version: SemVerVersion;\n};\n\nexport type JsonSnapsRegistryArgs = {\n messenger: SnapsRegistryMessenger;\n state?: SnapsRegistryState;\n fetchFunction?: typeof fetch;\n url?: JsonSnapsRegistryUrl;\n recentFetchThreshold?: number;\n refetchOnAllowlistMiss?: boolean;\n publicKey?: Hex;\n clientConfig: ClientConfig;\n};\n\nexport type GetResult = {\n type: `${typeof controllerName}:get`;\n handler: SnapsRegistry['get'];\n};\n\nexport type ResolveVersion = {\n type: `${typeof controllerName}:resolveVersion`;\n handler: SnapsRegistry['resolveVersion'];\n};\n\nexport type GetMetadata = {\n type: `${typeof controllerName}:getMetadata`;\n handler: SnapsRegistry['getMetadata'];\n};\n\nexport type Update = {\n type: `${typeof controllerName}:update`;\n handler: SnapsRegistry['update'];\n};\n\nexport type SnapsRegistryGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n SnapsRegistryState\n>;\n\nexport type SnapsRegistryActions =\n | SnapsRegistryGetStateAction\n | GetResult\n | GetMetadata\n | Update\n | ResolveVersion;\n\nexport type SnapsRegistryStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n SnapsRegistryState\n>;\n\nexport type SnapsRegistryEvents = SnapsRegistryStateChangeEvent;\n\nexport type SnapsRegistryMessenger = Messenger<\n 'SnapsRegistry',\n SnapsRegistryActions,\n SnapsRegistryEvents\n>;\n\nexport type SnapsRegistryState = {\n database: SnapsRegistryDatabase | null;\n signature: string | null;\n lastUpdated: number | null;\n databaseUnavailable: boolean;\n};\n\nconst controllerName = 'SnapsRegistry';\n\nconst defaultState = {\n database: null,\n signature: null,\n lastUpdated: null,\n databaseUnavailable: false,\n};\n\nexport class JsonSnapsRegistry extends BaseController<\n typeof controllerName,\n SnapsRegistryState,\n SnapsRegistryMessenger\n> {\n readonly #url: JsonSnapsRegistryUrl;\n\n readonly #publicKey: Hex;\n\n readonly #clientConfig: ClientConfig;\n\n readonly #fetchFunction: typeof fetch;\n\n readonly #recentFetchThreshold: number;\n\n readonly #refetchOnAllowlistMiss: boolean;\n\n #currentUpdate: Promise<void> | null;\n\n constructor({\n messenger,\n state,\n url = {\n registry: SNAP_REGISTRY_URL,\n signature: SNAP_REGISTRY_SIGNATURE_URL,\n },\n publicKey = DEFAULT_PUBLIC_KEY,\n clientConfig,\n fetchFunction = globalThis.fetch.bind(undefined),\n recentFetchThreshold = inMilliseconds(5, Duration.Minute),\n refetchOnAllowlistMiss = true,\n }: JsonSnapsRegistryArgs) {\n super({\n messenger,\n metadata: {\n database: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n signature: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n lastUpdated: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n databaseUnavailable: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n },\n name: controllerName,\n state: {\n ...defaultState,\n ...state,\n },\n });\n this.#url = url;\n this.#publicKey = publicKey;\n this.#clientConfig = clientConfig;\n this.#fetchFunction = fetchFunction;\n this.#recentFetchThreshold = recentFetchThreshold;\n this.#refetchOnAllowlistMiss = refetchOnAllowlistMiss;\n this.#currentUpdate = null;\n\n this.messenger.registerActionHandler('SnapsRegistry:get', async (...args) =>\n this.#get(...args),\n );\n\n this.messenger.registerActionHandler(\n 'SnapsRegistry:getMetadata',\n (...args) => this.#getMetadata(...args),\n );\n\n this.messenger.registerActionHandler(\n 'SnapsRegistry:resolveVersion',\n async (...args) => this.#resolveVersion(...args),\n );\n\n this.messenger.registerActionHandler('SnapsRegistry:update', async () =>\n this.#triggerUpdate(),\n );\n }\n\n #wasRecentlyFetched() {\n return (\n this.state.lastUpdated &&\n Date.now() - this.state.lastUpdated < this.#recentFetchThreshold\n );\n }\n\n /**\n * Triggers an update of the registry database.\n *\n * If an existing update is in progress this function will await that update.\n */\n async #triggerUpdate() {\n // If an update is ongoing, wait for that.\n if (this.#currentUpdate) {\n await this.#currentUpdate;\n return;\n }\n // If no update exists, create promise and store globally.\n if (this.#currentUpdate === null) {\n this.#currentUpdate = this.#update();\n }\n await this.#currentUpdate;\n this.#currentUpdate = null;\n }\n\n /**\n * Updates the registry database if the registry hasn't been updated recently.\n *\n * NOTE: SHOULD NOT be called directly, instead `triggerUpdate` should be used.\n */\n async #update() {\n // No-op if we recently fetched the registry.\n if (this.#wasRecentlyFetched()) {\n return;\n }\n\n try {\n const [database, signature] = await Promise.all([\n this.#safeFetch(this.#url.registry),\n this.#safeFetch(this.#url.signature),\n ]);\n\n const signatureJson = JSON.parse(signature);\n\n // If the signature matches the existing state, we can skip verification and don't need to update the database.\n if (signatureJson.signature === this.state.signature) {\n this.update((state) => {\n state.lastUpdated = Date.now();\n state.databaseUnavailable = false;\n });\n return;\n }\n\n await this.#verifySignature(database, signatureJson);\n\n this.update((state) => {\n state.database = JSON.parse(database);\n state.lastUpdated = Date.now();\n state.databaseUnavailable = false;\n state.signature = signatureJson.signature;\n });\n } catch {\n // Ignore\n this.update((state) => {\n state.databaseUnavailable = true;\n });\n }\n }\n\n async #getDatabase(): Promise<SnapsRegistryDatabase | null> {\n if (this.state.database === null) {\n await this.#triggerUpdate();\n }\n\n return this.state.database;\n }\n\n async #getSingle(\n snapId: string,\n snapInfo: SnapsRegistryInfo,\n refetch = false,\n ): Promise<SnapsRegistryResult> {\n const database = await this.#getDatabase();\n\n const blockedEntry = database?.blockedSnaps.find((blocked) => {\n if ('id' in blocked) {\n return (\n blocked.id === snapId &&\n satisfiesVersionRange(snapInfo.version, blocked.versionRange)\n );\n }\n\n return blocked.checksum === snapInfo.checksum;\n });\n\n if (blockedEntry) {\n return {\n status: SnapsRegistryStatus.Blocked,\n reason: blockedEntry.reason,\n };\n }\n\n const verified = database?.verifiedSnaps[snapId];\n const version = verified?.versions?.[snapInfo.version];\n const clientRange = version?.clientVersions?.[this.#clientConfig.type];\n const isCompatible =\n !clientRange ||\n satisfiesVersionRange(this.#clientConfig.version, clientRange);\n if (version && version.checksum === snapInfo.checksum && isCompatible) {\n return { status: SnapsRegistryStatus.Verified };\n }\n // For now, if we have an allowlist miss, we can refetch once and try again.\n if (this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#getSingle(snapId, snapInfo, true);\n }\n return {\n status: this.state.databaseUnavailable\n ? SnapsRegistryStatus.Unavailable\n : SnapsRegistryStatus.Unverified,\n };\n }\n\n async #get(\n snaps: SnapsRegistryRequest,\n ): Promise<Record<string, SnapsRegistryResult>> {\n return Object.entries(snaps).reduce<\n Promise<Record<string, SnapsRegistryResult>>\n >(async (previousPromise, [snapId, snapInfo]) => {\n const result = await this.#getSingle(snapId, snapInfo);\n const acc = await previousPromise;\n acc[snapId] = result;\n return acc;\n }, Promise.resolve({}));\n }\n\n /**\n * Find an allowlisted version within a specified version range. Otherwise return the version range itself.\n *\n * @param snapId - The ID of the snap we are trying to resolve a version for.\n * @param versionRange - The version range.\n * @param refetch - An optional flag used to determine if we are refetching the registry.\n * @returns An allowlisted version within the specified version range if available otherwise returns the input version range.\n */\n async #resolveVersion(\n snapId: string,\n versionRange: SemVerRange,\n refetch = false,\n ): Promise<SemVerRange> {\n const database = await this.#getDatabase();\n const versions = database?.verifiedSnaps[snapId]?.versions ?? null;\n\n if (!versions && this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#resolveVersion(snapId, versionRange, true);\n }\n\n // If we cannot narrow down the version range we return the unaltered version range.\n if (!versions) {\n return versionRange;\n }\n\n const compatibleVersions = Object.entries(versions).reduce<SemVerVersion[]>(\n (accumulator, [version, metadata]) => {\n const clientRange = metadata.clientVersions?.[this.#clientConfig.type];\n if (\n !clientRange ||\n satisfiesVersionRange(this.#clientConfig.version, clientRange)\n ) {\n accumulator.push(version as SemVerVersion);\n }\n\n return accumulator;\n },\n [],\n );\n\n const targetVersion = getTargetVersion(compatibleVersions, versionRange);\n\n if (!targetVersion && this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#resolveVersion(snapId, versionRange, true);\n }\n\n // If we cannot narrow down the version range we return the unaltered version range.\n if (!targetVersion) {\n return versionRange;\n }\n\n // A semver version is technically also a valid semver range.\n assertIsSemVerRange(targetVersion);\n return targetVersion;\n }\n\n /**\n * Get metadata for the given snap ID, if available, without updating registry.\n *\n * @param snapId - The ID of the snap to get metadata for.\n * @returns The metadata for the given snap ID, or `null` if the snap is not\n * verified.\n */\n #getMetadata(snapId: string): SnapsRegistryMetadata | null {\n return this.state?.database?.verifiedSnaps[snapId]?.metadata ?? null;\n }\n\n /**\n * Verify the signature of the registry.\n *\n * @param database - The registry database.\n * @param signature - The signature of the registry.\n * @throws If the signature is invalid.\n */\n async #verifySignature(\n database: string,\n signature: Infer<typeof SignatureStruct>,\n ) {\n assert(this.#publicKey, 'No public key provided.');\n\n const valid = await verify({\n registry: database,\n signature,\n publicKey: this.#publicKey,\n });\n\n assert(valid, 'Invalid registry signature.');\n }\n\n /**\n * Fetch the given URL, throwing if the response is not OK.\n *\n * @param url - The URL to fetch.\n * @returns The response body.\n * @private\n */\n async #safeFetch(url: string) {\n const response = await this.#fetchFunction(url, { cache: 'no-cache' });\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}.`);\n }\n\n return await response.text();\n }\n}\n"]}
@@ -45,6 +45,7 @@ export type SnapsRegistryEvents = SnapsRegistryStateChangeEvent;
45
45
  export type SnapsRegistryMessenger = Messenger<'SnapsRegistry', SnapsRegistryActions, SnapsRegistryEvents>;
46
46
  export type SnapsRegistryState = {
47
47
  database: SnapsRegistryDatabase | null;
48
+ signature: string | null;
48
49
  lastUpdated: number | null;
49
50
  databaseUnavailable: boolean;
50
51
  };
@@ -1 +1 @@
1
- {"version":3,"file":"json.d.cts","sourceRoot":"","sources":["../../../src/snaps/registry/json.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC3B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,iCAAiC;AAGtE,OAAO,KAAK,EAAE,GAAG,EAAe,aAAa,EAAE,wBAAwB;AASvE,OAAO,KAAK,EACV,aAAa,EAKd,uBAAmB;AAYpB,KAAK,oBAAoB,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC7B,OAAO,EAAE,aAAa,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,sBAAsB,CAAC;IAClC,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,KAAK,CAAC;IAC7B,GAAG,CAAC,EAAE,oBAAoB,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,YAAY,EAAE,YAAY,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,GAAG,OAAO,cAAc,MAAM,CAAC;IACrC,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,GAAG,OAAO,cAAc,iBAAiB,CAAC;IAChD,OAAO,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,GAAG,OAAO,cAAc,cAAc,CAAC;IAC7C,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,GAAG,OAAO,cAAc,SAAS,CAAC;IACxC,OAAO,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG,wBAAwB,CAChE,OAAO,cAAc,EACrB,kBAAkB,CACnB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAC5B,2BAA2B,GAC3B,SAAS,GACT,WAAW,GACX,MAAM,GACN,cAAc,CAAC;AAEnB,MAAM,MAAM,6BAA6B,GAAG,0BAA0B,CACpE,OAAO,cAAc,EACrB,kBAAkB,CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,6BAA6B,CAAC;AAEhE,MAAM,MAAM,sBAAsB,GAAG,SAAS,CAC5C,eAAe,EACf,oBAAoB,EACpB,mBAAmB,CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACvC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mBAAmB,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,QAAA,MAAM,cAAc,kBAAkB,CAAC;AAQvC,qBAAa,iBAAkB,SAAQ,cAAc,CACnD,OAAO,cAAc,EACrB,kBAAkB,EAClB,sBAAsB,CACvB;;gBAea,EACV,SAAS,EACT,KAAK,EACL,GAGC,EACD,SAA8B,EAC9B,YAAY,EACZ,aAAgD,EAChD,oBAAyD,EACzD,sBAA6B,GAC9B,EAAE,qBAAqB;CA4RzB"}
1
+ {"version":3,"file":"json.d.cts","sourceRoot":"","sources":["../../../src/snaps/registry/json.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC3B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EACV,qBAAqB,EAEtB,iCAAiC;AAIlC,OAAO,KAAK,EAAE,GAAG,EAAe,aAAa,EAAE,wBAAwB;AASvE,OAAO,KAAK,EACV,aAAa,EAKd,uBAAmB;AAYpB,KAAK,oBAAoB,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC7B,OAAO,EAAE,aAAa,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,sBAAsB,CAAC;IAClC,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,KAAK,CAAC;IAC7B,GAAG,CAAC,EAAE,oBAAoB,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,YAAY,EAAE,YAAY,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,GAAG,OAAO,cAAc,MAAM,CAAC;IACrC,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,GAAG,OAAO,cAAc,iBAAiB,CAAC;IAChD,OAAO,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,GAAG,OAAO,cAAc,cAAc,CAAC;IAC7C,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,GAAG,OAAO,cAAc,SAAS,CAAC;IACxC,OAAO,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG,wBAAwB,CAChE,OAAO,cAAc,EACrB,kBAAkB,CACnB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAC5B,2BAA2B,GAC3B,SAAS,GACT,WAAW,GACX,MAAM,GACN,cAAc,CAAC;AAEnB,MAAM,MAAM,6BAA6B,GAAG,0BAA0B,CACpE,OAAO,cAAc,EACrB,kBAAkB,CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,6BAA6B,CAAC;AAEhE,MAAM,MAAM,sBAAsB,GAAG,SAAS,CAC5C,eAAe,EACf,oBAAoB,EACpB,mBAAmB,CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACvC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mBAAmB,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,QAAA,MAAM,cAAc,kBAAkB,CAAC;AASvC,qBAAa,iBAAkB,SAAQ,cAAc,CACnD,OAAO,cAAc,EACrB,kBAAkB,EAClB,sBAAsB,CACvB;;gBAea,EACV,SAAS,EACT,KAAK,EACL,GAGC,EACD,SAA8B,EAC9B,YAAY,EACZ,aAAgD,EAChD,oBAAyD,EACzD,sBAA6B,GAC9B,EAAE,qBAAqB;CAiTzB"}
@@ -45,6 +45,7 @@ export type SnapsRegistryEvents = SnapsRegistryStateChangeEvent;
45
45
  export type SnapsRegistryMessenger = Messenger<'SnapsRegistry', SnapsRegistryActions, SnapsRegistryEvents>;
46
46
  export type SnapsRegistryState = {
47
47
  database: SnapsRegistryDatabase | null;
48
+ signature: string | null;
48
49
  lastUpdated: number | null;
49
50
  databaseUnavailable: boolean;
50
51
  };
@@ -1 +1 @@
1
- {"version":3,"file":"json.d.mts","sourceRoot":"","sources":["../../../src/snaps/registry/json.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC3B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,iCAAiC;AAGtE,OAAO,KAAK,EAAE,GAAG,EAAe,aAAa,EAAE,wBAAwB;AASvE,OAAO,KAAK,EACV,aAAa,EAKd,uBAAmB;AAYpB,KAAK,oBAAoB,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC7B,OAAO,EAAE,aAAa,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,sBAAsB,CAAC;IAClC,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,KAAK,CAAC;IAC7B,GAAG,CAAC,EAAE,oBAAoB,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,YAAY,EAAE,YAAY,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,GAAG,OAAO,cAAc,MAAM,CAAC;IACrC,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,GAAG,OAAO,cAAc,iBAAiB,CAAC;IAChD,OAAO,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,GAAG,OAAO,cAAc,cAAc,CAAC;IAC7C,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,GAAG,OAAO,cAAc,SAAS,CAAC;IACxC,OAAO,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG,wBAAwB,CAChE,OAAO,cAAc,EACrB,kBAAkB,CACnB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAC5B,2BAA2B,GAC3B,SAAS,GACT,WAAW,GACX,MAAM,GACN,cAAc,CAAC;AAEnB,MAAM,MAAM,6BAA6B,GAAG,0BAA0B,CACpE,OAAO,cAAc,EACrB,kBAAkB,CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,6BAA6B,CAAC;AAEhE,MAAM,MAAM,sBAAsB,GAAG,SAAS,CAC5C,eAAe,EACf,oBAAoB,EACpB,mBAAmB,CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACvC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mBAAmB,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,QAAA,MAAM,cAAc,kBAAkB,CAAC;AAQvC,qBAAa,iBAAkB,SAAQ,cAAc,CACnD,OAAO,cAAc,EACrB,kBAAkB,EAClB,sBAAsB,CACvB;;gBAea,EACV,SAAS,EACT,KAAK,EACL,GAGC,EACD,SAA8B,EAC9B,YAAY,EACZ,aAAgD,EAChD,oBAAyD,EACzD,sBAA6B,GAC9B,EAAE,qBAAqB;CA4RzB"}
1
+ {"version":3,"file":"json.d.mts","sourceRoot":"","sources":["../../../src/snaps/registry/json.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC3B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EACV,qBAAqB,EAEtB,iCAAiC;AAIlC,OAAO,KAAK,EAAE,GAAG,EAAe,aAAa,EAAE,wBAAwB;AASvE,OAAO,KAAK,EACV,aAAa,EAKd,uBAAmB;AAYpB,KAAK,oBAAoB,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC7B,OAAO,EAAE,aAAa,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,sBAAsB,CAAC;IAClC,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,KAAK,CAAC;IAC7B,GAAG,CAAC,EAAE,oBAAoB,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,YAAY,EAAE,YAAY,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,GAAG,OAAO,cAAc,MAAM,CAAC;IACrC,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,GAAG,OAAO,cAAc,iBAAiB,CAAC;IAChD,OAAO,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,GAAG,OAAO,cAAc,cAAc,CAAC;IAC7C,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,GAAG,OAAO,cAAc,SAAS,CAAC;IACxC,OAAO,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG,wBAAwB,CAChE,OAAO,cAAc,EACrB,kBAAkB,CACnB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAC5B,2BAA2B,GAC3B,SAAS,GACT,WAAW,GACX,MAAM,GACN,cAAc,CAAC;AAEnB,MAAM,MAAM,6BAA6B,GAAG,0BAA0B,CACpE,OAAO,cAAc,EACrB,kBAAkB,CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,6BAA6B,CAAC;AAEhE,MAAM,MAAM,sBAAsB,GAAG,SAAS,CAC5C,eAAe,EACf,oBAAoB,EACpB,mBAAmB,CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACvC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mBAAmB,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,QAAA,MAAM,cAAc,kBAAkB,CAAC;AASvC,qBAAa,iBAAkB,SAAQ,cAAc,CACnD,OAAO,cAAc,EACrB,kBAAkB,EAClB,sBAAsB,CACvB;;gBAea,EACV,SAAS,EACT,KAAK,EACL,GAGC,EACD,SAA8B,EAC9B,YAAY,EACZ,aAAgD,EAChD,oBAAyD,EACzD,sBAA6B,GAC9B,EAAE,qBAAqB;CAiTzB"}
@@ -9,6 +9,7 @@ const DEFAULT_PUBLIC_KEY = '0x025b65308f0f0fb8bc7f7ff87bfc296e0330eee5d3c1d1ee4a
9
9
  const controllerName = 'SnapsRegistry';
10
10
  const defaultState = {
11
11
  database: null,
12
+ signature: null,
12
13
  lastUpdated: null,
13
14
  databaseUnavailable: false,
14
15
  };
@@ -33,6 +34,12 @@ export class JsonSnapsRegistry extends BaseController {
33
34
  includeInDebugSnapshot: false,
34
35
  usedInUi: true,
35
36
  },
37
+ signature: {
38
+ includeInStateLogs: true,
39
+ persist: true,
40
+ includeInDebugSnapshot: true,
41
+ usedInUi: false,
42
+ },
36
43
  lastUpdated: {
37
44
  includeInStateLogs: true,
38
45
  persist: true,
@@ -101,11 +108,21 @@ export class JsonSnapsRegistry extends BaseController {
101
108
  this.#safeFetch(this.#url.registry),
102
109
  this.#safeFetch(this.#url.signature),
103
110
  ]);
104
- await this.#verifySignature(database, signature);
111
+ const signatureJson = JSON.parse(signature);
112
+ // If the signature matches the existing state, we can skip verification and don't need to update the database.
113
+ if (signatureJson.signature === this.state.signature) {
114
+ this.update((state) => {
115
+ state.lastUpdated = Date.now();
116
+ state.databaseUnavailable = false;
117
+ });
118
+ return;
119
+ }
120
+ await this.#verifySignature(database, signatureJson);
105
121
  this.update((state) => {
106
122
  state.database = JSON.parse(database);
107
123
  state.lastUpdated = Date.now();
108
124
  state.databaseUnavailable = false;
125
+ state.signature = signatureJson.signature;
109
126
  });
110
127
  }
111
128
  catch {
@@ -224,7 +241,7 @@ export class JsonSnapsRegistry extends BaseController {
224
241
  assert(this.#publicKey, 'No public key provided.');
225
242
  const valid = await verify({
226
243
  registry: database,
227
- signature: JSON.parse(signature),
244
+ signature,
228
245
  publicKey: this.#publicKey,
229
246
  });
230
247
  assert(valid, 'Invalid registry signature.');
@@ -1 +1 @@
1
- {"version":3,"file":"json.mjs","sourceRoot":"","sources":["../../../src/snaps/registry/json.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { SnapsRegistryDatabase } from '@metamask/snaps-registry';\nimport { verify } from '@metamask/snaps-registry';\nimport { getTargetVersion } from '@metamask/snaps-utils';\nimport type { Hex, SemVerRange, SemVerVersion } from '@metamask/utils';\nimport {\n assert,\n assertIsSemVerRange,\n Duration,\n inMilliseconds,\n satisfiesVersionRange,\n} from '@metamask/utils';\n\nimport type {\n SnapsRegistry,\n SnapsRegistryInfo,\n SnapsRegistryMetadata,\n SnapsRegistryRequest,\n SnapsRegistryResult,\n} from './registry';\nimport { SnapsRegistryStatus } from './registry';\n\nconst SNAP_REGISTRY_URL =\n 'https://acl.execution.metamask.io/latest/registry.json';\n\nconst SNAP_REGISTRY_SIGNATURE_URL =\n 'https://acl.execution.metamask.io/latest/signature.json';\n\nconst DEFAULT_PUBLIC_KEY =\n '0x025b65308f0f0fb8bc7f7ff87bfc296e0330eee5d3c1d1ee4a048b2fd6a86fa0a6';\n\ntype JsonSnapsRegistryUrl = {\n registry: string;\n signature: string;\n};\n\nexport type ClientConfig = {\n type: 'extension' | 'mobile';\n version: SemVerVersion;\n};\n\nexport type JsonSnapsRegistryArgs = {\n messenger: SnapsRegistryMessenger;\n state?: SnapsRegistryState;\n fetchFunction?: typeof fetch;\n url?: JsonSnapsRegistryUrl;\n recentFetchThreshold?: number;\n refetchOnAllowlistMiss?: boolean;\n publicKey?: Hex;\n clientConfig: ClientConfig;\n};\n\nexport type GetResult = {\n type: `${typeof controllerName}:get`;\n handler: SnapsRegistry['get'];\n};\n\nexport type ResolveVersion = {\n type: `${typeof controllerName}:resolveVersion`;\n handler: SnapsRegistry['resolveVersion'];\n};\n\nexport type GetMetadata = {\n type: `${typeof controllerName}:getMetadata`;\n handler: SnapsRegistry['getMetadata'];\n};\n\nexport type Update = {\n type: `${typeof controllerName}:update`;\n handler: SnapsRegistry['update'];\n};\n\nexport type SnapsRegistryGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n SnapsRegistryState\n>;\n\nexport type SnapsRegistryActions =\n | SnapsRegistryGetStateAction\n | GetResult\n | GetMetadata\n | Update\n | ResolveVersion;\n\nexport type SnapsRegistryStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n SnapsRegistryState\n>;\n\nexport type SnapsRegistryEvents = SnapsRegistryStateChangeEvent;\n\nexport type SnapsRegistryMessenger = Messenger<\n 'SnapsRegistry',\n SnapsRegistryActions,\n SnapsRegistryEvents\n>;\n\nexport type SnapsRegistryState = {\n database: SnapsRegistryDatabase | null;\n lastUpdated: number | null;\n databaseUnavailable: boolean;\n};\n\nconst controllerName = 'SnapsRegistry';\n\nconst defaultState = {\n database: null,\n lastUpdated: null,\n databaseUnavailable: false,\n};\n\nexport class JsonSnapsRegistry extends BaseController<\n typeof controllerName,\n SnapsRegistryState,\n SnapsRegistryMessenger\n> {\n readonly #url: JsonSnapsRegistryUrl;\n\n readonly #publicKey: Hex;\n\n readonly #clientConfig: ClientConfig;\n\n readonly #fetchFunction: typeof fetch;\n\n readonly #recentFetchThreshold: number;\n\n readonly #refetchOnAllowlistMiss: boolean;\n\n #currentUpdate: Promise<void> | null;\n\n constructor({\n messenger,\n state,\n url = {\n registry: SNAP_REGISTRY_URL,\n signature: SNAP_REGISTRY_SIGNATURE_URL,\n },\n publicKey = DEFAULT_PUBLIC_KEY,\n clientConfig,\n fetchFunction = globalThis.fetch.bind(undefined),\n recentFetchThreshold = inMilliseconds(5, Duration.Minute),\n refetchOnAllowlistMiss = true,\n }: JsonSnapsRegistryArgs) {\n super({\n messenger,\n metadata: {\n database: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n lastUpdated: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n databaseUnavailable: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n },\n name: controllerName,\n state: {\n ...defaultState,\n ...state,\n },\n });\n this.#url = url;\n this.#publicKey = publicKey;\n this.#clientConfig = clientConfig;\n this.#fetchFunction = fetchFunction;\n this.#recentFetchThreshold = recentFetchThreshold;\n this.#refetchOnAllowlistMiss = refetchOnAllowlistMiss;\n this.#currentUpdate = null;\n\n this.messenger.registerActionHandler('SnapsRegistry:get', async (...args) =>\n this.#get(...args),\n );\n\n this.messenger.registerActionHandler(\n 'SnapsRegistry:getMetadata',\n (...args) => this.#getMetadata(...args),\n );\n\n this.messenger.registerActionHandler(\n 'SnapsRegistry:resolveVersion',\n async (...args) => this.#resolveVersion(...args),\n );\n\n this.messenger.registerActionHandler('SnapsRegistry:update', async () =>\n this.#triggerUpdate(),\n );\n }\n\n #wasRecentlyFetched() {\n return (\n this.state.lastUpdated &&\n Date.now() - this.state.lastUpdated < this.#recentFetchThreshold\n );\n }\n\n /**\n * Triggers an update of the registry database.\n *\n * If an existing update is in progress this function will await that update.\n */\n async #triggerUpdate() {\n // If an update is ongoing, wait for that.\n if (this.#currentUpdate) {\n await this.#currentUpdate;\n return;\n }\n // If no update exists, create promise and store globally.\n if (this.#currentUpdate === null) {\n this.#currentUpdate = this.#update();\n }\n await this.#currentUpdate;\n this.#currentUpdate = null;\n }\n\n /**\n * Updates the registry database if the registry hasn't been updated recently.\n *\n * NOTE: SHOULD NOT be called directly, instead `triggerUpdate` should be used.\n */\n async #update() {\n // No-op if we recently fetched the registry.\n if (this.#wasRecentlyFetched()) {\n return;\n }\n\n try {\n const [database, signature] = await Promise.all([\n this.#safeFetch(this.#url.registry),\n this.#safeFetch(this.#url.signature),\n ]);\n\n await this.#verifySignature(database, signature);\n\n this.update((state) => {\n state.database = JSON.parse(database);\n state.lastUpdated = Date.now();\n state.databaseUnavailable = false;\n });\n } catch {\n // Ignore\n this.update((state) => {\n state.databaseUnavailable = true;\n });\n }\n }\n\n async #getDatabase(): Promise<SnapsRegistryDatabase | null> {\n if (this.state.database === null) {\n await this.#triggerUpdate();\n }\n\n return this.state.database;\n }\n\n async #getSingle(\n snapId: string,\n snapInfo: SnapsRegistryInfo,\n refetch = false,\n ): Promise<SnapsRegistryResult> {\n const database = await this.#getDatabase();\n\n const blockedEntry = database?.blockedSnaps.find((blocked) => {\n if ('id' in blocked) {\n return (\n blocked.id === snapId &&\n satisfiesVersionRange(snapInfo.version, blocked.versionRange)\n );\n }\n\n return blocked.checksum === snapInfo.checksum;\n });\n\n if (blockedEntry) {\n return {\n status: SnapsRegistryStatus.Blocked,\n reason: blockedEntry.reason,\n };\n }\n\n const verified = database?.verifiedSnaps[snapId];\n const version = verified?.versions?.[snapInfo.version];\n const clientRange = version?.clientVersions?.[this.#clientConfig.type];\n const isCompatible =\n !clientRange ||\n satisfiesVersionRange(this.#clientConfig.version, clientRange);\n if (version && version.checksum === snapInfo.checksum && isCompatible) {\n return { status: SnapsRegistryStatus.Verified };\n }\n // For now, if we have an allowlist miss, we can refetch once and try again.\n if (this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#getSingle(snapId, snapInfo, true);\n }\n return {\n status: this.state.databaseUnavailable\n ? SnapsRegistryStatus.Unavailable\n : SnapsRegistryStatus.Unverified,\n };\n }\n\n async #get(\n snaps: SnapsRegistryRequest,\n ): Promise<Record<string, SnapsRegistryResult>> {\n return Object.entries(snaps).reduce<\n Promise<Record<string, SnapsRegistryResult>>\n >(async (previousPromise, [snapId, snapInfo]) => {\n const result = await this.#getSingle(snapId, snapInfo);\n const acc = await previousPromise;\n acc[snapId] = result;\n return acc;\n }, Promise.resolve({}));\n }\n\n /**\n * Find an allowlisted version within a specified version range. Otherwise return the version range itself.\n *\n * @param snapId - The ID of the snap we are trying to resolve a version for.\n * @param versionRange - The version range.\n * @param refetch - An optional flag used to determine if we are refetching the registry.\n * @returns An allowlisted version within the specified version range if available otherwise returns the input version range.\n */\n async #resolveVersion(\n snapId: string,\n versionRange: SemVerRange,\n refetch = false,\n ): Promise<SemVerRange> {\n const database = await this.#getDatabase();\n const versions = database?.verifiedSnaps[snapId]?.versions ?? null;\n\n if (!versions && this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#resolveVersion(snapId, versionRange, true);\n }\n\n // If we cannot narrow down the version range we return the unaltered version range.\n if (!versions) {\n return versionRange;\n }\n\n const compatibleVersions = Object.entries(versions).reduce<SemVerVersion[]>(\n (accumulator, [version, metadata]) => {\n const clientRange = metadata.clientVersions?.[this.#clientConfig.type];\n if (\n !clientRange ||\n satisfiesVersionRange(this.#clientConfig.version, clientRange)\n ) {\n accumulator.push(version as SemVerVersion);\n }\n\n return accumulator;\n },\n [],\n );\n\n const targetVersion = getTargetVersion(compatibleVersions, versionRange);\n\n if (!targetVersion && this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#resolveVersion(snapId, versionRange, true);\n }\n\n // If we cannot narrow down the version range we return the unaltered version range.\n if (!targetVersion) {\n return versionRange;\n }\n\n // A semver version is technically also a valid semver range.\n assertIsSemVerRange(targetVersion);\n return targetVersion;\n }\n\n /**\n * Get metadata for the given snap ID, if available, without updating registry.\n *\n * @param snapId - The ID of the snap to get metadata for.\n * @returns The metadata for the given snap ID, or `null` if the snap is not\n * verified.\n */\n #getMetadata(snapId: string): SnapsRegistryMetadata | null {\n return this.state?.database?.verifiedSnaps[snapId]?.metadata ?? null;\n }\n\n /**\n * Verify the signature of the registry.\n *\n * @param database - The registry database.\n * @param signature - The signature of the registry.\n * @throws If the signature is invalid.\n */\n async #verifySignature(database: string, signature: string) {\n assert(this.#publicKey, 'No public key provided.');\n\n const valid = await verify({\n registry: database,\n signature: JSON.parse(signature),\n publicKey: this.#publicKey,\n });\n\n assert(valid, 'Invalid registry signature.');\n }\n\n /**\n * Fetch the given URL, throwing if the response is not OK.\n *\n * @param url - The URL to fetch.\n * @returns The response body.\n * @private\n */\n async #safeFetch(url: string) {\n const response = await this.#fetchFunction(url, { cache: 'no-cache' });\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}.`);\n }\n\n return await response.text();\n }\n}\n"]}
1
+ {"version":3,"file":"json.mjs","sourceRoot":"","sources":["../../../src/snaps/registry/json.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n SnapsRegistryDatabase,\n SignatureStruct,\n} from '@metamask/snaps-registry';\nimport { verify } from '@metamask/snaps-registry';\nimport { getTargetVersion } from '@metamask/snaps-utils';\nimport type { Infer } from '@metamask/superstruct';\nimport type { Hex, SemVerRange, SemVerVersion } from '@metamask/utils';\nimport {\n assert,\n assertIsSemVerRange,\n Duration,\n inMilliseconds,\n satisfiesVersionRange,\n} from '@metamask/utils';\n\nimport type {\n SnapsRegistry,\n SnapsRegistryInfo,\n SnapsRegistryMetadata,\n SnapsRegistryRequest,\n SnapsRegistryResult,\n} from './registry';\nimport { SnapsRegistryStatus } from './registry';\n\nconst SNAP_REGISTRY_URL =\n 'https://acl.execution.metamask.io/latest/registry.json';\n\nconst SNAP_REGISTRY_SIGNATURE_URL =\n 'https://acl.execution.metamask.io/latest/signature.json';\n\nconst DEFAULT_PUBLIC_KEY =\n '0x025b65308f0f0fb8bc7f7ff87bfc296e0330eee5d3c1d1ee4a048b2fd6a86fa0a6';\n\ntype JsonSnapsRegistryUrl = {\n registry: string;\n signature: string;\n};\n\nexport type ClientConfig = {\n type: 'extension' | 'mobile';\n version: SemVerVersion;\n};\n\nexport type JsonSnapsRegistryArgs = {\n messenger: SnapsRegistryMessenger;\n state?: SnapsRegistryState;\n fetchFunction?: typeof fetch;\n url?: JsonSnapsRegistryUrl;\n recentFetchThreshold?: number;\n refetchOnAllowlistMiss?: boolean;\n publicKey?: Hex;\n clientConfig: ClientConfig;\n};\n\nexport type GetResult = {\n type: `${typeof controllerName}:get`;\n handler: SnapsRegistry['get'];\n};\n\nexport type ResolveVersion = {\n type: `${typeof controllerName}:resolveVersion`;\n handler: SnapsRegistry['resolveVersion'];\n};\n\nexport type GetMetadata = {\n type: `${typeof controllerName}:getMetadata`;\n handler: SnapsRegistry['getMetadata'];\n};\n\nexport type Update = {\n type: `${typeof controllerName}:update`;\n handler: SnapsRegistry['update'];\n};\n\nexport type SnapsRegistryGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n SnapsRegistryState\n>;\n\nexport type SnapsRegistryActions =\n | SnapsRegistryGetStateAction\n | GetResult\n | GetMetadata\n | Update\n | ResolveVersion;\n\nexport type SnapsRegistryStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n SnapsRegistryState\n>;\n\nexport type SnapsRegistryEvents = SnapsRegistryStateChangeEvent;\n\nexport type SnapsRegistryMessenger = Messenger<\n 'SnapsRegistry',\n SnapsRegistryActions,\n SnapsRegistryEvents\n>;\n\nexport type SnapsRegistryState = {\n database: SnapsRegistryDatabase | null;\n signature: string | null;\n lastUpdated: number | null;\n databaseUnavailable: boolean;\n};\n\nconst controllerName = 'SnapsRegistry';\n\nconst defaultState = {\n database: null,\n signature: null,\n lastUpdated: null,\n databaseUnavailable: false,\n};\n\nexport class JsonSnapsRegistry extends BaseController<\n typeof controllerName,\n SnapsRegistryState,\n SnapsRegistryMessenger\n> {\n readonly #url: JsonSnapsRegistryUrl;\n\n readonly #publicKey: Hex;\n\n readonly #clientConfig: ClientConfig;\n\n readonly #fetchFunction: typeof fetch;\n\n readonly #recentFetchThreshold: number;\n\n readonly #refetchOnAllowlistMiss: boolean;\n\n #currentUpdate: Promise<void> | null;\n\n constructor({\n messenger,\n state,\n url = {\n registry: SNAP_REGISTRY_URL,\n signature: SNAP_REGISTRY_SIGNATURE_URL,\n },\n publicKey = DEFAULT_PUBLIC_KEY,\n clientConfig,\n fetchFunction = globalThis.fetch.bind(undefined),\n recentFetchThreshold = inMilliseconds(5, Duration.Minute),\n refetchOnAllowlistMiss = true,\n }: JsonSnapsRegistryArgs) {\n super({\n messenger,\n metadata: {\n database: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n signature: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n lastUpdated: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n databaseUnavailable: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n },\n name: controllerName,\n state: {\n ...defaultState,\n ...state,\n },\n });\n this.#url = url;\n this.#publicKey = publicKey;\n this.#clientConfig = clientConfig;\n this.#fetchFunction = fetchFunction;\n this.#recentFetchThreshold = recentFetchThreshold;\n this.#refetchOnAllowlistMiss = refetchOnAllowlistMiss;\n this.#currentUpdate = null;\n\n this.messenger.registerActionHandler('SnapsRegistry:get', async (...args) =>\n this.#get(...args),\n );\n\n this.messenger.registerActionHandler(\n 'SnapsRegistry:getMetadata',\n (...args) => this.#getMetadata(...args),\n );\n\n this.messenger.registerActionHandler(\n 'SnapsRegistry:resolveVersion',\n async (...args) => this.#resolveVersion(...args),\n );\n\n this.messenger.registerActionHandler('SnapsRegistry:update', async () =>\n this.#triggerUpdate(),\n );\n }\n\n #wasRecentlyFetched() {\n return (\n this.state.lastUpdated &&\n Date.now() - this.state.lastUpdated < this.#recentFetchThreshold\n );\n }\n\n /**\n * Triggers an update of the registry database.\n *\n * If an existing update is in progress this function will await that update.\n */\n async #triggerUpdate() {\n // If an update is ongoing, wait for that.\n if (this.#currentUpdate) {\n await this.#currentUpdate;\n return;\n }\n // If no update exists, create promise and store globally.\n if (this.#currentUpdate === null) {\n this.#currentUpdate = this.#update();\n }\n await this.#currentUpdate;\n this.#currentUpdate = null;\n }\n\n /**\n * Updates the registry database if the registry hasn't been updated recently.\n *\n * NOTE: SHOULD NOT be called directly, instead `triggerUpdate` should be used.\n */\n async #update() {\n // No-op if we recently fetched the registry.\n if (this.#wasRecentlyFetched()) {\n return;\n }\n\n try {\n const [database, signature] = await Promise.all([\n this.#safeFetch(this.#url.registry),\n this.#safeFetch(this.#url.signature),\n ]);\n\n const signatureJson = JSON.parse(signature);\n\n // If the signature matches the existing state, we can skip verification and don't need to update the database.\n if (signatureJson.signature === this.state.signature) {\n this.update((state) => {\n state.lastUpdated = Date.now();\n state.databaseUnavailable = false;\n });\n return;\n }\n\n await this.#verifySignature(database, signatureJson);\n\n this.update((state) => {\n state.database = JSON.parse(database);\n state.lastUpdated = Date.now();\n state.databaseUnavailable = false;\n state.signature = signatureJson.signature;\n });\n } catch {\n // Ignore\n this.update((state) => {\n state.databaseUnavailable = true;\n });\n }\n }\n\n async #getDatabase(): Promise<SnapsRegistryDatabase | null> {\n if (this.state.database === null) {\n await this.#triggerUpdate();\n }\n\n return this.state.database;\n }\n\n async #getSingle(\n snapId: string,\n snapInfo: SnapsRegistryInfo,\n refetch = false,\n ): Promise<SnapsRegistryResult> {\n const database = await this.#getDatabase();\n\n const blockedEntry = database?.blockedSnaps.find((blocked) => {\n if ('id' in blocked) {\n return (\n blocked.id === snapId &&\n satisfiesVersionRange(snapInfo.version, blocked.versionRange)\n );\n }\n\n return blocked.checksum === snapInfo.checksum;\n });\n\n if (blockedEntry) {\n return {\n status: SnapsRegistryStatus.Blocked,\n reason: blockedEntry.reason,\n };\n }\n\n const verified = database?.verifiedSnaps[snapId];\n const version = verified?.versions?.[snapInfo.version];\n const clientRange = version?.clientVersions?.[this.#clientConfig.type];\n const isCompatible =\n !clientRange ||\n satisfiesVersionRange(this.#clientConfig.version, clientRange);\n if (version && version.checksum === snapInfo.checksum && isCompatible) {\n return { status: SnapsRegistryStatus.Verified };\n }\n // For now, if we have an allowlist miss, we can refetch once and try again.\n if (this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#getSingle(snapId, snapInfo, true);\n }\n return {\n status: this.state.databaseUnavailable\n ? SnapsRegistryStatus.Unavailable\n : SnapsRegistryStatus.Unverified,\n };\n }\n\n async #get(\n snaps: SnapsRegistryRequest,\n ): Promise<Record<string, SnapsRegistryResult>> {\n return Object.entries(snaps).reduce<\n Promise<Record<string, SnapsRegistryResult>>\n >(async (previousPromise, [snapId, snapInfo]) => {\n const result = await this.#getSingle(snapId, snapInfo);\n const acc = await previousPromise;\n acc[snapId] = result;\n return acc;\n }, Promise.resolve({}));\n }\n\n /**\n * Find an allowlisted version within a specified version range. Otherwise return the version range itself.\n *\n * @param snapId - The ID of the snap we are trying to resolve a version for.\n * @param versionRange - The version range.\n * @param refetch - An optional flag used to determine if we are refetching the registry.\n * @returns An allowlisted version within the specified version range if available otherwise returns the input version range.\n */\n async #resolveVersion(\n snapId: string,\n versionRange: SemVerRange,\n refetch = false,\n ): Promise<SemVerRange> {\n const database = await this.#getDatabase();\n const versions = database?.verifiedSnaps[snapId]?.versions ?? null;\n\n if (!versions && this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#resolveVersion(snapId, versionRange, true);\n }\n\n // If we cannot narrow down the version range we return the unaltered version range.\n if (!versions) {\n return versionRange;\n }\n\n const compatibleVersions = Object.entries(versions).reduce<SemVerVersion[]>(\n (accumulator, [version, metadata]) => {\n const clientRange = metadata.clientVersions?.[this.#clientConfig.type];\n if (\n !clientRange ||\n satisfiesVersionRange(this.#clientConfig.version, clientRange)\n ) {\n accumulator.push(version as SemVerVersion);\n }\n\n return accumulator;\n },\n [],\n );\n\n const targetVersion = getTargetVersion(compatibleVersions, versionRange);\n\n if (!targetVersion && this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#resolveVersion(snapId, versionRange, true);\n }\n\n // If we cannot narrow down the version range we return the unaltered version range.\n if (!targetVersion) {\n return versionRange;\n }\n\n // A semver version is technically also a valid semver range.\n assertIsSemVerRange(targetVersion);\n return targetVersion;\n }\n\n /**\n * Get metadata for the given snap ID, if available, without updating registry.\n *\n * @param snapId - The ID of the snap to get metadata for.\n * @returns The metadata for the given snap ID, or `null` if the snap is not\n * verified.\n */\n #getMetadata(snapId: string): SnapsRegistryMetadata | null {\n return this.state?.database?.verifiedSnaps[snapId]?.metadata ?? null;\n }\n\n /**\n * Verify the signature of the registry.\n *\n * @param database - The registry database.\n * @param signature - The signature of the registry.\n * @throws If the signature is invalid.\n */\n async #verifySignature(\n database: string,\n signature: Infer<typeof SignatureStruct>,\n ) {\n assert(this.#publicKey, 'No public key provided.');\n\n const valid = await verify({\n registry: database,\n signature,\n publicKey: this.#publicKey,\n });\n\n assert(valid, 'Invalid registry signature.');\n }\n\n /**\n * Fetch the given URL, throwing if the response is not OK.\n *\n * @param url - The URL to fetch.\n * @returns The response body.\n * @private\n */\n async #safeFetch(url: string) {\n const response = await this.#fetchFunction(url, { cache: 'no-cache' });\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}.`);\n }\n\n return await response.text();\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask/snaps-controllers",
3
- "version": "17.1.2",
3
+ "version": "17.2.1",
4
4
  "description": "Controllers for MetaMask Snaps",
5
5
  "keywords": [
6
6
  "MetaMask",
@@ -86,15 +86,16 @@
86
86
  "@metamask/key-tree": "^10.1.1",
87
87
  "@metamask/messenger": "^0.3.0",
88
88
  "@metamask/object-multiplex": "^2.1.0",
89
- "@metamask/permission-controller": "^12.1.1",
90
- "@metamask/phishing-controller": "^15.0.0",
89
+ "@metamask/permission-controller": "^12.2.0",
90
+ "@metamask/phishing-controller": "^16.1.0",
91
91
  "@metamask/post-message-stream": "^10.0.0",
92
92
  "@metamask/rpc-errors": "^7.0.3",
93
93
  "@metamask/snaps-registry": "^4.0.0",
94
94
  "@metamask/snaps-rpc-methods": "^14.1.1",
95
- "@metamask/snaps-sdk": "^10.2.0",
96
- "@metamask/snaps-utils": "^11.6.3",
97
- "@metamask/utils": "^11.8.1",
95
+ "@metamask/snaps-sdk": "^10.3.0",
96
+ "@metamask/snaps-utils": "^11.7.1",
97
+ "@metamask/superstruct": "^3.2.1",
98
+ "@metamask/utils": "^11.9.0",
98
99
  "@xstate/fsm": "^2.0.0",
99
100
  "async-mutex": "^0.5.0",
100
101
  "concat-stream": "^2.0.0",
@@ -110,7 +111,7 @@
110
111
  "tar-stream": "^3.1.7"
111
112
  },
112
113
  "devDependencies": {
113
- "@lavamoat/allow-scripts": "^3.4.0",
114
+ "@lavamoat/allow-scripts": "^3.4.1",
114
115
  "@metamask/auto-changelog": "^5.0.2",
115
116
  "@metamask/browser-passworder": "^6.0.0",
116
117
  "@noble/hashes": "^1.7.1",