@metamask/snaps-controllers 4.1.0 → 5.0.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 (145) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/dist/cjs/cronjob/CronjobController.js +3 -3
  3. package/dist/cjs/cronjob/CronjobController.js.map +1 -1
  4. package/dist/cjs/index.js +1 -0
  5. package/dist/cjs/index.js.map +1 -1
  6. package/dist/cjs/interface/SnapInterfaceController.js +166 -0
  7. package/dist/cjs/interface/SnapInterfaceController.js.map +1 -0
  8. package/dist/cjs/interface/index.js +20 -0
  9. package/dist/cjs/interface/index.js.map +1 -0
  10. package/dist/cjs/interface/utils.js +59 -0
  11. package/dist/cjs/interface/utils.js.map +1 -0
  12. package/dist/cjs/services/browser.js +1 -0
  13. package/dist/cjs/services/browser.js.map +1 -1
  14. package/dist/cjs/services/index.js +1 -0
  15. package/dist/cjs/services/index.js.map +1 -1
  16. package/dist/cjs/services/webview/WebViewExecutionService.js +99 -0
  17. package/dist/cjs/services/webview/WebViewExecutionService.js.map +1 -0
  18. package/dist/cjs/services/webview/WebViewMessageStream.js +127 -0
  19. package/dist/cjs/services/webview/WebViewMessageStream.js.map +1 -0
  20. package/dist/cjs/services/webview/index.js +20 -0
  21. package/dist/cjs/services/webview/index.js.map +1 -0
  22. package/dist/cjs/snaps/SnapController.js +111 -51
  23. package/dist/cjs/snaps/SnapController.js.map +1 -1
  24. package/dist/cjs/snaps/constants.js +25 -0
  25. package/dist/cjs/snaps/constants.js.map +1 -0
  26. package/dist/cjs/snaps/index.js +0 -2
  27. package/dist/cjs/snaps/index.js.map +1 -1
  28. package/dist/cjs/snaps/location/npm.js +13 -2
  29. package/dist/cjs/snaps/location/npm.js.map +1 -1
  30. package/dist/cjs/snaps/registry/json.js +10 -5
  31. package/dist/cjs/snaps/registry/json.js.map +1 -1
  32. package/dist/esm/cronjob/CronjobController.js +2 -2
  33. package/dist/esm/cronjob/CronjobController.js.map +1 -1
  34. package/dist/esm/index.js +1 -0
  35. package/dist/esm/index.js.map +1 -1
  36. package/dist/esm/interface/SnapInterfaceController.js +158 -0
  37. package/dist/esm/interface/SnapInterfaceController.js.map +1 -0
  38. package/dist/esm/interface/index.js +3 -0
  39. package/dist/esm/interface/index.js.map +1 -0
  40. package/dist/esm/interface/utils.js +62 -0
  41. package/dist/esm/interface/utils.js.map +1 -0
  42. package/dist/esm/services/browser.js +1 -0
  43. package/dist/esm/services/browser.js.map +1 -1
  44. package/dist/esm/services/index.js +1 -0
  45. package/dist/esm/services/index.js.map +1 -1
  46. package/dist/esm/services/webview/WebViewExecutionService.js +89 -0
  47. package/dist/esm/services/webview/WebViewExecutionService.js.map +1 -0
  48. package/dist/esm/services/webview/WebViewMessageStream.js +119 -0
  49. package/dist/esm/services/webview/WebViewMessageStream.js.map +1 -0
  50. package/dist/esm/services/webview/index.js +3 -0
  51. package/dist/esm/services/webview/index.js.map +1 -0
  52. package/dist/esm/snaps/SnapController.js +105 -45
  53. package/dist/esm/snaps/SnapController.js.map +1 -1
  54. package/dist/esm/snaps/constants.js +16 -0
  55. package/dist/esm/snaps/constants.js.map +1 -0
  56. package/dist/esm/snaps/index.js +0 -2
  57. package/dist/esm/snaps/index.js.map +1 -1
  58. package/dist/esm/snaps/location/npm.js +13 -2
  59. package/dist/esm/snaps/location/npm.js.map +1 -1
  60. package/dist/esm/snaps/registry/json.js +10 -5
  61. package/dist/esm/snaps/registry/json.js.map +1 -1
  62. package/dist/types/index.d.ts +1 -0
  63. package/dist/types/interface/SnapInterfaceController.d.ts +85 -0
  64. package/dist/types/interface/index.d.ts +1 -0
  65. package/dist/types/interface/utils.d.ts +36 -0
  66. package/dist/types/services/browser.d.ts +1 -0
  67. package/dist/types/services/index.d.ts +1 -0
  68. package/dist/types/services/webview/WebViewExecutionService.d.ts +20 -0
  69. package/dist/types/services/webview/WebViewMessageStream.d.ts +32 -0
  70. package/dist/types/services/webview/index.d.ts +1 -0
  71. package/dist/types/snaps/SnapController.d.ts +7 -4
  72. package/dist/types/snaps/constants.d.ts +1 -0
  73. package/dist/types/snaps/index.d.ts +0 -2
  74. package/dist/types/utils.d.ts +33 -13
  75. package/package.json +12 -12
  76. package/dist/cjs/snaps/endowments/cronjob.js +0 -100
  77. package/dist/cjs/snaps/endowments/cronjob.js.map +0 -1
  78. package/dist/cjs/snaps/endowments/enum.js +0 -26
  79. package/dist/cjs/snaps/endowments/enum.js.map +0 -1
  80. package/dist/cjs/snaps/endowments/ethereum-provider.js +0 -43
  81. package/dist/cjs/snaps/endowments/ethereum-provider.js.map +0 -1
  82. package/dist/cjs/snaps/endowments/home-page.js +0 -37
  83. package/dist/cjs/snaps/endowments/home-page.js.map +0 -1
  84. package/dist/cjs/snaps/endowments/index.js +0 -107
  85. package/dist/cjs/snaps/endowments/index.js.map +0 -1
  86. package/dist/cjs/snaps/endowments/keyring.js +0 -100
  87. package/dist/cjs/snaps/endowments/keyring.js.map +0 -1
  88. package/dist/cjs/snaps/endowments/lifecycle-hooks.js +0 -37
  89. package/dist/cjs/snaps/endowments/lifecycle-hooks.js.map +0 -1
  90. package/dist/cjs/snaps/endowments/name-lookup.js +0 -106
  91. package/dist/cjs/snaps/endowments/name-lookup.js.map +0 -1
  92. package/dist/cjs/snaps/endowments/network-access.js +0 -44
  93. package/dist/cjs/snaps/endowments/network-access.js.map +0 -1
  94. package/dist/cjs/snaps/endowments/rpc.js +0 -99
  95. package/dist/cjs/snaps/endowments/rpc.js.map +0 -1
  96. package/dist/cjs/snaps/endowments/signature-insight.js +0 -106
  97. package/dist/cjs/snaps/endowments/signature-insight.js.map +0 -1
  98. package/dist/cjs/snaps/endowments/transaction-insight.js +0 -106
  99. package/dist/cjs/snaps/endowments/transaction-insight.js.map +0 -1
  100. package/dist/cjs/snaps/endowments/web-assembly.js +0 -42
  101. package/dist/cjs/snaps/endowments/web-assembly.js.map +0 -1
  102. package/dist/cjs/snaps/permissions.js +0 -61
  103. package/dist/cjs/snaps/permissions.js.map +0 -1
  104. package/dist/esm/snaps/endowments/cronjob.js +0 -99
  105. package/dist/esm/snaps/endowments/cronjob.js.map +0 -1
  106. package/dist/esm/snaps/endowments/enum.js +0 -16
  107. package/dist/esm/snaps/endowments/enum.js.map +0 -1
  108. package/dist/esm/snaps/endowments/ethereum-provider.js +0 -33
  109. package/dist/esm/snaps/endowments/ethereum-provider.js.map +0 -1
  110. package/dist/esm/snaps/endowments/home-page.js +0 -27
  111. package/dist/esm/snaps/endowments/home-page.js.map +0 -1
  112. package/dist/esm/snaps/endowments/index.js +0 -60
  113. package/dist/esm/snaps/endowments/index.js.map +0 -1
  114. package/dist/esm/snaps/endowments/keyring.js +0 -91
  115. package/dist/esm/snaps/endowments/keyring.js.map +0 -1
  116. package/dist/esm/snaps/endowments/lifecycle-hooks.js +0 -27
  117. package/dist/esm/snaps/endowments/lifecycle-hooks.js.map +0 -1
  118. package/dist/esm/snaps/endowments/name-lookup.js +0 -98
  119. package/dist/esm/snaps/endowments/name-lookup.js.map +0 -1
  120. package/dist/esm/snaps/endowments/network-access.js +0 -34
  121. package/dist/esm/snaps/endowments/network-access.js.map +0 -1
  122. package/dist/esm/snaps/endowments/rpc.js +0 -88
  123. package/dist/esm/snaps/endowments/rpc.js.map +0 -1
  124. package/dist/esm/snaps/endowments/signature-insight.js +0 -99
  125. package/dist/esm/snaps/endowments/signature-insight.js.map +0 -1
  126. package/dist/esm/snaps/endowments/transaction-insight.js +0 -99
  127. package/dist/esm/snaps/endowments/transaction-insight.js.map +0 -1
  128. package/dist/esm/snaps/endowments/web-assembly.js +0 -32
  129. package/dist/esm/snaps/endowments/web-assembly.js.map +0 -1
  130. package/dist/esm/snaps/permissions.js +0 -50
  131. package/dist/esm/snaps/permissions.js.map +0 -1
  132. package/dist/types/snaps/endowments/cronjob.d.ts +0 -51
  133. package/dist/types/snaps/endowments/enum.d.ts +0 -13
  134. package/dist/types/snaps/endowments/ethereum-provider.d.ts +0 -14
  135. package/dist/types/snaps/endowments/home-page.d.ts +0 -15
  136. package/dist/types/snaps/endowments/index.d.ts +0 -127
  137. package/dist/types/snaps/endowments/keyring.d.ts +0 -39
  138. package/dist/types/snaps/endowments/lifecycle-hooks.d.ts +0 -15
  139. package/dist/types/snaps/endowments/name-lookup.d.ts +0 -38
  140. package/dist/types/snaps/endowments/network-access.d.ts +0 -14
  141. package/dist/types/snaps/endowments/rpc.d.ts +0 -38
  142. package/dist/types/snaps/endowments/signature-insight.d.ts +0 -39
  143. package/dist/types/snaps/endowments/transaction-insight.d.ts +0 -39
  144. package/dist/types/snaps/endowments/web-assembly.d.ts +0 -14
  145. package/dist/types/snaps/permissions.d.ts +0 -16
@@ -64,20 +64,17 @@ function _define_property(obj, key, value) {
64
64
  import { BaseController } from '@metamask/base-controller';
65
65
  import { SubjectType } from '@metamask/permission-controller';
66
66
  import { rpcErrors } from '@metamask/rpc-errors';
67
- import { WALLET_SNAP_PERMISSION_KEY } from '@metamask/snaps-rpc-methods';
67
+ import { WALLET_SNAP_PERMISSION_KEY, getMaxRequestTimeCaveat, handlerEndowments, SnapEndowments, getKeyringCaveatOrigins, getRpcCaveatOrigins, processSnapPermissions } from '@metamask/snaps-rpc-methods';
68
68
  import { AuxiliaryFileEncoding, getErrorMessage } from '@metamask/snaps-sdk';
69
- import { validateComponentLinks, assertIsSnapManifest, assertIsValidSnapId, DEFAULT_ENDOWMENTS, DEFAULT_REQUESTED_SNAP_VERSION, encodeAuxiliaryFile, HandlerType, isOriginAllowed, logError, normalizeRelative, OnTransactionResponseStruct, OnSignatureResponseStruct, resolveVersionRange, SnapCaveatType, SnapStatus, SnapStatusEvents, unwrapError, OnHomePageResponseStruct, getValidatedLocalizationFiles, VirtualFile, NpmSnapFileNames } from '@metamask/snaps-utils';
69
+ import { assertIsSnapManifest, assertIsValidSnapId, DEFAULT_ENDOWMENTS, DEFAULT_REQUESTED_SNAP_VERSION, encodeAuxiliaryFile, HandlerType, isOriginAllowed, logError, normalizeRelative, OnTransactionResponseStruct, OnSignatureResponseStruct, resolveVersionRange, SnapCaveatType, SnapStatus, SnapStatusEvents, unwrapError, OnHomePageResponseStruct, getValidatedLocalizationFiles, VirtualFile, NpmSnapFileNames, OnNameLookupResponseStruct, getLocalizedSnapManifest } from '@metamask/snaps-utils';
70
70
  import { assert, assertIsJsonRpcRequest, assertStruct, Duration, gtRange, gtVersion, hasProperty, inMilliseconds, isNonEmptyArray, isValidSemVerRange, satisfiesVersionRange, timeSince } from '@metamask/utils';
71
71
  import { createMachine, interpret } from '@xstate/fsm';
72
72
  import { nanoid } from 'nanoid';
73
73
  import { forceStrict, validateMachine } from '../fsm';
74
74
  import { log } from '../logging';
75
75
  import { fetchSnap, hasTimedOut, setDiff, withTimeout } from '../utils';
76
- import { handlerEndowments, SnapEndowments } from './endowments';
77
- import { getKeyringCaveatOrigins } from './endowments/keyring';
78
- import { getRpcCaveatOrigins } from './endowments/rpc';
76
+ import { ALLOWED_PERMISSIONS } from './constants';
79
77
  import { detectSnapLocation } from './location';
80
- import { processSnapPermissions } from './permissions';
81
78
  import { SnapsRegistryStatus } from './registry';
82
79
  import { RequestQueue } from './RequestQueue';
83
80
  import { Timer } from './Timer';
@@ -164,11 +161,19 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
164
161
  * @param args - The add snap args.
165
162
  * @returns The resulting snap object.
166
163
  */ _set = /*#__PURE__*/ new WeakSet(), _validateSnapPermissions = /*#__PURE__*/ new WeakSet(), /**
164
+ * Determine the execution timeout for a given handler permission.
165
+ *
166
+ * If no permission is specified or the permission itself has no execution timeout defined
167
+ * the constructor argument `maxRequestTime` will be used.
168
+ *
169
+ * @param permission - An optional permission constraint for the handler being called.
170
+ * @returns The execution timeout for the given handler.
171
+ */ _getExecutionTimeout = /*#__PURE__*/ new WeakSet(), /**
167
172
  * Gets the RPC message handler for the given snap.
168
173
  *
169
174
  * @param snapId - The id of the Snap whose message handler to get.
170
175
  * @returns The RPC handler for the given snap.
171
- */ _getRpcRequestHandler = /*#__PURE__*/ new WeakSet(), _triggerPhishingListUpdate = /*#__PURE__*/ new WeakSet(), _checkPhishingList = /*#__PURE__*/ new WeakSet(), _assertSnapRpcRequestResult = /*#__PURE__*/ new WeakSet(), _executeWithTimeout = /*#__PURE__*/ new WeakSet(), _recordSnapRpcRequestStart = /*#__PURE__*/ new WeakSet(), _recordSnapRpcRequestFinish = /*#__PURE__*/ new WeakSet(), /**
176
+ */ _getRpcRequestHandler = /*#__PURE__*/ new WeakSet(), _createInterface = /*#__PURE__*/ new WeakSet(), _assertInterfaceExists = /*#__PURE__*/ new WeakSet(), _transformSnapRpcRequestResult = /*#__PURE__*/ new WeakSet(), _assertSnapRpcRequestResult = /*#__PURE__*/ new WeakSet(), _executeWithTimeout = /*#__PURE__*/ new WeakSet(), _recordSnapRpcRequestStart = /*#__PURE__*/ new WeakSet(), _recordSnapRpcRequestFinish = /*#__PURE__*/ new WeakSet(), /**
172
177
  * Retrieves the rollback snapshot of a snap.
173
178
  *
174
179
  * @param snapId - The snap id.
@@ -765,7 +770,8 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
765
770
  }
766
771
  await _class_private_method_get(this, _assertIsInstallAllowed, assertIsInstallAllowed).call(this, snapId, {
767
772
  version: newVersion,
768
- checksum: manifest.source.shasum
773
+ checksum: manifest.source.shasum,
774
+ permissions: manifest.initialPermissions
769
775
  });
770
776
  const processedPermissions = processSnapPermissions(manifest.initialPermissions);
771
777
  _class_private_method_get(this, _validateSnapPermissions, validateSnapPermissions).call(this, processedPermissions);
@@ -912,29 +918,32 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
912
918
  };
913
919
  assertIsJsonRpcRequest(request);
914
920
  const permissionName = handlerEndowments[handlerType];
915
- const hasPermission = this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName);
916
- if (!hasPermission) {
921
+ assert(typeof permissionName === 'string' || permissionName === null, "'permissionName' must be either a string or null.");
922
+ const permissions = this.messagingSystem.call('PermissionController:getPermissions', snapId);
923
+ // If permissionName is null, the handler does not require a permission.
924
+ if (permissionName !== null && (!permissions || !hasProperty(permissions, permissionName))) {
917
925
  throw new Error(`Snap "${snapId}" is not permitted to use "${permissionName}".`);
918
926
  }
927
+ const handlerPermissions = permissionName ? permissions[permissionName] : undefined;
919
928
  if (permissionName === SnapEndowments.Rpc || permissionName === SnapEndowments.Keyring) {
920
- const subject = this.messagingSystem.call('SubjectMetadataController:getSubjectMetadata', origin);
921
- const permissions = this.messagingSystem.call('PermissionController:getPermissions', snapId);
922
- const handlerPermissions = permissions?.[permissionName];
923
929
  assert(handlerPermissions);
930
+ const subject = this.messagingSystem.call('SubjectMetadataController:getSubjectMetadata', origin);
924
931
  const origins = permissionName === SnapEndowments.Rpc ? getRpcCaveatOrigins(handlerPermissions) : getKeyringCaveatOrigins(handlerPermissions);
925
932
  assert(origins);
926
933
  if (!isOriginAllowed(origins, subject?.subjectType ?? SubjectType.Website, origin)) {
927
934
  throw new Error(`Snap "${snapId}" is not permitted to handle requests from "${origin}".`);
928
935
  }
929
936
  }
930
- const handler = await _class_private_method_get(this, _getRpcRequestHandler, getRpcRequestHandler).call(this, snapId);
937
+ const handler = _class_private_method_get(this, _getRpcRequestHandler, getRpcRequestHandler).call(this, snapId);
931
938
  if (!handler) {
932
939
  throw new Error(`Snap RPC message handler not found for snap "${snapId}".`);
933
940
  }
941
+ const timeout = _class_private_method_get(this, _getExecutionTimeout, getExecutionTimeout).call(this, handlerPermissions);
934
942
  return handler({
935
943
  origin,
936
944
  handler: handlerType,
937
- request
945
+ request,
946
+ timeout
938
947
  });
939
948
  }
940
949
  constructor({ closeAllConnections, messenger, state, dynamicPermissions = [
@@ -1025,12 +1034,28 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
1025
1034
  */ _class_private_method_init(this, _getEndowments);
1026
1035
  _class_private_method_init(this, _set);
1027
1036
  _class_private_method_init(this, _validateSnapPermissions);
1037
+ _class_private_method_init(this, _getExecutionTimeout);
1028
1038
  _class_private_method_init(this, _getRpcRequestHandler);
1029
- _class_private_method_init(this, _triggerPhishingListUpdate);
1030
- _class_private_method_init(this, _checkPhishingList);
1031
1039
  /**
1032
- * Asserts that the returned result of a Snap RPC call is the expected shape.
1040
+ * Create a dynamic interface in the SnapInterfaceController.
1041
+ *
1042
+ * @param snapId - The snap ID.
1043
+ * @param content - The initial interface content.
1044
+ * @returns An identifier that can be used to identify the interface.
1045
+ */ _class_private_method_init(this, _createInterface);
1046
+ _class_private_method_init(this, _assertInterfaceExists);
1047
+ /**
1048
+ * Transform a RPC request result if necessary.
1033
1049
  *
1050
+ * @param snapId - The snap ID of the snap that produced the result.
1051
+ * @param handlerType - The handler type that produced the result.
1052
+ * @param result - The result.
1053
+ * @returns The transformed result if applicable, otherwise the original result.
1054
+ */ _class_private_method_init(this, _transformSnapRpcRequestResult);
1055
+ /**
1056
+ * Assert that the returned result of a Snap RPC call is the expected shape.
1057
+ *
1058
+ * @param snapId - The snap ID.
1034
1059
  * @param handlerType - The handler type of the RPC Request.
1035
1060
  * @param result - The result of the RPC request.
1036
1061
  */ _class_private_method_init(this, _assertSnapRpcRequestResult);
@@ -1343,7 +1368,9 @@ async function assertIsInstallAllowed(snapId, snapInfo) {
1343
1368
  const result = results[snapId];
1344
1369
  if (result.status === SnapsRegistryStatus.Blocked) {
1345
1370
  throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": The version is blocked. ${result.reason?.explanation ?? ''}`);
1346
- } else if (_class_private_field_get(this, _featureFlags).requireAllowlist && result.status !== SnapsRegistryStatus.Verified) {
1371
+ }
1372
+ const isAllowlistingRequired = Object.keys(snapInfo.permissions).some((permission)=>!ALLOWED_PERMISSIONS.includes(permission));
1373
+ if (_class_private_field_get(this, _featureFlags).requireAllowlist && isAllowlistingRequired && result.status !== SnapsRegistryStatus.Verified) {
1347
1374
  throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": The snap is not on the allowlist.`);
1348
1375
  }
1349
1376
  }
@@ -1474,7 +1501,8 @@ async function add(args) {
1474
1501
  }
1475
1502
  await _class_private_method_get(this, _assertIsInstallAllowed, assertIsInstallAllowed).call(this, snapId, {
1476
1503
  version: manifest.version,
1477
- checksum: manifest.source.shasum
1504
+ checksum: manifest.source.shasum,
1505
+ permissions: manifest.initialPermissions
1478
1506
  });
1479
1507
  return _class_private_method_get(this, _set, set).call(this, {
1480
1508
  ...args,
@@ -1544,7 +1572,7 @@ function set(args) {
1544
1572
  const { id: snapId, origin, files, isUpdate = false, removable, preinstalled } = args;
1545
1573
  const { manifest, sourceCode: sourceCodeFile, svgIcon, auxiliaryFiles: rawAuxiliaryFiles, localizationFiles } = files;
1546
1574
  assertIsSnapManifest(manifest.result);
1547
- const { version, proposedName } = manifest.result;
1575
+ const { version } = manifest.result;
1548
1576
  const sourceCode = sourceCodeFile.toString();
1549
1577
  assert(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
1550
1578
  const auxiliaryFiles = rawAuxiliaryFiles.map((file)=>{
@@ -1565,6 +1593,7 @@ function set(args) {
1565
1593
  origin
1566
1594
  }
1567
1595
  ];
1596
+ const localizedFiles = localizationFiles.map((file)=>file.result);
1568
1597
  const snap = {
1569
1598
  // Restore relevant snap state if it exists
1570
1599
  ...existingSnap,
@@ -1582,7 +1611,7 @@ function set(args) {
1582
1611
  version,
1583
1612
  versionHistory,
1584
1613
  auxiliaryFiles,
1585
- localizationFiles: localizationFiles.map((file)=>file.result)
1614
+ localizationFiles: localizedFiles
1586
1615
  };
1587
1616
  // If the snap was blocked, it isn't any longer
1588
1617
  delete snap.blockInformation;
@@ -1598,6 +1627,9 @@ function set(args) {
1598
1627
  rollbackSnapshot.statePatches = inversePatches;
1599
1628
  }
1600
1629
  }
1630
+ // In case the Snap uses a localized manifest, we need to get the
1631
+ // proposed name from the localized manifest.
1632
+ const { proposedName } = getLocalizedSnapManifest(manifest.result, 'en', localizedFiles);
1601
1633
  this.messagingSystem.call('SubjectMetadataController:addSubjectMetadata', {
1602
1634
  subjectType: SubjectType.Snap,
1603
1635
  name: proposedName,
@@ -1613,7 +1645,7 @@ function set(args) {
1613
1645
  function validateSnapPermissions(processedPermissions) {
1614
1646
  const permissionKeys = Object.keys(processedPermissions);
1615
1647
  const handlerPermissions = Array.from(new Set(Object.values(handlerEndowments)));
1616
- assert(permissionKeys.some((key)=>handlerPermissions.includes(key)), `A snap must request at least one of the following permissions: ${handlerPermissions.join(', ')}.`);
1648
+ assert(permissionKeys.some((key)=>handlerPermissions.includes(key)), `A snap must request at least one of the following permissions: ${handlerPermissions.filter((handler)=>handler !== null).join(', ')}.`);
1617
1649
  const excludedPermissionErrors = permissionKeys.reduce((errors, permission)=>{
1618
1650
  if (hasProperty(_class_private_field_get(this, _excludedPermissions), permission)) {
1619
1651
  errors.push(_class_private_field_get(this, _excludedPermissions)[permission]);
@@ -1622,6 +1654,9 @@ function validateSnapPermissions(processedPermissions) {
1622
1654
  }, []);
1623
1655
  assert(excludedPermissionErrors.length === 0, `One or more permissions are not allowed:\n${excludedPermissionErrors.join('\n')}`);
1624
1656
  }
1657
+ function getExecutionTimeout(permission) {
1658
+ return getMaxRequestTimeCaveat(permission) ?? this.maxRequestTime;
1659
+ }
1625
1660
  function getRpcRequestHandler(snapId) {
1626
1661
  const runtime = _class_private_method_get(this, _getRuntimeExpect, getRuntimeExpect).call(this, snapId);
1627
1662
  const existingHandler = runtime.rpcHandler;
@@ -1632,7 +1667,7 @@ function getRpcRequestHandler(snapId) {
1632
1667
  // We need to set up this promise map to map snapIds to their respective startPromises,
1633
1668
  // because otherwise we would lose context on the correct startPromise.
1634
1669
  const startPromises = new Map();
1635
- const rpcHandler = async ({ origin, handler: handlerType, request })=>{
1670
+ const rpcHandler = async ({ origin, handler: handlerType, request, timeout })=>{
1636
1671
  if (this.state.snaps[snapId].enabled === false) {
1637
1672
  throw new Error(`Snap "${snapId}" is disabled.`);
1638
1673
  }
@@ -1658,7 +1693,7 @@ function getRpcRequestHandler(snapId) {
1658
1693
  }
1659
1694
  }
1660
1695
  }
1661
- const timer = new Timer(this.maxRequestTime);
1696
+ const timer = new Timer(timeout);
1662
1697
  _class_private_method_get(this, _recordSnapRpcRequestStart, recordSnapRpcRequestStart).call(this, snapId, request.id, timer);
1663
1698
  const handleRpcRequestPromise = this.messagingSystem.call('ExecutionService:handleRpcRequest', snapId, {
1664
1699
  origin,
@@ -1668,8 +1703,8 @@ function getRpcRequestHandler(snapId) {
1668
1703
  // This will either get the result or reject due to the timeout.
1669
1704
  try {
1670
1705
  const result = await _class_private_method_get(this, _executeWithTimeout, executeWithTimeout).call(this, handleRpcRequestPromise, timer);
1671
- await _class_private_method_get(this, _assertSnapRpcRequestResult, assertSnapRpcRequestResult).call(this, handlerType, result);
1672
- return result;
1706
+ await _class_private_method_get(this, _assertSnapRpcRequestResult, assertSnapRpcRequestResult).call(this, snapId, handlerType, result);
1707
+ return _class_private_method_get(this, _transformSnapRpcRequestResult, transformSnapRpcRequestResult).call(this, snapId, handlerType, result);
1673
1708
  } catch (error) {
1674
1709
  const [jsonRpcError, handled] = unwrapError(error);
1675
1710
  if (!handled) {
@@ -1683,40 +1718,64 @@ function getRpcRequestHandler(snapId) {
1683
1718
  runtime.rpcHandler = rpcHandler;
1684
1719
  return rpcHandler;
1685
1720
  }
1686
- async function triggerPhishingListUpdate() {
1687
- return this.messagingSystem.call('PhishingController:maybeUpdateState');
1721
+ async function createInterface(snapId, content) {
1722
+ return this.messagingSystem.call('SnapInterfaceController:createInterface', snapId, content);
1688
1723
  }
1689
- function checkPhishingList(origin) {
1690
- return this.messagingSystem.call('PhishingController:testOrigin', origin).result;
1724
+ function assertInterfaceExists(snapId, id) {
1725
+ // This will throw if the interface isn't accessible, but we assert nevertheless.
1726
+ assert(this.messagingSystem.call('SnapInterfaceController:getInterface', snapId, id));
1691
1727
  }
1692
- async function assertSnapRpcRequestResult(handlerType, result) {
1728
+ async function transformSnapRpcRequestResult(snapId, handlerType, result) {
1729
+ switch(handlerType){
1730
+ case HandlerType.OnTransaction:
1731
+ case HandlerType.OnSignature:
1732
+ case HandlerType.OnHomePage:
1733
+ {
1734
+ // Since this type has been asserted earlier we can cast
1735
+ const castResult = result;
1736
+ // If a handler returns static content, we turn it into a dynamic UI
1737
+ if (castResult && hasProperty(castResult, 'content')) {
1738
+ const { content, ...rest } = castResult;
1739
+ const id = await _class_private_method_get(this, _createInterface, createInterface).call(this, snapId, content);
1740
+ return {
1741
+ ...rest,
1742
+ id
1743
+ };
1744
+ }
1745
+ return result;
1746
+ }
1747
+ default:
1748
+ return result;
1749
+ }
1750
+ }
1751
+ async function assertSnapRpcRequestResult(snapId, handlerType, result) {
1693
1752
  switch(handlerType){
1694
1753
  case HandlerType.OnTransaction:
1695
1754
  {
1696
1755
  assertStruct(result, OnTransactionResponseStruct);
1697
- // Null is an allowed return value here
1698
- if (result === null) {
1699
- return;
1756
+ if (result && hasProperty(result, 'id')) {
1757
+ _class_private_method_get(this, _assertInterfaceExists, assertInterfaceExists).call(this, snapId, result.id);
1700
1758
  }
1701
- await _class_private_method_get(this, _triggerPhishingListUpdate, triggerPhishingListUpdate).call(this);
1702
- validateComponentLinks(result.content, _class_private_method_get(this, _checkPhishingList, checkPhishingList).bind(this));
1703
1759
  break;
1704
1760
  }
1705
1761
  case HandlerType.OnSignature:
1706
1762
  {
1707
1763
  assertStruct(result, OnSignatureResponseStruct);
1708
- // Null is an allowed return value here
1709
- if (result === null) {
1710
- return;
1764
+ if (result && hasProperty(result, 'id')) {
1765
+ _class_private_method_get(this, _assertInterfaceExists, assertInterfaceExists).call(this, snapId, result.id);
1711
1766
  }
1712
- await _class_private_method_get(this, _triggerPhishingListUpdate, triggerPhishingListUpdate).call(this);
1713
- validateComponentLinks(result.content, _class_private_method_get(this, _checkPhishingList, checkPhishingList).bind(this));
1714
1767
  break;
1715
1768
  }
1716
1769
  case HandlerType.OnHomePage:
1717
- assertStruct(result, OnHomePageResponseStruct);
1718
- await _class_private_method_get(this, _triggerPhishingListUpdate, triggerPhishingListUpdate).call(this);
1719
- validateComponentLinks(result.content, _class_private_method_get(this, _checkPhishingList, checkPhishingList).bind(this));
1770
+ {
1771
+ assertStruct(result, OnHomePageResponseStruct);
1772
+ if (result && hasProperty(result, 'id')) {
1773
+ _class_private_method_get(this, _assertInterfaceExists, assertInterfaceExists).call(this, snapId, result.id);
1774
+ }
1775
+ break;
1776
+ }
1777
+ case HandlerType.OnNameLookup:
1778
+ assertStruct(result, OnNameLookupResponseStruct);
1720
1779
  break;
1721
1780
  default:
1722
1781
  break;
@@ -1869,6 +1928,7 @@ function isValidUpdate(snapId, newVersionRange) {
1869
1928
  }
1870
1929
  async function callLifecycleHook(snapId, handler) {
1871
1930
  const permissionName = handlerEndowments[handler];
1931
+ assert(permissionName, 'Lifecycle hook must have an endowment.');
1872
1932
  const hasPermission = this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName);
1873
1933
  if (!hasPermission) {
1874
1934
  return;