@metamask/snaps-controllers 4.0.0 → 5.0.0
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.
- package/CHANGELOG.md +33 -1
- package/dist/cjs/cronjob/CronjobController.js +3 -3
- package/dist/cjs/cronjob/CronjobController.js.map +1 -1
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/interface/SnapInterfaceController.js +166 -0
- package/dist/cjs/interface/SnapInterfaceController.js.map +1 -0
- package/dist/cjs/interface/index.js +20 -0
- package/dist/cjs/interface/index.js.map +1 -0
- package/dist/cjs/interface/utils.js +59 -0
- package/dist/cjs/interface/utils.js.map +1 -0
- package/dist/cjs/services/ProxyPostMessageStream.js +3 -10
- package/dist/cjs/services/ProxyPostMessageStream.js.map +1 -1
- package/dist/cjs/services/browser.js +1 -0
- package/dist/cjs/services/browser.js.map +1 -1
- package/dist/cjs/services/index.js +1 -0
- package/dist/cjs/services/index.js.map +1 -1
- package/dist/cjs/services/node/NodeProcessExecutionService.js +13 -1
- package/dist/cjs/services/node/NodeProcessExecutionService.js.map +1 -1
- package/dist/cjs/services/node/NodeThreadExecutionService.js +14 -1
- package/dist/cjs/services/node/NodeThreadExecutionService.js.map +1 -1
- package/dist/cjs/services/offscreen/OffscreenExecutionService.js +36 -99
- package/dist/cjs/services/offscreen/OffscreenExecutionService.js.map +1 -1
- package/dist/cjs/services/proxy/ProxyExecutionService.js +110 -0
- package/dist/cjs/services/proxy/ProxyExecutionService.js.map +1 -0
- package/dist/cjs/services/webview/WebViewExecutionService.js +99 -0
- package/dist/cjs/services/webview/WebViewExecutionService.js.map +1 -0
- package/dist/cjs/services/webview/WebViewMessageStream.js +127 -0
- package/dist/cjs/services/webview/WebViewMessageStream.js.map +1 -0
- package/dist/cjs/services/webview/index.js +20 -0
- package/dist/cjs/services/webview/index.js.map +1 -0
- package/dist/cjs/snaps/SnapController.js +303 -138
- package/dist/cjs/snaps/SnapController.js.map +1 -1
- package/dist/cjs/snaps/constants.js +25 -0
- package/dist/cjs/snaps/constants.js.map +1 -0
- package/dist/cjs/snaps/index.js +0 -2
- package/dist/cjs/snaps/index.js.map +1 -1
- package/dist/cjs/snaps/location/npm.js +13 -2
- package/dist/cjs/snaps/location/npm.js.map +1 -1
- package/dist/cjs/utils.js +32 -0
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/cronjob/CronjobController.js +2 -2
- package/dist/esm/cronjob/CronjobController.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/interface/SnapInterfaceController.js +158 -0
- package/dist/esm/interface/SnapInterfaceController.js.map +1 -0
- package/dist/esm/interface/index.js +3 -0
- package/dist/esm/interface/index.js.map +1 -0
- package/dist/esm/interface/utils.js +62 -0
- package/dist/esm/interface/utils.js.map +1 -0
- package/dist/esm/services/ProxyPostMessageStream.js +3 -10
- package/dist/esm/services/ProxyPostMessageStream.js.map +1 -1
- package/dist/esm/services/browser.js +1 -0
- package/dist/esm/services/browser.js.map +1 -1
- package/dist/esm/services/index.js +1 -0
- package/dist/esm/services/index.js.map +1 -1
- package/dist/esm/services/node/NodeProcessExecutionService.js +13 -1
- package/dist/esm/services/node/NodeProcessExecutionService.js.map +1 -1
- package/dist/esm/services/node/NodeThreadExecutionService.js +14 -1
- package/dist/esm/services/node/NodeThreadExecutionService.js.map +1 -1
- package/dist/esm/services/offscreen/OffscreenExecutionService.js +36 -99
- package/dist/esm/services/offscreen/OffscreenExecutionService.js.map +1 -1
- package/dist/esm/services/proxy/ProxyExecutionService.js +100 -0
- package/dist/esm/services/proxy/ProxyExecutionService.js.map +1 -0
- package/dist/esm/services/webview/WebViewExecutionService.js +89 -0
- package/dist/esm/services/webview/WebViewExecutionService.js.map +1 -0
- package/dist/esm/services/webview/WebViewMessageStream.js +119 -0
- package/dist/esm/services/webview/WebViewMessageStream.js.map +1 -0
- package/dist/esm/services/webview/index.js +3 -0
- package/dist/esm/services/webview/index.js.map +1 -0
- package/dist/esm/snaps/SnapController.js +299 -134
- package/dist/esm/snaps/SnapController.js.map +1 -1
- package/dist/esm/snaps/constants.js +16 -0
- package/dist/esm/snaps/constants.js.map +1 -0
- package/dist/esm/snaps/index.js +0 -2
- package/dist/esm/snaps/index.js.map +1 -1
- package/dist/esm/snaps/location/npm.js +13 -2
- package/dist/esm/snaps/location/npm.js.map +1 -1
- package/dist/esm/utils.js +37 -0
- package/dist/esm/utils.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/interface/SnapInterfaceController.d.ts +85 -0
- package/dist/types/interface/index.d.ts +1 -0
- package/dist/types/interface/utils.d.ts +36 -0
- package/dist/types/services/ProxyPostMessageStream.d.ts +1 -2
- package/dist/types/services/browser.d.ts +1 -0
- package/dist/types/services/index.d.ts +1 -0
- package/dist/types/services/offscreen/OffscreenExecutionService.d.ts +5 -22
- package/dist/types/services/proxy/ProxyExecutionService.d.ts +39 -0
- package/dist/types/services/webview/WebViewExecutionService.d.ts +20 -0
- package/dist/types/services/webview/WebViewMessageStream.d.ts +32 -0
- package/dist/types/services/webview/index.d.ts +1 -0
- package/dist/types/snaps/SnapController.d.ts +37 -6
- package/dist/types/snaps/constants.d.ts +1 -0
- package/dist/types/snaps/index.d.ts +0 -2
- package/dist/types/utils.d.ts +119 -0
- package/package.json +14 -14
- package/dist/cjs/snaps/endowments/cronjob.js +0 -100
- package/dist/cjs/snaps/endowments/cronjob.js.map +0 -1
- package/dist/cjs/snaps/endowments/enum.js +0 -25
- package/dist/cjs/snaps/endowments/enum.js.map +0 -1
- package/dist/cjs/snaps/endowments/ethereum-provider.js +0 -43
- package/dist/cjs/snaps/endowments/ethereum-provider.js.map +0 -1
- package/dist/cjs/snaps/endowments/home-page.js +0 -37
- package/dist/cjs/snaps/endowments/home-page.js.map +0 -1
- package/dist/cjs/snaps/endowments/index.js +0 -99
- package/dist/cjs/snaps/endowments/index.js.map +0 -1
- package/dist/cjs/snaps/endowments/keyring.js +0 -100
- package/dist/cjs/snaps/endowments/keyring.js.map +0 -1
- package/dist/cjs/snaps/endowments/lifecycle-hooks.js +0 -37
- package/dist/cjs/snaps/endowments/lifecycle-hooks.js.map +0 -1
- package/dist/cjs/snaps/endowments/name-lookup.js +0 -106
- package/dist/cjs/snaps/endowments/name-lookup.js.map +0 -1
- package/dist/cjs/snaps/endowments/network-access.js +0 -44
- package/dist/cjs/snaps/endowments/network-access.js.map +0 -1
- package/dist/cjs/snaps/endowments/rpc.js +0 -99
- package/dist/cjs/snaps/endowments/rpc.js.map +0 -1
- package/dist/cjs/snaps/endowments/transaction-insight.js +0 -106
- package/dist/cjs/snaps/endowments/transaction-insight.js.map +0 -1
- package/dist/cjs/snaps/endowments/web-assembly.js +0 -42
- package/dist/cjs/snaps/endowments/web-assembly.js.map +0 -1
- package/dist/cjs/snaps/permissions.js +0 -61
- package/dist/cjs/snaps/permissions.js.map +0 -1
- package/dist/esm/snaps/endowments/cronjob.js +0 -99
- package/dist/esm/snaps/endowments/cronjob.js.map +0 -1
- package/dist/esm/snaps/endowments/enum.js +0 -15
- package/dist/esm/snaps/endowments/enum.js.map +0 -1
- package/dist/esm/snaps/endowments/ethereum-provider.js +0 -33
- package/dist/esm/snaps/endowments/ethereum-provider.js.map +0 -1
- package/dist/esm/snaps/endowments/home-page.js +0 -27
- package/dist/esm/snaps/endowments/home-page.js.map +0 -1
- package/dist/esm/snaps/endowments/index.js +0 -54
- package/dist/esm/snaps/endowments/index.js.map +0 -1
- package/dist/esm/snaps/endowments/keyring.js +0 -91
- package/dist/esm/snaps/endowments/keyring.js.map +0 -1
- package/dist/esm/snaps/endowments/lifecycle-hooks.js +0 -27
- package/dist/esm/snaps/endowments/lifecycle-hooks.js.map +0 -1
- package/dist/esm/snaps/endowments/name-lookup.js +0 -98
- package/dist/esm/snaps/endowments/name-lookup.js.map +0 -1
- package/dist/esm/snaps/endowments/network-access.js +0 -34
- package/dist/esm/snaps/endowments/network-access.js.map +0 -1
- package/dist/esm/snaps/endowments/rpc.js +0 -88
- package/dist/esm/snaps/endowments/rpc.js.map +0 -1
- package/dist/esm/snaps/endowments/transaction-insight.js +0 -99
- package/dist/esm/snaps/endowments/transaction-insight.js.map +0 -1
- package/dist/esm/snaps/endowments/web-assembly.js +0 -32
- package/dist/esm/snaps/endowments/web-assembly.js.map +0 -1
- package/dist/esm/snaps/permissions.js +0 -50
- package/dist/esm/snaps/permissions.js.map +0 -1
- package/dist/types/snaps/endowments/cronjob.d.ts +0 -51
- package/dist/types/snaps/endowments/enum.d.ts +0 -12
- package/dist/types/snaps/endowments/ethereum-provider.d.ts +0 -14
- package/dist/types/snaps/endowments/home-page.d.ts +0 -15
- package/dist/types/snaps/endowments/index.d.ts +0 -115
- package/dist/types/snaps/endowments/keyring.d.ts +0 -39
- package/dist/types/snaps/endowments/lifecycle-hooks.d.ts +0 -15
- package/dist/types/snaps/endowments/name-lookup.d.ts +0 -38
- package/dist/types/snaps/endowments/network-access.d.ts +0 -14
- package/dist/types/snaps/endowments/rpc.d.ts +0 -38
- package/dist/types/snaps/endowments/transaction-insight.d.ts +0 -39
- package/dist/types/snaps/endowments/web-assembly.d.ts +0 -14
- 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 {
|
|
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
|
-
import {
|
|
76
|
-
import {
|
|
77
|
-
import { getKeyringCaveatOrigins } from './endowments/keyring';
|
|
78
|
-
import { getRpcCaveatOrigins } from './endowments/rpc';
|
|
75
|
+
import { fetchSnap, hasTimedOut, setDiff, withTimeout } from '../utils';
|
|
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';
|
|
@@ -125,7 +122,7 @@ var _closeAllConnections = /*#__PURE__*/ new WeakMap(), _dynamicPermissions = /*
|
|
|
125
122
|
_initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
126
123
|
* Constructor helper for registering the controller's messaging system
|
|
127
124
|
* actions.
|
|
128
|
-
*/ _registerMessageHandlers = /*#__PURE__*/ new WeakSet(), _pollForLastRequestStatus = /*#__PURE__*/ new WeakSet(), _blockSnap = /*#__PURE__*/ new WeakSet(), /**
|
|
125
|
+
*/ _registerMessageHandlers = /*#__PURE__*/ new WeakSet(), _handlePreinstalledSnaps = /*#__PURE__*/ new WeakSet(), _pollForLastRequestStatus = /*#__PURE__*/ new WeakSet(), _blockSnap = /*#__PURE__*/ new WeakSet(), /**
|
|
129
126
|
* Unblocks a snap so that it can be enabled and started again. Emits
|
|
130
127
|
* {@link SnapUnblocked}. Does nothing if the snap is not installed or already
|
|
131
128
|
* unblocked.
|
|
@@ -142,7 +139,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
142
139
|
*
|
|
143
140
|
* @param snapId - The id of the snap to transition.
|
|
144
141
|
* @param event - The event enum to use to transition.
|
|
145
|
-
*/ _transition = /*#__PURE__*/ new WeakSet(), _terminateSnap = /*#__PURE__*/ new WeakSet(), /**
|
|
142
|
+
*/ _transition = /*#__PURE__*/ new WeakSet(), _terminateSnap = /*#__PURE__*/ new WeakSet(), _handleInitialConnections = /*#__PURE__*/ new WeakSet(), _addSnapToSubject = /*#__PURE__*/ new WeakSet(), /**
|
|
146
143
|
* Removes a snap's permission (caveat) from all subjects.
|
|
147
144
|
*
|
|
148
145
|
* @param snapId - The id of the Snap.
|
|
@@ -163,12 +160,20 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
163
160
|
*
|
|
164
161
|
* @param args - The add snap args.
|
|
165
162
|
* @returns The resulting snap object.
|
|
166
|
-
*/ _set = /*#__PURE__*/ new WeakSet(),
|
|
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(),
|
|
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.
|
|
@@ -181,6 +186,16 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
181
186
|
* @throws {@link Error}. If the snap exists before creation or if creation fails.
|
|
182
187
|
* @returns A `RollbackSnapshot`.
|
|
183
188
|
*/ _createRollbackSnapshot = /*#__PURE__*/ new WeakSet(), _rollbackSnap = /*#__PURE__*/ new WeakSet(), _rollbackSnaps = /*#__PURE__*/ new WeakSet(), _getRuntime = /*#__PURE__*/ new WeakSet(), _getRuntimeExpect = /*#__PURE__*/ new WeakSet(), _setupRuntime = /*#__PURE__*/ new WeakSet(), _calculatePermissionsChange = /*#__PURE__*/ new WeakSet(), /**
|
|
189
|
+
* Updates the permissions for a snap following an install, update or rollback.
|
|
190
|
+
*
|
|
191
|
+
* Grants newly requested permissions and revokes unused/revoked permissions.
|
|
192
|
+
*
|
|
193
|
+
* @param args - An options bag.
|
|
194
|
+
* @param args.snapId - The snap ID.
|
|
195
|
+
* @param args.newPermissions - New permissions to be granted.
|
|
196
|
+
* @param args.unusedPermissions - Unused permissions to be revoked.
|
|
197
|
+
* @param args.requestData - Optional request data from an approval.
|
|
198
|
+
*/ _updatePermissions = /*#__PURE__*/ new WeakSet(), /**
|
|
184
199
|
* Checks if a snap will pass version validation checks
|
|
185
200
|
* with the new version range that is requested. The first
|
|
186
201
|
* check that is done is to check if the existing snap version
|
|
@@ -471,6 +486,10 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
471
486
|
if (!Array.isArray(snapIds)) {
|
|
472
487
|
throw new Error('Expected array of snap ids.');
|
|
473
488
|
}
|
|
489
|
+
snapIds.forEach((snapId)=>{
|
|
490
|
+
const snap = this.getExpect(snapId);
|
|
491
|
+
assert(snap.removable !== false, `${snapId} is not removable.`);
|
|
492
|
+
});
|
|
474
493
|
await Promise.all(snapIds.map(async (snapId)=>{
|
|
475
494
|
const snap = this.getExpect(snapId);
|
|
476
495
|
const truncated = this.getTruncatedExpect(snapId);
|
|
@@ -664,6 +683,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
664
683
|
snapId,
|
|
665
684
|
type: SNAP_APPROVAL_INSTALL
|
|
666
685
|
});
|
|
686
|
+
this.messagingSystem.publish('SnapController:snapInstallStarted', snapId, origin, false);
|
|
667
687
|
// Existing snaps must be stopped before overwriting
|
|
668
688
|
if (existingSnap && this.isRunning(snapId)) {
|
|
669
689
|
await this.stopSnap(snapId, SnapStatusEvents.Stop);
|
|
@@ -697,11 +717,13 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
697
717
|
return truncated;
|
|
698
718
|
} catch (error) {
|
|
699
719
|
logError(`Error when adding ${snapId}.`, error);
|
|
720
|
+
const errorString = error instanceof Error ? error.message : error.toString();
|
|
700
721
|
_class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
|
|
701
722
|
loading: false,
|
|
702
723
|
type: SNAP_APPROVAL_INSTALL,
|
|
703
|
-
error:
|
|
724
|
+
error: errorString
|
|
704
725
|
});
|
|
726
|
+
this.messagingSystem.publish('SnapController:snapInstallFailed', snapId, origin, false, errorString);
|
|
705
727
|
throw error;
|
|
706
728
|
}
|
|
707
729
|
}
|
|
@@ -733,9 +755,11 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
733
755
|
type: SNAP_APPROVAL_UPDATE
|
|
734
756
|
});
|
|
735
757
|
try {
|
|
758
|
+
this.messagingSystem.publish('SnapController:snapInstallStarted', snapId, origin, true);
|
|
736
759
|
const snap = this.getExpect(snapId);
|
|
737
|
-
const
|
|
738
|
-
const
|
|
760
|
+
const oldManifest = snap.manifest;
|
|
761
|
+
const newSnap = await fetchSnap(snapId, location);
|
|
762
|
+
const { sourceCode: sourceCodeFile, manifest: manifestFile } = newSnap;
|
|
739
763
|
const manifest = manifestFile.result;
|
|
740
764
|
const newVersion = manifest.version;
|
|
741
765
|
if (!gtVersion(newVersion, snap.version)) {
|
|
@@ -746,7 +770,8 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
746
770
|
}
|
|
747
771
|
await _class_private_method_get(this, _assertIsInstallAllowed, assertIsInstallAllowed).call(this, snapId, {
|
|
748
772
|
version: newVersion,
|
|
749
|
-
checksum: manifest.source.shasum
|
|
773
|
+
checksum: manifest.source.shasum,
|
|
774
|
+
permissions: manifest.initialPermissions
|
|
750
775
|
});
|
|
751
776
|
const processedPermissions = processSnapPermissions(manifest.initialPermissions);
|
|
752
777
|
_class_private_method_get(this, _validateSnapPermissions, validateSnapPermissions).call(this, processedPermissions);
|
|
@@ -772,28 +797,22 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
772
797
|
_class_private_method_get(this, _set, set).call(this, {
|
|
773
798
|
origin,
|
|
774
799
|
id: snapId,
|
|
775
|
-
files: newSnap
|
|
800
|
+
files: newSnap,
|
|
776
801
|
isUpdate: true
|
|
777
802
|
});
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
}
|
|
784
|
-
if (
|
|
785
|
-
this.
|
|
786
|
-
approvedPermissions: approvedNewPermissions,
|
|
787
|
-
subject: {
|
|
788
|
-
origin: snapId
|
|
789
|
-
},
|
|
790
|
-
requestData
|
|
791
|
-
});
|
|
803
|
+
_class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
|
|
804
|
+
snapId,
|
|
805
|
+
unusedPermissions,
|
|
806
|
+
newPermissions: approvedNewPermissions,
|
|
807
|
+
requestData
|
|
808
|
+
});
|
|
809
|
+
if (manifest.initialConnections) {
|
|
810
|
+
_class_private_method_get(this, _handleInitialConnections, handleInitialConnections).call(this, snapId, oldManifest.initialConnections ?? null, manifest.initialConnections);
|
|
792
811
|
}
|
|
793
812
|
const rollbackSnapshot = _class_private_method_get(this, _getRollbackSnapshot, getRollbackSnapshot).call(this, snapId);
|
|
794
813
|
if (rollbackSnapshot !== undefined) {
|
|
795
814
|
rollbackSnapshot.permissions.revoked = unusedPermissions;
|
|
796
|
-
rollbackSnapshot.permissions.granted =
|
|
815
|
+
rollbackSnapshot.permissions.granted = approvedNewPermissions;
|
|
797
816
|
rollbackSnapshot.permissions.requestData = requestData;
|
|
798
817
|
}
|
|
799
818
|
const sourceCode = sourceCodeFile.toString();
|
|
@@ -817,11 +836,13 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
817
836
|
return truncatedSnap;
|
|
818
837
|
} catch (error) {
|
|
819
838
|
logError(`Error when updating ${snapId},`, error);
|
|
839
|
+
const errorString = error instanceof Error ? error.message : error.toString();
|
|
820
840
|
_class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
|
|
821
841
|
loading: false,
|
|
822
|
-
error:
|
|
842
|
+
error: errorString,
|
|
823
843
|
type: SNAP_APPROVAL_UPDATE
|
|
824
844
|
});
|
|
845
|
+
this.messagingSystem.publish('SnapController:snapInstallFailed', snapId, origin, true, errorString);
|
|
825
846
|
throw error;
|
|
826
847
|
}
|
|
827
848
|
}
|
|
@@ -856,14 +877,13 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
856
877
|
permissions: processedPermissions
|
|
857
878
|
});
|
|
858
879
|
const { permissions: approvedPermissions, ...requestData } = await pendingApproval.promise;
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
});
|
|
880
|
+
_class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
|
|
881
|
+
snapId,
|
|
882
|
+
newPermissions: approvedPermissions,
|
|
883
|
+
requestData
|
|
884
|
+
});
|
|
885
|
+
if (snap.manifest.initialConnections) {
|
|
886
|
+
_class_private_method_get(this, _handleInitialConnections, handleInitialConnections).call(this, snapId, null, snap.manifest.initialConnections);
|
|
867
887
|
}
|
|
868
888
|
} finally{
|
|
869
889
|
const runtime = _class_private_method_get(this, _getRuntimeExpect, getRuntimeExpect).call(this, snapId);
|
|
@@ -898,34 +918,37 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
898
918
|
};
|
|
899
919
|
assertIsJsonRpcRequest(request);
|
|
900
920
|
const permissionName = handlerEndowments[handlerType];
|
|
901
|
-
|
|
902
|
-
|
|
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))) {
|
|
903
925
|
throw new Error(`Snap "${snapId}" is not permitted to use "${permissionName}".`);
|
|
904
926
|
}
|
|
927
|
+
const handlerPermissions = permissionName ? permissions[permissionName] : undefined;
|
|
905
928
|
if (permissionName === SnapEndowments.Rpc || permissionName === SnapEndowments.Keyring) {
|
|
906
|
-
const subject = this.messagingSystem.call('SubjectMetadataController:getSubjectMetadata', origin);
|
|
907
|
-
const permissions = this.messagingSystem.call('PermissionController:getPermissions', snapId);
|
|
908
|
-
const handlerPermissions = permissions?.[permissionName];
|
|
909
929
|
assert(handlerPermissions);
|
|
930
|
+
const subject = this.messagingSystem.call('SubjectMetadataController:getSubjectMetadata', origin);
|
|
910
931
|
const origins = permissionName === SnapEndowments.Rpc ? getRpcCaveatOrigins(handlerPermissions) : getKeyringCaveatOrigins(handlerPermissions);
|
|
911
932
|
assert(origins);
|
|
912
933
|
if (!isOriginAllowed(origins, subject?.subjectType ?? SubjectType.Website, origin)) {
|
|
913
934
|
throw new Error(`Snap "${snapId}" is not permitted to handle requests from "${origin}".`);
|
|
914
935
|
}
|
|
915
936
|
}
|
|
916
|
-
const handler =
|
|
937
|
+
const handler = _class_private_method_get(this, _getRpcRequestHandler, getRpcRequestHandler).call(this, snapId);
|
|
917
938
|
if (!handler) {
|
|
918
939
|
throw new Error(`Snap RPC message handler not found for snap "${snapId}".`);
|
|
919
940
|
}
|
|
941
|
+
const timeout = _class_private_method_get(this, _getExecutionTimeout, getExecutionTimeout).call(this, handlerPermissions);
|
|
920
942
|
return handler({
|
|
921
943
|
origin,
|
|
922
944
|
handler: handlerType,
|
|
923
|
-
request
|
|
945
|
+
request,
|
|
946
|
+
timeout
|
|
924
947
|
});
|
|
925
948
|
}
|
|
926
949
|
constructor({ closeAllConnections, messenger, state, dynamicPermissions = [
|
|
927
950
|
'eth_accounts'
|
|
928
|
-
], environmentEndowmentPermissions = [], excludedPermissions = {}, idleTimeCheckInterval = inMilliseconds(5, Duration.Second), maxIdleTime = inMilliseconds(30, Duration.Second), maxRequestTime = inMilliseconds(60, Duration.Second), fetchFunction = globalThis.fetch.bind(globalThis), featureFlags = {}, detectSnapLocation: detectSnapLocationFunction = detectSnapLocation }){
|
|
951
|
+
], environmentEndowmentPermissions = [], excludedPermissions = {}, idleTimeCheckInterval = inMilliseconds(5, Duration.Second), maxIdleTime = inMilliseconds(30, Duration.Second), maxRequestTime = inMilliseconds(60, Duration.Second), fetchFunction = globalThis.fetch.bind(globalThis), featureFlags = {}, detectSnapLocation: detectSnapLocationFunction = detectSnapLocation, preinstalledSnaps }){
|
|
929
952
|
super({
|
|
930
953
|
messenger,
|
|
931
954
|
metadata: {
|
|
@@ -963,6 +986,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
963
986
|
});
|
|
964
987
|
_class_private_method_init(this, _initializeStateMachine);
|
|
965
988
|
_class_private_method_init(this, _registerMessageHandlers);
|
|
989
|
+
_class_private_method_init(this, _handlePreinstalledSnaps);
|
|
966
990
|
_class_private_method_init(this, _pollForLastRequestStatus);
|
|
967
991
|
/**
|
|
968
992
|
* Blocks an installed snap and prevents it from being started again. Emits
|
|
@@ -980,6 +1004,8 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
980
1004
|
*
|
|
981
1005
|
* @param snapId - The snap to terminate.
|
|
982
1006
|
*/ _class_private_method_init(this, _terminateSnap);
|
|
1007
|
+
_class_private_method_init(this, _handleInitialConnections);
|
|
1008
|
+
_class_private_method_init(this, _addSnapToSubject);
|
|
983
1009
|
_class_private_method_init(this, _removeSnapFromSubjects);
|
|
984
1010
|
_class_private_method_init(this, _revokeAllSnapPermissions);
|
|
985
1011
|
_class_private_method_init(this, _createApproval);
|
|
@@ -1007,20 +1033,29 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
1007
1033
|
* @returns An array of the names of the endowments.
|
|
1008
1034
|
*/ _class_private_method_init(this, _getEndowments);
|
|
1009
1035
|
_class_private_method_init(this, _set);
|
|
1010
|
-
/**
|
|
1011
|
-
* Fetches the manifest and source code of a snap.
|
|
1012
|
-
*
|
|
1013
|
-
* @param snapId - The id of the Snap.
|
|
1014
|
-
* @param location - Source from which snap will be fetched.
|
|
1015
|
-
* @returns A tuple of the Snap manifest object and the Snap source code.
|
|
1016
|
-
*/ _class_private_method_init(this, _fetchSnap);
|
|
1017
1036
|
_class_private_method_init(this, _validateSnapPermissions);
|
|
1037
|
+
_class_private_method_init(this, _getExecutionTimeout);
|
|
1018
1038
|
_class_private_method_init(this, _getRpcRequestHandler);
|
|
1019
|
-
_class_private_method_init(this, _triggerPhishingListUpdate);
|
|
1020
|
-
_class_private_method_init(this, _checkPhishingList);
|
|
1021
1039
|
/**
|
|
1022
|
-
*
|
|
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.
|
|
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.
|
|
1023
1057
|
*
|
|
1058
|
+
* @param snapId - The snap ID.
|
|
1024
1059
|
* @param handlerType - The handler type of the RPC Request.
|
|
1025
1060
|
* @param result - The result of the RPC request.
|
|
1026
1061
|
*/ _class_private_method_init(this, _assertSnapRpcRequestResult);
|
|
@@ -1058,6 +1093,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
1058
1093
|
_class_private_method_init(this, _getRuntimeExpect);
|
|
1059
1094
|
_class_private_method_init(this, _setupRuntime);
|
|
1060
1095
|
_class_private_method_init(this, _calculatePermissionsChange);
|
|
1096
|
+
_class_private_method_init(this, _updatePermissions);
|
|
1061
1097
|
_class_private_method_init(this, _isValidUpdate);
|
|
1062
1098
|
/**
|
|
1063
1099
|
* Call a lifecycle hook on a snap, if the snap has the
|
|
@@ -1154,7 +1190,10 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
1154
1190
|
});
|
|
1155
1191
|
_class_private_method_get(this, _initializeStateMachine, initializeStateMachine).call(this);
|
|
1156
1192
|
_class_private_method_get(this, _registerMessageHandlers, registerMessageHandlers).call(this);
|
|
1157
|
-
|
|
1193
|
+
if (preinstalledSnaps) {
|
|
1194
|
+
_class_private_method_get(this, _handlePreinstalledSnaps, handlePreinstalledSnaps).call(this, preinstalledSnaps);
|
|
1195
|
+
}
|
|
1196
|
+
Object.values(this.state?.snaps ?? {}).forEach((snap)=>_class_private_method_get(this, _setupRuntime, setupRuntime).call(this, snap.id));
|
|
1158
1197
|
}
|
|
1159
1198
|
}
|
|
1160
1199
|
function initializeStateMachine() {
|
|
@@ -1231,6 +1270,63 @@ function registerMessageHandlers() {
|
|
|
1231
1270
|
this.messagingSystem.registerActionHandler(`${controllerName}:revokeDynamicPermissions`, (...args)=>this.revokeDynamicSnapPermissions(...args));
|
|
1232
1271
|
this.messagingSystem.registerActionHandler(`${controllerName}:getFile`, async (...args)=>this.getSnapFile(...args));
|
|
1233
1272
|
}
|
|
1273
|
+
function handlePreinstalledSnaps(preinstalledSnaps) {
|
|
1274
|
+
for (const { snapId, manifest, files, removable } of preinstalledSnaps){
|
|
1275
|
+
const existingSnap = this.get(snapId);
|
|
1276
|
+
const isAlreadyInstalled = existingSnap !== undefined;
|
|
1277
|
+
const isUpdate = isAlreadyInstalled && gtVersion(manifest.version, existingSnap.version);
|
|
1278
|
+
// Disallow downgrades and overwriting non preinstalled snaps
|
|
1279
|
+
if (isAlreadyInstalled && (!isUpdate || existingSnap.preinstalled !== true)) {
|
|
1280
|
+
continue;
|
|
1281
|
+
}
|
|
1282
|
+
const manifestFile = new VirtualFile({
|
|
1283
|
+
path: NpmSnapFileNames.Manifest,
|
|
1284
|
+
value: JSON.stringify(manifest),
|
|
1285
|
+
result: manifest
|
|
1286
|
+
});
|
|
1287
|
+
const virtualFiles = files.map(({ path, value })=>new VirtualFile({
|
|
1288
|
+
value,
|
|
1289
|
+
path
|
|
1290
|
+
}));
|
|
1291
|
+
const { filePath, iconPath } = manifest.source.location.npm;
|
|
1292
|
+
const sourceCode = virtualFiles.find((file)=>file.path === filePath);
|
|
1293
|
+
const svgIcon = iconPath ? virtualFiles.find((file)=>file.path === iconPath) : undefined;
|
|
1294
|
+
assert(sourceCode, 'Source code not provided for preinstalled snap.');
|
|
1295
|
+
assert(!iconPath || iconPath && svgIcon, 'Icon not provided for preinstalled snap.');
|
|
1296
|
+
assert(manifest.source.files === undefined, 'Auxiliary files are not currently supported for preinstalled snaps.');
|
|
1297
|
+
const localizationFiles = manifest.source.locales?.map((path)=>virtualFiles.find((file)=>file.path === path)) ?? [];
|
|
1298
|
+
const validatedLocalizationFiles = getValidatedLocalizationFiles(localizationFiles.filter(Boolean));
|
|
1299
|
+
assert(localizationFiles.length === validatedLocalizationFiles.length, 'Missing localization files for preinstalled snap.');
|
|
1300
|
+
const filesObject = {
|
|
1301
|
+
manifest: manifestFile,
|
|
1302
|
+
sourceCode,
|
|
1303
|
+
svgIcon,
|
|
1304
|
+
auxiliaryFiles: [],
|
|
1305
|
+
localizationFiles: validatedLocalizationFiles
|
|
1306
|
+
};
|
|
1307
|
+
// Add snap to the SnapController state
|
|
1308
|
+
_class_private_method_get(this, _set, set).call(this, {
|
|
1309
|
+
id: snapId,
|
|
1310
|
+
origin: 'metamask',
|
|
1311
|
+
files: filesObject,
|
|
1312
|
+
removable,
|
|
1313
|
+
preinstalled: true
|
|
1314
|
+
});
|
|
1315
|
+
// Setup permissions
|
|
1316
|
+
const processedPermissions = processSnapPermissions(manifest.initialPermissions);
|
|
1317
|
+
_class_private_method_get(this, _validateSnapPermissions, validateSnapPermissions).call(this, processedPermissions);
|
|
1318
|
+
const { newPermissions, unusedPermissions } = _class_private_method_get(this, _calculatePermissionsChange, calculatePermissionsChange).call(this, snapId, processedPermissions);
|
|
1319
|
+
_class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
|
|
1320
|
+
snapId,
|
|
1321
|
+
newPermissions,
|
|
1322
|
+
unusedPermissions
|
|
1323
|
+
});
|
|
1324
|
+
// Set status
|
|
1325
|
+
this.update((state)=>{
|
|
1326
|
+
state.snaps[snapId].status = SnapStatus.Stopped;
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1234
1330
|
function pollForLastRequestStatus() {
|
|
1235
1331
|
_class_private_field_set(this, _timeoutForLastRequestStatus, setTimeout(()=>{
|
|
1236
1332
|
_class_private_method_get(this, _stopSnapsLastRequestPastMax, stopSnapsLastRequestPastMax).call(this).catch((error)=>{
|
|
@@ -1272,7 +1368,9 @@ async function assertIsInstallAllowed(snapId, snapInfo) {
|
|
|
1272
1368
|
const result = results[snapId];
|
|
1273
1369
|
if (result.status === SnapsRegistryStatus.Blocked) {
|
|
1274
1370
|
throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": The version is blocked. ${result.reason?.explanation ?? ''}`);
|
|
1275
|
-
}
|
|
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) {
|
|
1276
1374
|
throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": The snap is not on the allowlist.`);
|
|
1277
1375
|
}
|
|
1278
1376
|
}
|
|
@@ -1293,6 +1391,52 @@ async function terminateSnap(snapId) {
|
|
|
1293
1391
|
await this.messagingSystem.call('ExecutionService:terminateSnap', snapId);
|
|
1294
1392
|
this.messagingSystem.publish('SnapController:snapTerminated', this.getTruncatedExpect(snapId));
|
|
1295
1393
|
}
|
|
1394
|
+
function handleInitialConnections(snapId, previousInitialConnections, initialConnections) {
|
|
1395
|
+
if (previousInitialConnections) {
|
|
1396
|
+
const revokedInitialConnections = setDiff(previousInitialConnections, initialConnections);
|
|
1397
|
+
for (const origin of Object.keys(revokedInitialConnections)){
|
|
1398
|
+
this.removeSnapFromSubject(origin, snapId);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
for (const origin of Object.keys(initialConnections)){
|
|
1402
|
+
_class_private_method_get(this, _addSnapToSubject, addSnapToSubject).call(this, origin, snapId);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
function addSnapToSubject(origin, snapId) {
|
|
1406
|
+
const subjectPermissions = this.messagingSystem.call('PermissionController:getPermissions', origin);
|
|
1407
|
+
const existingCaveat = subjectPermissions?.[WALLET_SNAP_PERMISSION_KEY]?.caveats?.find((caveat)=>caveat.type === SnapCaveatType.SnapIds);
|
|
1408
|
+
const subjectHasSnap = Boolean((existingCaveat?.value)?.[snapId]);
|
|
1409
|
+
// If the subject is already connected to the snap, this is a no-op.
|
|
1410
|
+
if (subjectHasSnap) {
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
// If an existing caveat exists, we add the snap to that.
|
|
1414
|
+
if (existingCaveat) {
|
|
1415
|
+
this.messagingSystem.call('PermissionController:updateCaveat', origin, WALLET_SNAP_PERMISSION_KEY, SnapCaveatType.SnapIds, {
|
|
1416
|
+
...existingCaveat,
|
|
1417
|
+
[snapId]: {}
|
|
1418
|
+
});
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
const approvedPermissions = {
|
|
1422
|
+
[WALLET_SNAP_PERMISSION_KEY]: {
|
|
1423
|
+
caveats: [
|
|
1424
|
+
{
|
|
1425
|
+
type: SnapCaveatType.SnapIds,
|
|
1426
|
+
value: {
|
|
1427
|
+
[snapId]: {}
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
]
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
this.messagingSystem.call('PermissionController:grantPermissions', {
|
|
1434
|
+
approvedPermissions,
|
|
1435
|
+
subject: {
|
|
1436
|
+
origin
|
|
1437
|
+
}
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1296
1440
|
function removeSnapFromSubjects(snapId) {
|
|
1297
1441
|
const subjects = this.messagingSystem.call('PermissionController:getSubjectNames');
|
|
1298
1442
|
for (const subject of subjects){
|
|
@@ -1350,18 +1494,19 @@ async function add(args) {
|
|
|
1350
1494
|
// If fetching and setting the snap succeeds, this property will be set
|
|
1351
1495
|
// to null in the authorize() method.
|
|
1352
1496
|
runtime.installPromise = (async ()=>{
|
|
1353
|
-
const fetchedSnap = await
|
|
1354
|
-
const manifest = fetchedSnap.
|
|
1497
|
+
const fetchedSnap = await fetchSnap(snapId, location);
|
|
1498
|
+
const manifest = fetchedSnap.manifest.result;
|
|
1355
1499
|
if (!satisfiesVersionRange(manifest.version, versionRange)) {
|
|
1356
1500
|
throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${manifest.version}" which doesn't satisfy requested version range "${versionRange}".`);
|
|
1357
1501
|
}
|
|
1358
1502
|
await _class_private_method_get(this, _assertIsInstallAllowed, assertIsInstallAllowed).call(this, snapId, {
|
|
1359
1503
|
version: manifest.version,
|
|
1360
|
-
checksum: manifest.source.shasum
|
|
1504
|
+
checksum: manifest.source.shasum,
|
|
1505
|
+
permissions: manifest.initialPermissions
|
|
1361
1506
|
});
|
|
1362
1507
|
return _class_private_method_get(this, _set, set).call(this, {
|
|
1363
1508
|
...args,
|
|
1364
|
-
|
|
1509
|
+
files: fetchedSnap,
|
|
1365
1510
|
id: snapId
|
|
1366
1511
|
});
|
|
1367
1512
|
})();
|
|
@@ -1424,10 +1569,10 @@ async function getEndowments(snapId) {
|
|
|
1424
1569
|
return dedupedEndowments;
|
|
1425
1570
|
}
|
|
1426
1571
|
function set(args) {
|
|
1427
|
-
const { id: snapId, origin, files, isUpdate = false } = args;
|
|
1572
|
+
const { id: snapId, origin, files, isUpdate = false, removable, preinstalled } = args;
|
|
1428
1573
|
const { manifest, sourceCode: sourceCodeFile, svgIcon, auxiliaryFiles: rawAuxiliaryFiles, localizationFiles } = files;
|
|
1429
1574
|
assertIsSnapManifest(manifest.result);
|
|
1430
|
-
const { version
|
|
1575
|
+
const { version } = manifest.result;
|
|
1431
1576
|
const sourceCode = sourceCodeFile.toString();
|
|
1432
1577
|
assert(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
|
|
1433
1578
|
const auxiliaryFiles = rawAuxiliaryFiles.map((file)=>{
|
|
@@ -1448,6 +1593,7 @@ function set(args) {
|
|
|
1448
1593
|
origin
|
|
1449
1594
|
}
|
|
1450
1595
|
];
|
|
1596
|
+
const localizedFiles = localizationFiles.map((file)=>file.result);
|
|
1451
1597
|
const snap = {
|
|
1452
1598
|
// Restore relevant snap state if it exists
|
|
1453
1599
|
...existingSnap,
|
|
@@ -1455,6 +1601,8 @@ function set(args) {
|
|
|
1455
1601
|
// previous state.
|
|
1456
1602
|
blocked: false,
|
|
1457
1603
|
enabled: true,
|
|
1604
|
+
removable,
|
|
1605
|
+
preinstalled,
|
|
1458
1606
|
id: snapId,
|
|
1459
1607
|
initialPermissions: manifest.result.initialPermissions,
|
|
1460
1608
|
manifest: manifest.result,
|
|
@@ -1463,7 +1611,7 @@ function set(args) {
|
|
|
1463
1611
|
version,
|
|
1464
1612
|
versionHistory,
|
|
1465
1613
|
auxiliaryFiles,
|
|
1466
|
-
localizationFiles:
|
|
1614
|
+
localizationFiles: localizedFiles
|
|
1467
1615
|
};
|
|
1468
1616
|
// If the snap was blocked, it isn't any longer
|
|
1469
1617
|
delete snap.blockInformation;
|
|
@@ -1479,6 +1627,9 @@ function set(args) {
|
|
|
1479
1627
|
rollbackSnapshot.statePatches = inversePatches;
|
|
1480
1628
|
}
|
|
1481
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);
|
|
1482
1633
|
this.messagingSystem.call('SubjectMetadataController:addSubjectMetadata', {
|
|
1483
1634
|
subjectType: SubjectType.Snap,
|
|
1484
1635
|
name: proposedName,
|
|
@@ -1491,40 +1642,10 @@ function set(args) {
|
|
|
1491
1642
|
sourceCode
|
|
1492
1643
|
};
|
|
1493
1644
|
}
|
|
1494
|
-
async function fetchSnap(snapId, location) {
|
|
1495
|
-
try {
|
|
1496
|
-
const manifest = await location.manifest();
|
|
1497
|
-
const sourceCode = await location.fetch(manifest.result.source.location.npm.filePath);
|
|
1498
|
-
const { iconPath } = manifest.result.source.location.npm;
|
|
1499
|
-
const svgIcon = iconPath ? await location.fetch(iconPath) : undefined;
|
|
1500
|
-
const auxiliaryFiles = await getSnapFiles(location, manifest.result.source.files);
|
|
1501
|
-
await Promise.all(auxiliaryFiles.map(async (file)=>{
|
|
1502
|
-
// This should still be safe
|
|
1503
|
-
// eslint-disable-next-line require-atomic-updates
|
|
1504
|
-
file.data.base64 = await encodeBase64(file);
|
|
1505
|
-
}));
|
|
1506
|
-
const localizationFiles = await getSnapFiles(location, manifest.result.source.locales);
|
|
1507
|
-
const validatedLocalizationFiles = getValidatedLocalizationFiles(localizationFiles);
|
|
1508
|
-
const files = {
|
|
1509
|
-
manifest,
|
|
1510
|
-
sourceCode,
|
|
1511
|
-
svgIcon,
|
|
1512
|
-
auxiliaryFiles,
|
|
1513
|
-
localizationFiles: validatedLocalizationFiles
|
|
1514
|
-
};
|
|
1515
|
-
await validateFetchedSnap(files);
|
|
1516
|
-
return {
|
|
1517
|
-
files,
|
|
1518
|
-
location
|
|
1519
|
-
};
|
|
1520
|
-
} catch (error) {
|
|
1521
|
-
throw new Error(`Failed to fetch snap "${snapId}": ${getErrorMessage(error)}.`);
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
1645
|
function validateSnapPermissions(processedPermissions) {
|
|
1525
1646
|
const permissionKeys = Object.keys(processedPermissions);
|
|
1526
1647
|
const handlerPermissions = Array.from(new Set(Object.values(handlerEndowments)));
|
|
1527
|
-
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(', ')}.`);
|
|
1528
1649
|
const excludedPermissionErrors = permissionKeys.reduce((errors, permission)=>{
|
|
1529
1650
|
if (hasProperty(_class_private_field_get(this, _excludedPermissions), permission)) {
|
|
1530
1651
|
errors.push(_class_private_field_get(this, _excludedPermissions)[permission]);
|
|
@@ -1533,6 +1654,9 @@ function validateSnapPermissions(processedPermissions) {
|
|
|
1533
1654
|
}, []);
|
|
1534
1655
|
assert(excludedPermissionErrors.length === 0, `One or more permissions are not allowed:\n${excludedPermissionErrors.join('\n')}`);
|
|
1535
1656
|
}
|
|
1657
|
+
function getExecutionTimeout(permission) {
|
|
1658
|
+
return getMaxRequestTimeCaveat(permission) ?? this.maxRequestTime;
|
|
1659
|
+
}
|
|
1536
1660
|
function getRpcRequestHandler(snapId) {
|
|
1537
1661
|
const runtime = _class_private_method_get(this, _getRuntimeExpect, getRuntimeExpect).call(this, snapId);
|
|
1538
1662
|
const existingHandler = runtime.rpcHandler;
|
|
@@ -1543,7 +1667,7 @@ function getRpcRequestHandler(snapId) {
|
|
|
1543
1667
|
// We need to set up this promise map to map snapIds to their respective startPromises,
|
|
1544
1668
|
// because otherwise we would lose context on the correct startPromise.
|
|
1545
1669
|
const startPromises = new Map();
|
|
1546
|
-
const rpcHandler = async ({ origin, handler: handlerType, request })=>{
|
|
1670
|
+
const rpcHandler = async ({ origin, handler: handlerType, request, timeout })=>{
|
|
1547
1671
|
if (this.state.snaps[snapId].enabled === false) {
|
|
1548
1672
|
throw new Error(`Snap "${snapId}" is disabled.`);
|
|
1549
1673
|
}
|
|
@@ -1569,7 +1693,7 @@ function getRpcRequestHandler(snapId) {
|
|
|
1569
1693
|
}
|
|
1570
1694
|
}
|
|
1571
1695
|
}
|
|
1572
|
-
const timer = new Timer(
|
|
1696
|
+
const timer = new Timer(timeout);
|
|
1573
1697
|
_class_private_method_get(this, _recordSnapRpcRequestStart, recordSnapRpcRequestStart).call(this, snapId, request.id, timer);
|
|
1574
1698
|
const handleRpcRequestPromise = this.messagingSystem.call('ExecutionService:handleRpcRequest', snapId, {
|
|
1575
1699
|
origin,
|
|
@@ -1579,8 +1703,8 @@ function getRpcRequestHandler(snapId) {
|
|
|
1579
1703
|
// This will either get the result or reject due to the timeout.
|
|
1580
1704
|
try {
|
|
1581
1705
|
const result = await _class_private_method_get(this, _executeWithTimeout, executeWithTimeout).call(this, handleRpcRequestPromise, timer);
|
|
1582
|
-
await _class_private_method_get(this, _assertSnapRpcRequestResult, assertSnapRpcRequestResult).call(this, handlerType, result);
|
|
1583
|
-
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);
|
|
1584
1708
|
} catch (error) {
|
|
1585
1709
|
const [jsonRpcError, handled] = unwrapError(error);
|
|
1586
1710
|
if (!handled) {
|
|
@@ -1594,29 +1718,64 @@ function getRpcRequestHandler(snapId) {
|
|
|
1594
1718
|
runtime.rpcHandler = rpcHandler;
|
|
1595
1719
|
return rpcHandler;
|
|
1596
1720
|
}
|
|
1597
|
-
async function
|
|
1598
|
-
return this.messagingSystem.call('
|
|
1721
|
+
async function createInterface(snapId, content) {
|
|
1722
|
+
return this.messagingSystem.call('SnapInterfaceController:createInterface', snapId, content);
|
|
1723
|
+
}
|
|
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));
|
|
1599
1727
|
}
|
|
1600
|
-
function
|
|
1601
|
-
|
|
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
|
+
}
|
|
1602
1750
|
}
|
|
1603
|
-
async function assertSnapRpcRequestResult(handlerType, result) {
|
|
1751
|
+
async function assertSnapRpcRequestResult(snapId, handlerType, result) {
|
|
1604
1752
|
switch(handlerType){
|
|
1605
1753
|
case HandlerType.OnTransaction:
|
|
1606
1754
|
{
|
|
1607
1755
|
assertStruct(result, OnTransactionResponseStruct);
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1756
|
+
if (result && hasProperty(result, 'id')) {
|
|
1757
|
+
_class_private_method_get(this, _assertInterfaceExists, assertInterfaceExists).call(this, snapId, result.id);
|
|
1758
|
+
}
|
|
1759
|
+
break;
|
|
1760
|
+
}
|
|
1761
|
+
case HandlerType.OnSignature:
|
|
1762
|
+
{
|
|
1763
|
+
assertStruct(result, OnSignatureResponseStruct);
|
|
1764
|
+
if (result && hasProperty(result, 'id')) {
|
|
1765
|
+
_class_private_method_get(this, _assertInterfaceExists, assertInterfaceExists).call(this, snapId, result.id);
|
|
1611
1766
|
}
|
|
1612
|
-
await _class_private_method_get(this, _triggerPhishingListUpdate, triggerPhishingListUpdate).call(this);
|
|
1613
|
-
validateComponentLinks(result.content, _class_private_method_get(this, _checkPhishingList, checkPhishingList).bind(this));
|
|
1614
1767
|
break;
|
|
1615
1768
|
}
|
|
1616
1769
|
case HandlerType.OnHomePage:
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
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);
|
|
1620
1779
|
break;
|
|
1621
1780
|
default:
|
|
1622
1781
|
break;
|
|
@@ -1651,11 +1810,7 @@ function createRollbackSnapshot(snapId) {
|
|
|
1651
1810
|
assert(_class_private_field_get(this, _rollbackSnapshots).get(snapId) === undefined, new Error(`Snap "${snapId}" rollback snapshot already exists.`));
|
|
1652
1811
|
_class_private_field_get(this, _rollbackSnapshots).set(snapId, {
|
|
1653
1812
|
statePatches: [],
|
|
1654
|
-
permissions: {
|
|
1655
|
-
revoked: null,
|
|
1656
|
-
granted: [],
|
|
1657
|
-
requestData: null
|
|
1658
|
-
},
|
|
1813
|
+
permissions: {},
|
|
1659
1814
|
newVersion: ''
|
|
1660
1815
|
});
|
|
1661
1816
|
const newRollbackSnapshot = _class_private_field_get(this, _rollbackSnapshots).get(snapId);
|
|
@@ -1683,20 +1838,12 @@ async function rollbackSnap(snapId) {
|
|
|
1683
1838
|
state.snaps[snapId].status = SnapStatus.Stopped;
|
|
1684
1839
|
});
|
|
1685
1840
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
requestData: permissions.requestData
|
|
1693
|
-
});
|
|
1694
|
-
}
|
|
1695
|
-
if (permissions.granted?.length) {
|
|
1696
|
-
this.messagingSystem.call('PermissionController:revokePermissions', {
|
|
1697
|
-
[snapId]: permissions.granted
|
|
1698
|
-
});
|
|
1699
|
-
}
|
|
1841
|
+
_class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
|
|
1842
|
+
snapId,
|
|
1843
|
+
unusedPermissions: permissions.granted,
|
|
1844
|
+
newPermissions: permissions.revoked,
|
|
1845
|
+
requestData: permissions.requestData
|
|
1846
|
+
});
|
|
1700
1847
|
const truncatedSnap = this.getTruncatedExpect(snapId);
|
|
1701
1848
|
this.messagingSystem.publish('SnapController:snapRolledback', truncatedSnap, rollbackSnapshot.newVersion);
|
|
1702
1849
|
_class_private_field_get(this, _rollbackSnapshots).delete(snapId);
|
|
@@ -1752,6 +1899,23 @@ function calculatePermissionsChange(snapId, desiredPermissionsSet) {
|
|
|
1752
1899
|
approvedPermissions
|
|
1753
1900
|
};
|
|
1754
1901
|
}
|
|
1902
|
+
function updatePermissions({ snapId, unusedPermissions = {}, newPermissions = {}, requestData }) {
|
|
1903
|
+
const unusedPermissionsKeys = Object.keys(unusedPermissions);
|
|
1904
|
+
if (isNonEmptyArray(unusedPermissionsKeys)) {
|
|
1905
|
+
this.messagingSystem.call('PermissionController:revokePermissions', {
|
|
1906
|
+
[snapId]: unusedPermissionsKeys
|
|
1907
|
+
});
|
|
1908
|
+
}
|
|
1909
|
+
if (isNonEmptyArray(Object.keys(newPermissions))) {
|
|
1910
|
+
this.messagingSystem.call('PermissionController:grantPermissions', {
|
|
1911
|
+
approvedPermissions: newPermissions,
|
|
1912
|
+
subject: {
|
|
1913
|
+
origin: snapId
|
|
1914
|
+
},
|
|
1915
|
+
requestData
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1755
1919
|
function isValidUpdate(snapId, newVersionRange) {
|
|
1756
1920
|
const existingSnap = this.getExpect(snapId);
|
|
1757
1921
|
if (satisfiesVersionRange(existingSnap.version, newVersionRange)) {
|
|
@@ -1764,6 +1928,7 @@ function isValidUpdate(snapId, newVersionRange) {
|
|
|
1764
1928
|
}
|
|
1765
1929
|
async function callLifecycleHook(snapId, handler) {
|
|
1766
1930
|
const permissionName = handlerEndowments[handler];
|
|
1931
|
+
assert(permissionName, 'Lifecycle hook must have an endowment.');
|
|
1767
1932
|
const hasPermission = this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName);
|
|
1768
1933
|
if (!hasPermission) {
|
|
1769
1934
|
return;
|